MEP26: 艺术家风格#

状态#

被拒绝

分支和拉取请求#

摘要#

本 MEP 提议一个新的样式表实现,以允许对艺术家进行更全面和动态的样式设计。

当前版本的 matplotlib (1.4.0) 允许在创建图表之前应用基于 rcParams 语法的样式表。以下方法提出了一种基于 CSS 的新语法,该语法将允许对单个艺术家和属性进行样式设置,可以动态应用于现有对象。

这与(并朝着)转向DOM/树状架构的总体目标相关。

详细描述#

目前,现有艺术家对象(如图形、轴、Line2D等)的外观只能通过艺术家对象上的 set_get_ 方法进行更新,这相当繁琐,特别是如果没有存储对艺术家(们)的引用。1.4 版本引入的新样式表允许在创建绘图之前进行样式设置,但不提供任何动态更新绘图或区分同类型艺术家(例如,为不同的 Line2D 对象分别指定 线条颜色线条样式)的方法。

初始开发应集中于允许对艺术家原语(那些不包含其他 ArtistArtist)进行样式设置,进一步的开发可以扩展 CSS 语法规则和解析器,以允许更复杂的样式设置。请参阅附录以获取原语列表。

新的方法论将需要开发一系列步骤:

  • 一种新的样式表语法(可能基于CSS),允许通过类型、类、ID等选择艺术家。

  • 一种将样式表解析为树的机制

  • 一种机制,用于将解析树转换为可用于更新相关艺术家属性的内容。理想情况下,这将实现一种遍历树状结构中艺术家的方法。

  • 一种从现有艺术家属性生成样式表的机制。这将有助于允许用户从现有图形中导出样式表(其中外观可能已使用 matplotlib API 设置)...

实现#

如果将'风格'创建为一个单独的类,并将其作为属性存储在艺术家对象中,那么允许'第三方'修改/设置艺术家的风格将最为简单。GraphicsContextBase`类已经提供了一个``Style``类的基础,艺术家的`~.Artist.draw`方法可以重构为使用``Style``类,而不是设置自己的.GraphicsContextBase`并将与其风格相关的属性转移到其中。这里展示了一个如何实现这一点的最小示例:JamesRamm/mpl_experiment

在我看来,这也会使API和代码库更加整洁,因为现在艺术风格属性的单独的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"

ArtistIdentIdentNumberColour 是令牌(表达式的基本构建块),它们由正则表达式定义。

语法#

CSS 样式表由一系列按层次顺序排列的 规则集 组成(规则从上到下应用)。每条规则遵循以下语法

selector {attribute: value;}

每个规则可以有任意数量的 属性: 对,并且一个样式表可以有任意数量的规则。

初始语法仅设计用于 Artist 原语。它没有解决如何设置 Container 类型上的属性(其属性本身可能是具有可设置属性的 Artist)的问题,然而,未来的解决方案可能只是嵌套 RuleSet

选择器#

选择器定义了属性更新应应用的对象。作为起点,我建议在初始开发中仅使用2个选择器:

艺术家类型选择器

按类型选择一个 Artist。例如 Line2DText:

Line2D {attribute: value}

匹配艺术家类型选择器(BNF 语法中的 ArtistIdent)的正则表达式为:

ArtistIdent = r'(?P<ArtistIdent>\bLine2D\b|\bText\b|\bAxesImage\b|\bFigureImage\b|\bPatch\b)'

GID 选择器#

通过其 gid 选择一个 Artist:

Line2D#myGID {attribute: value}

gid 可以是任何字符串,因此正则表达式可以如下所示:

Ident = r'(?P<Ident>[a-zA-Z_][a-zA-Z_0-9]*)'

上述选择器大致对应于它们的 CSS 对应项 (http://www.w3.org/TR/CSS21/selector.html)

属性与值#

  • Attributes 是所讨论的 Artist 的任何有效(可设置)属性。

  • Values 是该属性的任何有效值(通常是字符串或数字)。

解析#

解析过程包括将样式表分解为标记(Python Cookbook 在第66页提供了一个很好的标记化配方),应用语法规则并构建一个 Tree。这需要定义样式表的语法(同样,我们可以借鉴CSS)并编写一个解析器。幸运的是,Python Cookbook 中也有这方面的配方。

matplotlib 图形的访问者模式#

为了将样式表规则应用于相关的艺术家,我们需要'访问'图中的每个艺术家并应用相关规则。这里是一个访问者类(再次感谢python cookbook),其中每个 node 将是图中的一个艺术家。需要为每个mpl艺术家实现一个 visit_ 方法,以处理每个艺术家的不同属性。

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 类将获取样式表规则并在每一条规则上实现访问者模式。

向后兼容性#

实现一个独立的 Style 类会破坏向后兼容性,因为许多艺术家的get/set方法将变得冗余。虽然可以修改这些方法以挂钩到 Style 类(作为艺术家的属性存储),但我更倾向于直接删除它们,以简化代码库并提供一个简单、无杂乱的API...

替代方案#

没有替代方案,但这里涵盖的一些内容与 MEP25 重叠,这可能有助于此开发。

附录#

Matplotlib 基本元素#

这将形成样式表可以使用的初始选择器。

  • Line2D

  • 文本

  • AxesImage

  • FigureImage

  • 补丁