MEP9:全局交互管理器#

为所有用户与艺术家的交互添加一个全局管理器;根据用户的需要,使任何艺术家都可以调整大小,移动,突出显示和选择.

状态#

讨论

分支和 Pull requests#

dhyams/matplotlib

摘要#

目标是以与绘图程序非常相似的方式与 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 中,而不是像我目前编码的那样作为"monkey patch".

mixin 的当前摘要#

(请注意,这个 mixin 目前只是私有代码,但可以添加到分支中)

InteractiveArtistMixin:

Mixin 类,用于使任何在 matplotlib 画布上绘制的通用对象可移动并且可能可调整大小.尽可能遵循 Powerpoint 模型;不是因为我迷恋 Powerpoint,而是因为这是大多数人理解的.艺术家也可以是可选择的,这意味着当双击时,艺术家将收到 on_activated() 回调.最后,艺术家可以是可高亮的,这意味着每当鼠标经过时,都会在艺术家上绘制一个高亮.通常,可高亮的艺术家也是可选择的,但这取决于用户.因此,基本上有四个属性可以由用户按每个艺术家设置:

  • highlightable

  • selectable

  • moveable

  • resizeable

要可移动(可拖动)或可调整大小,mixin 的目标对象必须支持以下协议:

  • get_pixel_position_ll(self)

  • get_pixel_size(self)

  • set_pixel_position_and_size(self,x,y,sx,sy)

请注意,不可调整大小的对象可以自由地忽略 sx 和 sy 参数.要可高亮,mixin 的目标对象还必须支持以下协议:

  • 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) # 确定对象是否希望将 press 事件路由到它

  • handle_unpicked_cursor() # 可以由对象使用,以在光标经过未选中的对象时设置光标.

支持多个画布,维护拖动锁定,运动通知器以及每个画布的全局"启用"标志.通过在调整大小期间按住 shift 键来支持固定纵横比调整大小.

已知问题:

  • 在选择/拖动操作期间不遵守 Zorder.由于使用的 blit 技术,我不相信可以修复此问题.我能想到的唯一方法是搜索所有 zorder 大于我的艺术家,将它们全部设置为 animated,然后在每次拖动刷新期间在顶部重新绘制它们.这可能非常慢;需要尝试.

  • 该mixin仅适用于wx后端,原因有两点:1) 光标是硬编码的,以及 2) 有一个对 wx.GetMousePosition() 的调用.通过让每个后端提供这些东西,可以合理地修复这两个缺点.

向后兼容性#

没有向后兼容性问题,尽管一旦到位,适当地废弃一些现有的交互式函数(如 legend.draggable())是合适的.

替代方案#

据我所知没有.