So what does it do?

PyAttributes let's you add arbitrary attributes to any object in a Dia diagram. Objects are anything in a diagram that you can select, like boxes, arrows, text etc. The attributes have a name and can store different kinds of information, some can even do actions like running a external command, loading a file or.. uhm, well anything.

Confused? We'll maybe an example will set you straight. We take a diagram containing two boxes and a zig-zag line.

Clip from a diagram. Two boxes and a zig-zag line.

There is a dialog for adding attributes to objects in the menu, 'Dialog/Setup Attributes...'. You choose one or more objects then choose the type of the Attribute and fill in the name.

The Setup View.

After setting up the attributes you can view and update them through the object view. Just select a object and in the menu choose 'Dialogs/Object View...'. With it you can view and fill in new values of attributes.

Object View

Why?

As you probably figured out by now the extra attributes doesn't change the diagram at all. They are metadata, only meaningful for you, and they don't get printed with the diagram. So what's their use? They can be quite useful for extra information about what the objects in the diagram really represents, for example; take a network diagram with computers and routers etc. you can give everything having a IP number in reality a attribute of the type IP with the name 'address'. You could also give them attributes like name and room. The fun does not stop there, you can add attributes that perform actions, like a SSH prompt, ping or a web page view of a wiki pointing at different pages depending on which object you're on. You could easily make a documenting and administrating tool from your diagram.

Saving and Loading

To save a diagram with the extra information you have to use a special save function 'Save with PyAtt', the same goes for loading. You can load a diagram saved with PyAttributes in Dia without the plugin. This way you just don't get the extra attributes.

The Attributes

Every attribute has a name and a type. Some attributes requires more than a name for setup and some are to large to display it's value in the Object Views list of attributes. Large values gets a whole new tab for themselves, just double click on the name of the attribute in the list. Selecting it and pressing the 'View' button results in the same thing.

These are some fundamental attributes and should cover quite alot, but if you need something else it's easy to write your own in python. Tutorial is coming.

Terminal

The Terminal attribute runs any command that's in your path in a bash prompt and functions otherwise as an ordinary xterm. When adding a Terminal to your objects it requires that you give it a command as well as a name. If you make a typo or change your mind you can change the command in the Object View, just click on the line in the attribute list where the command is displayed. The Terminal does not fit the list for obvious reasons so you'll have to activate it with a double click or the 'View' button in the Object View. Some things you can do is run emacs, ssh, ping, lynx against a wikiw, a sql client, etc etc..

Webpage

The Webpage attribute uses the Gecko engine from Mozilla, through the Gtk+ widget GtkMozEmbed. In other words it's like Firefox, but without any buttons :-) The Webpage attribute needs an URL on setup as well as a name and ,as the Terminal attribute, you can change it in the Object View later if you want. It also needs a tab and you open it by double clicking in the list or the 'View' button in the Object View.

The attribute creates a temporary directory, ordinarily in /tmp, to store it's cookies and cache.

Rolling Your Own Attribute

So, you want to make your own attribute, eh? Well it ain't too hard, but you'll need to know how to program in Python, go to http://www.python.org and enlighten yourself. Ok, let's make a new attribute, step by step.

A Boolean Attribute

We'll make a Boolean attribute that can have either the value 'False' or 'True'. Nothing else. For this we're going to need two python classes, one for the attribute and one for the view (GUI) of the attribute. Both of these are going to subclass classes in the module pyattributes, which resides in the file pyattributes/attribute.py. When in doubt, use the source Luke.

So fire up your favourite text editor (emacs,vim,gedit etc) and create a new file named boolean_attribute.py or whatever you like. Copy and paste or download the code here. The first class: First we need to import the module with the class that we want to subclass, which is pyattributes.Attribute. import pyattributes Then we define the class and set some class attributes: class BooleanAttribute(pyattributes.Attribute): type = 'Boolean' desc = 'A Boolean, can either be True or False' type is the name of the attribute and must be unique, and desc is a short description. The constructor is as follows:

    
    def __init__(self,name):
        #call the superclass
        pyattributes.Attribute.__init__(self,name)
        
        #default value is False
        self.value = False
        self.can_parse = True
    
First we call the super classes and then we set two important attributes. can_parse acts as a flag, if set to True the method parse(self,txt) must be implemented. As you can see we set our BooleanAttribute to always start with the value False.

Next we define a method that returns the view of the BooleanAttribute, the second class we haven't made yet:

    def get_view(self):
        """ Returns a view of this attribute. """
        return BooleanView(self)
        
Nothing fancy, just instantiates a new view and returns it.

Since we set can_parse to True we need to implement the method parse. In the Object View there is a list of all attributes that is bound to that object, in this list the attributes string representation is shown, in our case that will be either 'True' or 'False'. The user can click on the value to edit the it directly in the list bypassing it's view, that's where the parse method comes in. It's suppose to try to parse the user supplied string. If it's successful it changes the value of the attribute and returns True, if not it only has to return False. We want the attribute to change when the user inputs something we can interpret as a boolean.

    def parse(self,txt):
        """ Parses user input to determine if they mean True or False 
            True,T,true,t and 1 makes True
            False,F,false and 0 makes False
        """
        if txt in ['True','T','t','true','1']:
            self.value = True
            return True
        elif txt in ['False','F','false','f','0']:
            self.value = False
            return True
        return False   #didn't parse
        

And as a final touch we add the __str__ method so that the string representation of our BooleanAttribute is correct. This is already defined in the super class, but it's good to know about.

    def __str__(self):
        """ The string representation of the value. Used in lists."""
        return str(self.value)
        
That's all for the first class, now let's have a look at the view.

The view also needs the pyattributes module, but it also needs PyGtk. The whole code for the class is:

import pygtk
pygtk.require('2.0')
import gtk

class BooleanView(gtk.VBox,pyattributes.AttributeView):

    def __init__(self,att):
        #call the superclasses
        gtk.VBox.__init__(self)
        pyattributes.AttributeView.__init__(self,att)
        
        #let's use a dialog instead of a tab in the ObjectView since it's a small value
        self.has_dialog = True
        
        #the value is shown with two radio buttons
        self.__rb_T = gtk.RadioButton(None,'True')
        rb_F = gtk.RadioButton(self.__rb_T,'False')
        self.pack_start(self.__rb_T)
        self.pack_start(rb_F)
        
        #set active button
        self.__rb_T.set_active(att.value)
        rb_F.set_active(not att.value)
        
        #we listen to the 'toggled' signal and change the value accordinly
        self.__rb_T.connect('toggled',self.on_toggle)
        
    def on_toggle(self,rb_butt):
        """ Changes the value of the attribute when a button is toggled """
        self.attribute.value = self.__rb_T.get_active()
        
    def close(self):
        """ Used for cleanup when the dialog containing this view is closed"""
        #we don't need to do anything
        pass
		
As you can see in the class definition our view inherits both gtk.VBox and AttributeView. All view must inherit both gtk.Widget and AttributeView. The view of the BooleanAttribute can be shown in two ways, either as dialogue or as new tab in the Object View, it's up to has_dialog. Since this is a small value it need not a whole tab. The view consist of two radio buttons inside, we don't need to worry about windows and such for the dialogue, that is taken care of in the AttributeView class. The method on_toggle listens to the 'toggled' signal from one of the buttons and changes the value of the BooleanAttribute accordingly.

The last thing we need to do is to register the BooleanAttribute with pyattributes.AttributeFactory.

#register the attribute
pyattributes.AttributeFactory.get_instance().register_attribute(BooleanAttribute)
        
To get it working just put the script in $HOME/.dia/python/. Dia will then automatically import it at start and the attribute will be registered!

For examples of more advanced classes do look at the code, especially at the classes Attribute, AttributeView and SimpleSetupView. Happy coding!