MEP9: 全局交互管理器#
为所有用户与艺术家的交互添加一个全局管理器;使任何艺术家可根据用户需求进行调整大小、移动、高亮和选择。
状态#
讨论
分支和拉取请求#
摘要#
目标是能够以与绘图程序非常相似的方式与 matplotlib 艺术家进行交互。在适当的情况下,用户应该能够移动、调整大小或选择已经位于画布上的艺术家。当然,脚本编写者最终控制着艺术家是否可以进行交互,或者它是否是静态的。
这段代码已经私下实现并测试过,需要从当前的“mixin”实现迁移到 matplotlib 的正式部分。
最终结果将是为 matplotlib.artist.Artist 提供四个新关键字:_moveable_、_resizeable_、_selectable_ 和 _highlightable_。将这些关键字中的任何一个设置为 True 将激活该艺术家的交互性。
实际上,这个 MEP 是 matplotlib 中事件处理的逻辑扩展;matplotlib 已经支持“低级”交互,如鼠标左键按下、按键按下或类似操作。MEP 将支持扩展到逻辑层面,当检测到用户的某些交互手势时,会在艺术家上执行回调。
详细描述#
这个新功能将用于让最终用户更好地与图形交互。很多时候,图形几乎就是用户想要的,但需要对组件进行小幅度的重新定位和/或调整大小。与其强迫用户回到脚本中通过试错来调整位置,简单的拖放操作会更合适。
此外,这将更好地支持使用 matplotlib 的应用程序;在这里,最终用户没有合理的访问权限或愿望去编辑底层源代码以微调图表。在这里,如果 matplotlib 提供这种能力,用户可以移动或调整画布上的艺术家大小以满足他们的需求。此外,如果应用程序支持这种操作,用户应该能够通过鼠标悬停来突出显示艺术家,并通过双击来选择它。在这个 MEP 中,我们还希望原生支持突出显示和选择;应用程序负责处理艺术家被选中时发生的事情。典型的处理方式是显示一个对话框来编辑艺术家的属性。
在未来,同样地(这不是本 MEP 的一部分),matplotlib 可以为每个艺术家提供特定于后端的属性对话框,这些对话框在选择艺术家时弹出。本 MEP 将是实现这种功能所需的必要垫脚石。
目前,matplotlib 中有一些交互功能(例如 legend.draggable()),但它们往往分散且并非所有艺术家都可用。此 MEP 旨在统一交互接口,并使其适用于所有艺术家。
当前的 MEP 还包括用于调整艺术家大小的抓手,以及在移动或调整艺术家大小时绘制的适当框。
实现#
向艺术家的“树”中添加适当的方法,以便交互管理器有一个一致的接口来处理。如果艺术家要支持交互性,建议添加的方法是:
get_pixel_position_ll(self): 获取艺术家边界框左下角的像素位置
get_pixel_size(self): 获取艺术家边界框的大小,以像素为单位
set_pixel_position_and_size(self,x,y,dx,dy): 设置艺术家的新尺寸,使其适应指定的边界框。
为后端添加功能,使其能够 1) 提供光标,因为这些光标在移动/调整大小时需要用于视觉指示,以及 2) 提供一个获取当前鼠标位置的函数。
实现管理器。这已经由dhyams私下作为mixin完成,并且已经过相当多的测试。目标是将管理器的功能移入艺术家,以便它在matplotlib中正确实现,而不是像我目前编码的那样作为一个“猴子补丁”。
mixin 的当前总结#
(请注意,这个mixin目前只是私有代码,但显然可以添加到一个分支中)
InteractiveArtistMixin:
Mixin 类,用于使任何在 matplotlib 画布上绘制的通用对象可移动,并且可能可调整大小。尽可能遵循 PowerPoint 模型;不是因为我对 PowerPoint 情有独钟,而是因为这是大多数人所理解的。艺术家也可以是可选的,这意味着当双击时,艺术家将接收 on_activated() 回调。最后,艺术家可以是可高亮的,这意味着每当鼠标经过时,都会在艺术家上绘制高亮。通常,可高亮的艺术家也将是可选的,但这取决于用户。因此,基本上有四个属性可以由用户在每个艺术家基础上设置:
可高亮
可选择的
可移动的
可调整大小
要使对象可移动(可拖动)或可调整大小,mixin 的目标对象必须支持以下协议:
get_pixel_position_ll(self)
get_pixel_size(self)
set_pixel_position_and_size(self,x,y,sx,sy)
请注意,不可调整大小的对象可以自由忽略 sx 和 sy 参数。为了能够高亮显示,作为混合目标的对象还必须支持以下协议:
get_highlight(self)
这将返回一个艺术家列表,用于绘制高亮显示。
如果mixin的目标对象不是matplotlib艺术家,则还必须实现以下协议。这样做通常相当简单,因为必须在某个地方有一个正在绘制的艺术家。通常,您的对象只需将这些调用路由到该艺术家。
get_figure(self)
get_axes(self)
contains(self, event)
set_animated(self, flag)
draw(self,renderer)
get_visible(self)
以下通知会在艺术家上被调用,艺术家可以选择性地实现这些通知。
on_select_begin(self)
on_select_end(self)
on_drag_begin(self)
on_drag_end(self)
on_activated(self)
on_highlight(self)
on_right_click(self, event)
on_left_click(self, event)
on_middle_click(self, event)
on_context_click(self, event)
on_key_up(self, event)
on_key_down(self, event)
如果没有交互式艺术家处理事件,以下通知会在画布上调用:
on_press(self, event)
on_left_click(self, event)
on_middle_click(self, event)
on_right_click(self, event)
on_context_click(self, event)
on_key_up(self, event)
on_key_down(self, event)
以下函数(如果存在)可以用来修改交互对象的行为:
press_filter(self, event) # 确定对象是否希望将按下事件路由到它
handle_unpicked_cursor() # 当光标未选中对象时,对象可以使用此方法将光标设置为光标经过对象时的状态。
支持多个画布,保持拖动锁定,运动通知器,以及每个画布的全局“启用”标志。通过在调整大小时按住 shift 键,支持固定宽高比调整大小。
已知问题:
在选择/拖动操作期间,Zorder 不被遵守。由于使用了 blit 技术,我不认为这个问题可以修复。我能想到的唯一方法是搜索所有 zorder 大于我的艺术家,将它们全部设置为动画,然后在每次拖动刷新时将它们全部重绘在顶部。这可能会非常慢;需要尝试。
该混合类仅适用于 wx 后端,原因有两点:1) 光标是硬编码的,2) 调用了 wx.GetMousePosition()。这两个缺点可以通过让每个后端提供这些功能来合理地解决。
向后兼容性#
没有向后兼容的问题,尽管一旦这个功能到位,废弃一些现有的交互功能(如 legend.draggable())将是合适的。
替代方案#
据我所知没有。