MEP26: Artist 样式#
状态#
已拒绝
分支和 Pull requests#
摘要#
此 MEP 提出了一个新的样式表实现,以允许更全面和动态的 artist 样式.
当前版本的 matplotlib (1.4.0) 允许在创建绘图之前应用基于 rcParams 语法的样式表.下面的方法提出了一种基于 CSS 的新语法,它允许对单个艺术家和属性进行样式设置,可以动态地应用于现有对象.
这与迁移到 DOM/树状架构的总体目标相关(并朝着该目标迈进).
详细描述#
目前,现有 artist 对象(figure,axes,Line2D 等)的外观只能通过 artist 对象上的 set_ 和 get_ 方法进行更新,这非常费力,尤其是在没有存储对 artist 的引用的情况下.1.4 中引入的新样式表允许在创建绘图之前进行样式设置,但不提供任何动态更新绘图或区分相同类型的 artist 的方法(即,分别为不同的 Line2D 对象指定 line color 和 line style ).
最初的开发应该集中在允许样式化的 artist 原语(那些不包含其他 Artist s 的 Artist s)上,进一步的开发可以扩展 CSS 语法规则和解析器以允许更复杂的样式设置. 有关原语列表,请参见附录.
新的方法将需要开发多个步骤:
一种新的样式表语法(可能基于 CSS),允许按类型,类,id 等选择 artist.
一种将样式表解析为树的机制
一种将解析树转换为可用于更新相关 artist 属性的机制.理想情况下,这应该实现一种以树状结构遍历 artist 的方法.
一种从现有 artist 属性生成样式表的机制. 这对于允许用户从现有图形(其外观可能已使用 matplotlib API 设置)导出样式表非常有用……
实施#
如果将"style"创建为单独的类并作为属性存储在 artist 中,则允许"第三方"修改/设置 artist 的样式将是最简单的. GraphicsContextBase 类已经提供了 Style 类的基础,并且可以重构 artist 的 draw 方法以使用 Style 类,而不是设置自己的 GraphicsContextBase 并将其与样式相关的属性传输到它. 这是一个关于如何实现此目的的最小示例:JamesRamm/mpl_experiment
在我看来,这也会使 API 和代码库更加整洁,因为 artist 样式属性的各个 get/set 方法现在是多余的……间接相关的是用属性替换 get/set 方法的总体趋势. 使用属性实现样式类将是朝着这个方向迈出的一大步……
对于初始开发,我建议开发一种基于 CSS 的简化版本的语法(非常非常简化). 我赞成将其称为 Artist Style Sheets :+1: :
BNF 语法#
我建议最初实现一个非常简单的语法(就像概念验证一样),将来可以对其进行扩展. 语法的 BNF 形式如下所示,然后进行解释:
RuleSet ::= SelectorSequence "{"Declaration"}"
SelectorSequence :: = Selector {"," Selector}
Declaration ::= propName":" propValue";"
Selector ::= ArtistIdent{"#"Ident}
propName ::= Ident
propValue ::= Ident | Number | Colour | "None"
ArtistIdent , Ident , Number 和 Colour 是 token(表达式的基本构建块),由正则表达式定义.
语法#
CSS 样式表由一系列按层次顺序排列的规则集组成(规则从上到下应用). 每个规则都遵循以下语法:
selector {attribute: value;}
每个规则可以有任意数量的 attribute: value 对,并且样式表可以有任意数量的规则.
初始语法仅针对 Artist 基元设计. 它没有解决如何在 Container 类型上设置属性的问题(这些类型的属性本身可能是具有可设置属性的 Artist s),但是,未来的解决方案可能只是嵌套的 RuleSet s
选择器#
选择器定义应将属性更新应用到的对象. 作为起点,我建议在初始开发中使用 2 个选择器:
Artist 类型选择器
按类型选择一个 Artist . 例如 Line2D 或 Text
Line2D {attribute: value}
用于匹配 artist 类型选择器的 regex(BNF 语法中的 ArtistIdent )将是:
ArtistIdent = r'(?P<ArtistIdent>\bLine2D\b|\bText\b|\bAxesImage\b|\bFigureImage\b|\bPatch\b)'
GID 选择器#
按其 gid 选择一个 Artist
Line2D#myGID {attribute: value}
gid 可以是任何字符串,因此 regex 可以如下所示:
Ident = r'(?P<Ident>[a-zA-Z_][a-zA-Z_0-9]*)'
以上选择器大致对应于它们的 CSS 对等项 (http://www.w3.org/TR/CSS21/selector.html)
属性和值#
Attributes是所讨论的Artist的任何有效(可设置)属性.Values是属性的任何有效值(通常是字符串或数字).
解析#
解析将包括将样式表分解为 token(python cookbook 在第 66 页上提供了一个不错的 tokenizing recipe),应用语法规则并构造一个 Tree . 这需要定义样式表的语法(同样,我们可以从 CSS 借用)并编写解析器. 幸运的是,python cookbook 中也有一个关于此的 recipe.
matplotlib figure 的 Visitor 模式#
为了将样式表规则应用于相关的 artist,我们需要"访问" figure 中的每个 artist 并应用相关的规则. 这是一个 visitor 类(再次感谢 python cookbook),其中每个 node 将是 figure 中的一个 artist. 需要为每个 mpl artist 实现一个 visit_ 方法,以处理每个 artist 的不同属性:
class Visitor:
def visit(self, node):
name = 'visit_' + type(node).__name__
meth = getattr(self, name, None)
if meth is None:
raise NotImplementedError
return meth(node)
然后,一个 evaluator 类将采用样式表规则并在每个规则上实现 visitor.
向后兼容性#
实现单独的 Style 类会破坏向后兼容性,因为 artist 上的许多 get/set 方法将变得多余. 虽然可以更改这些方法以连接到 Style 类(作为属性存储在 artist 中),但我倾向于直接删除它们,以使代码库更加整洁/简化并提供简单,清晰的 API……
替代方案#
没有其他替代方案,但这里涉及的一些基础内容与 MEP25 重叠,这可能有助于此开发
附录#
Matplotlib 原始对象#
这将构成样式表可以使用的初始选择器.
Line2D
Text
AxesImage
FigureImage
Patch