MEP26: Artist styling#
Status#
Rejected
Branches and Pull requests#
Abstract#
This MEP proposes a new stylesheet implementation to allow morecomprehensive and dynamic styling of artists.
The current version of matplotlib (1.4.0) allows stylesheets based onthe rcParams syntax to be applied before creation of a plot. Themethodology below proposes a new syntax, based on CSS, which wouldallow styling of individual artists and properties, which can beapplied dynamically to existing objects.
This is related to (and makes steps toward) the overall goal of movingto a DOM/tree-like architecture.
Detailed description#
Currently, the look and appearance of existing artist objects (figure,axes, Line2D, etc.) can only be updated viaset_ andget_ methodson the artist object, which is quite laborious, especially if noreference to the artist(s) has been stored. The new style sheetsintroduced in 1.4 allow styling before a plot is created, but do notoffer any means to dynamically update plots or distinguish betweenartists of the same type (i.e. to specify thelinecolor andlinestyle separately for differingLine2D objects).
The initial development should concentrate on allowing styling ofartist primitives (thoseArtists that do not contain otherArtists), and further development could expand the CSS syntax rulesand parser to allow more complex styling. See the appendix for a listof primitives.
The new methodology would require development of a number of steps:
A new stylesheet syntax (likely based on CSS) to allow selection ofartists by type, class, id, etc.
A mechanism by which to parse a stylesheet into a tree
A mechanism by which to translate the parse-tree into somethingwhich can be used to update the properties of relevantartists. Ideally this would implement a method by which to traversethe artists in a tree-like structure.
A mechanism by which to generate a stylesheet from existing artistproperties. This would be useful to allow a user to export astylesheet from an existing figure (where the appearance may havebeen set using the matplotlib API)...
Implementation#
It will be easiest to allow a '3rd party' to modify/set the style of an artistif the 'style' is created as a separate class and store against the artist as aproperty. TheGraphicsContextBase class already provides a the basis of aStyle class and an artist'sdraw method can be refactored to usetheStyle class rather than setting up its ownGraphicsContextBase andtransferring its style-related properties to it. A minimal example of how thiscould be implemented is shown here:JamesRamm/mpl_experiment
IMO, this will also make the API and code base much neater asindividual get/set methods for artist style properties are nowredundant... Indirectly related would be a general drive to replaceget/set methods with properties. Implementing the style class withproperties would be a big stride toward this...
For initial development, I suggest developing a syntax based on a much(much much) simplified version of CSS. I am in favour of dubbing thisArtist Style Sheets :+1: :
BNF Grammar#
I propose a very simple syntax to implement initially (like a proof ofconcept), which can be expanded upon in the future. The BNF form ofthe syntax is given below and then explained
RuleSet::=SelectorSequence"{"Declaration"}"SelectorSequence::=Selector{","Selector}Declaration::=propName":"propValue";"Selector::=ArtistIdent{"#"Ident}propName::=IdentpropValue::=Ident|Number|Colour|"None"
ArtistIdent,Ident,Number andColour are tokens (the basicbuilding blocks of the expression) which are defined by regularexpressions.
Syntax#
A CSS stylesheet consists of a series ofrule sets in hierarchicalorder (rules are applied from top to bottom). Each rule follows thesyntax
selector{attribute:value;}
Each rule can have any number ofattribute:value pairs, and astylesheet can have any number of rules.
The initial syntax is designed only forArtist primitives. It doesnot address the question of how to set properties onContainer types(whose properties may themselves beArtists with settableproperties), however, a future solution to this could simply be nestedRuleSets
Selectors#
Selectors define the object to which the attribute updates should beapplied. As a starting point, I propose just 2 selectors to use ininitial development:
Artist Type Selector
Select anArtist by it's type. E.gLine2D orText:
Line2D{attribute:value}
The regex for matching the artist type selector (ArtistIdent in the BNF grammar) would be:
ArtistIdent=r'(?P<ArtistIdent>\bLine2D\b|\bText\b|\bAxesImage\b|\bFigureImage\b|\bPatch\b)'
GID selector#
Select anArtist by itsgid:
Line2D#myGID {attribute: value}
Agid can be any string, so the regex could be as follows:
Ident=r'(?P<Ident>[a-zA-Z_][a-zA-Z_0-9]*)'
The above selectors roughly correspond to their CSS counterparts(http://www.w3.org/TR/CSS21/selector.html)
Attributes and values#
Attributesare any valid (settable) property for theArtistin question.Valuesare any valid value for the property (Usually a string, or number).
Parsing#
Parsing would consist of breaking the stylesheet into tokens (thepython cookbook gives a nice tokenizing recipe on page 66), applyingthe syntax rules and constructing aTree. This requires defining thegrammar of the stylesheet (again, we can borrow from CSS) and writinga parser. Happily, there is a recipe for this in the python cookbookas well.
Visitor pattern for matplotlib figure#
In order to apply the stylesheet rules to the relevant artists, weneed to 'visit' each artist in a figure and apply the relevant rule.Here is a visitor class (again, thanks to python cookbook), where eachnode would be an artist in the figure. Avisit_ method would needto be implemented for each mpl artist, to handle the differentproperties for each
classVisitor:defvisit(self,node):name='visit_'+type(node).__name__meth=getattr(self,name,None)ifmethisNone:raiseNotImplementedErrorreturnmeth(node)
Anevaluator class would then take the stylesheet rules andimplement the visitor on each one of them.
Backward compatibility#
Implementing a separateStyle class would break backwardcompatibility as many get/set methods on an artist would becomeredundant. While it would be possible to alter these methods to hookinto theStyle class (stored as a property against the artist), Iwould be in favor of simply removing them to both neaten/simplify thecodebase and to provide a simple, uncluttered API...
Alternatives#
No alternatives, but some of the ground covered here overlaps withMEP25, which may assist in this development
Appendix#
Matplotlib primitives#
This will form the initial selectors which stylesheets can use.
Line2D
Text
AxesImage
FigureImage
Patch