MEP27:将 pyplot 与后端分离#
状态#
进度
分支和 Pull requests#
主 PR(包括 GTK3):
后端特定分支差异:
摘要#
此 MEP 重构了后端,以提供更结构化和一致的 API,删除通用代码并整合现有代码.为此,我们建议拆分:
FigureManagerBase及其派生类到核心功能类FigureManager和后端特定类WindowBase,以及ShowBase及其派生类到Gcf.show_all和MainLoopBase.
详细描述#
此 MEP 旨在将后端 API 合并为一个统一的 API,从后端中删除通用代码(包括 _pylab_helpers 和 Gcf ),并将代码推送到 matplotlib 中更合适的级别.这样,我们就会自动消除后端中出现的不一致之处,例如 FigureManagerBase.resize(w, h) ,它有时会设置画布,有时会根据后端将整个窗口设置为给定的尺寸.
通用代码主要出现在从 FigureManagerBase 和 ShowBase 派生的类中的两个位置.
目前
FigureManagerBase有三个工作:文档将其描述为 pyplot 模式的辅助类,将所有内容打包成一个整洁的包
但它不仅仅包装画布和工具栏,它还完成所有窗口任务.这两个任务的合并在以下行中体现得最好:
self.set_window_title("Figure %d" % num)这将后端特定代码self.set_window_title(title)与 matplotlib 通用代码title = "Figure %d" % num结合在一起.目前,
FigureManager的后端特定子类决定何时结束主循环.这似乎也是非常错误的,因为图形不应该控制其他图形.
ShowBase有两个工作:它的工作是遍历在
_pylab_helpers.Gcf中注册的所有图形管理器,并告诉它们显示自己.其次,它的工作是执行后端特定的
mainloop以阻止主程序,从而防止图形消失.
实施#
此 MEP 的描述为我们提供了大部分解决方案:
从
FigureManagerBase中删除窗口方面,让它简单地包装这个新类以及其他后端类.创建一个新的WindowBase类,它可以处理此功能,并具有传递方法(:arrow_right:)到WindowBase.继承WindowBase的类也应该继承 GUI 特定窗口类,以确保向后兼容性(manager.window == manager.window).将
ShowBase的主循环重构为MainLoopBase,它也封装了循环的结束.我们将MainLoop的一个实例作为一个键解锁退出方法(要求在循环可以结束之前返回所有键)传递给FigureManager.请注意,这为多个后端同时运行提供了可能性.既然
FigureManagerBase中没有任何后端特定的东西,那么将其重命名为FigureManager,并移动到一个新文件backend_managers.py,并注意:这允许我们将后端的转换分解为单独的 PR,因为我们可以保持现有的
FigureManagerBase类及其依赖项完好无损.这也预见到了 MEP22,其中新的
NavigationBase已经演变为独立于后端的ToolManager.
FigureManagerBase(canvas, num) |
FigureManager(figure, num) |
|
笔记 |
|---|---|---|---|
show |
show |
||
destroy |
调用所有组件上的 destroy |
destroy |
|
full_screen_toggle |
处理逻辑 |
set_fullscreen |
|
resize |
resize |
||
key_press |
key_press |
||
get_window_title |
get_window_title |
||
set_window_title |
set_window_title |
||
_get_toolbar |
FigureManagerBase 的所有子类的通用方法 |
||
set_default_size |
|||
add_element_to_window |
ShowBase |
MainLoopBase |
笔记 |
|---|---|---|
mainloop |
begin |
|
end |
当子类不再存在实例时,自动调用 |
|
__call__ |
方法已移动到 Gcf.show_all |
未来兼容性#
正如上面在讨论 MEP 22 时所暗示的那样,此重构使得添加新的通用功能变得容易.目前,MEP 22 必须对每个从 FigureManagerBase 扩展的类进行丑陋的 hacks.有了这段代码,只需要在单个 FigureManager 类中进行修改即可.这也使得稍后弃用 NavigationToolbar2 非常简单,只需要修改单个 FigureManager 类
MEP 23 提供了另一个用例,其中此重构的代码将非常有用.
向后兼容性#
由于我们保持所有后端代码不变,只向现有类添加缺失的方法,因此这应该适用于所有用例.唯一的区别在于,由于 API 的标准化,那些使用 FigureManager.resize 来调整画布大小而不是窗口大小的后端.
我设想,此重构使之过时的类将被弃用,并与 NavigationToolbar2 在同一时间表上删除,另请注意,对 FigureCanvasWx 构造函数的调用签名更改,虽然向后兼容,但我认为旧的 (imho 丑陋的风格) 签名应该以与其他所有内容相同的方式被弃用和删除.
backend |
manager.resize(w,h) |
额外 |
|---|---|---|
gtk3 |
window |
|
Tk |
canvas |
|
Qt |
window |
|
Wx |
canvas |
FigureManagerWx 有 |
替代方案#
如果存在任何解决相同问题的替代方案,则应在此处讨论,并说明选择的方法的理由.
问题#
Mdehoon: 你能详细说明如何同时运行多个后端吗?
OceanWolf:@mdehoon,正如我所说,不是为了这个 MEP,但我认为这个 MEP 开启了它作为未来可能性的大门.基本上, MainLoopBase 类充当每个后端的 Gcf,在这个 MEP 中,它跟踪每个后端打开的图形数量,并管理这些后端的主循环.当它检测到没有图形仍然为该后端打开时,它会关闭后端特定的主循环.因此,我设想,只需少量调整,我们就可以实现完全多后端的 matplotlib.目前还不知道为什么有人会想要这样做,但我将这种可能性留在 MainLoopBase 中.将所有后端代码的细节从 FigureManager 中重构出来也有助于这一点,一个管理器来统治它们(后端).
Mdehoon: @OceanWolf, 好的,谢谢你的解释.对于 matplotlib 的可维护性来说,拥有统一的后端 API 非常重要.我认为这个 MEP 是朝着正确方向迈出的一步.