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(自我)
获取像素大小(自我)
set_pixel_position_and_size(self,x,y,sx,sy)
请注意,不可调整大小的对象可以随意忽略 sx 和 sy 参数。要突出显示,作为 mixin 目标的对象还必须支持以下协议:
get_highlight(自我)
它返回将用于绘制亮点的艺术家列表。
如果 mixin 的目标对象不是 matplotlib 艺术家,则还必须实现以下协议。这样做通常是相当微不足道的,因为在某个地方必须有一位正在绘制的艺术家。通常,您的对象只会将这些调用路由到该艺术家。
get_figure(自我)
获取轴(自我)
包含(自我,事件)
set_animated(自我,标志)
绘制(自我,渲染器)
get_visible(自我)
在艺术家上调用以下通知,艺术家可以选择实现这些。
on_select_begin(self)
on_select_end(自我)
on_drag_begin(self)
on_drag_end(self)
on_activated(自我)
on_highlight(自我)
on_right_click(自我,事件)
on_left_click(自我,事件)
on_middle_click(自我,事件)
on_context_click(自我,事件)
on_key_up(自我,事件)
on_key_down(自我,事件)
如果没有交互式艺术家处理该事件,则会在画布上调用以下通知:
on_press(自我,事件)
on_left_click(自我,事件)
on_middle_click(自我,事件)
on_right_click(自我,事件)
on_context_click(自我,事件)
on_key_up(自我,事件)
on_key_down(自我,事件)
以下函数(如果存在)可用于修改交互对象的行为:
press_filter(self,event) # 确定对象是否希望将按下事件路由到它
handle_unpicked_cursor() # 对象可以使用它来设置光标,因为光标在取消选中时经过对象。
支持多个画布,为每个画布维护一个拖动锁定、运动通知器和一个全局“启用”标志。通过在调整大小期间按住 shift 键支持固定纵横比调整大小。
已知问题:
在选择/拖动操作期间不服从 Zorder。由于使用了 blit 技术,我不相信这可以解决。我能想到的唯一方法是搜索所有 zorder 大于我的艺术家,将它们全部设置为动画,然后在每次拖动刷新期间将它们全部重绘在顶部。这可能很慢;需要尝试。
mixin 仅适用于 wx 后端,原因有两个:1) 游标是硬编码的,2) 调用 wx.GetMousePosition() 通过让每个后端提供这些东西,这两个缺点都得到了合理的解决。
向后兼容性#
向后兼容没有问题,尽管一旦到位,废弃一些现有的交互功能(如legend.draggable())是合适的
替代方案#
据我所知没有。