2.3 Attributes: Styles and Decorations

Attributes define properties of a given object when it is being used. Typically, there are different kind of attributes which are usually orthogonal to each other, while for one type of attribute, several choices are possible. An example is the stroking of a path. There, linewidth and linestyle are different kind of attributes. The linewidth might be normal, thin, thick, etc, and the linestyle might be solid, dashed etc.

Attributes always occur in lists passed as an optional keyword argument to a method or a function. Usually, attributes are the first keyword argument, so one can just pass the list without specifying the keyword. Again, for the path example, a typical call looks like

c.stroke(path, [style.linewidth.Thick, style.linestyle.dashed])

Here, we also encounter another feature of PyX's attribute system. For many attributes useful default values are stored as member variables of the actual attribute. For instance, style.linewidth.Thick is equivalent to style.linewidth(0.04, type="w", unit="cm"), that is $0.04$ width cm (see Sect. 13 for more information about PyX's unit system).

Another important feature of PyX attributes is what is call attributed merging. A trivial example is the following:

# the following two lines are equivalent
c.stroke(path, [style.linewidth.Thick, style.linewidth.thin])
c.stroke(path, [style.linewidth.thin])
Here, the style.linewidth.thin attribute overrides the preceding style.linewidth.Thick declaration. This is especially important in more complex cases where PyXdefines default attributes for a certain operation. When calling the corresponding methods with an attribute list, this list is appended to the list of defaults. This way, the user can easily override certain defaults, while leaving the other default values intact. In addition, every attribute kind defines a special clear attribute, which allows to selectively delete a default value. For path stroking this looks like
# the following two lines are equivalent
c.stroke(path, [style.linewidth.Thick, style.linewidth.clear])
c.stroke(path)
The clear attribute is also provided by the base classes of the various styles. For instance, style.strokestyle.clear clears all strokestyle subclasses and thus style.linewidth and style.linestyle. Since all attributes derive from attr.attr, you can remove all defaults using attr.clear. An overview over the most important attribute typesprovided by PyX is given in the following table.


Attribute category description examples
deco.deco decorator specifying the way the path is drawn deco.stroked
deco.filled
deco.arrow
style.strokestyle style used for path stroking style.linecap
style.linejoin
style.miterlimit
style.dash
style.linestyle
style.linewidth
color.color
style.fillstyle style used for path filling color.color
pattern.pattern
deformer.deformer operations changing the shape of the path deformer.cycloid
deformer.smoothed
text.textattr attributes used for typesetting text.halign
text.valign
text.mathmode
text.phantom
text.size
text.parbox
trafo.trafo transformations applied when drawing object trafo.mirror
trafo.rotate
trafo.scale
trafo.slant
trafo.translate


XXX specify which classes in the table are in fact instances

Note that operations usually allow for certain attribute categories only. For example when stroking a path, text attributes are not allowed, while stroke attributes and decorators are. Some attributes might belong to several attribute categories like colours, which are both, stroke and fill attributes.

Last, we discuss another important feature of PyX's attribute system. In order to allow the easy customisation of predefined attributes, it is possible to create a modified attribute by calling of an attribute instance, thereby specifying new parameters. A typical example is to modify the way a path is stroked or filled by constructing appropriate deco.stroked or deco.filled instances. For instance, the code

c.stroke(path, [deco.filled([color.rgb.green])])
draws a path filled in green with a black outline. Here, deco.filled is already an instance which is modified to fill with the given color. Note that an equivalent version would be
c.draw(path, [deco.stroked, deco.filled([color.rgb.green])])
In particular, you can see that deco.stroked is already an attribute instance, since otherwise you were not allowed to pass it as a parameter to the draw method. Another example where the modification of a decorator is useful are arrows. For instance, the following code draws an arrow head with a more acute angle (compared to the default value of $45$ degrees):
c.stroke(path, [deco.earrow(angle=30)])

XXX changeable attributes