如何在 matplotlib 中为交互式绘图的 onclick(event) 函数创建类?

Posted

技术标签:

【中文标题】如何在 matplotlib 中为交互式绘图的 onclick(event) 函数创建类?【英文标题】:How to make class for onclick(event) functions for Interactive plot in matplotlib? 【发布时间】:2021-01-14 16:28:53 【问题描述】:

我尝试使用一些函数创建交互式 matplotlib 图。我想将这些函数归为一个类(我还是新手,从别人的代码中得到帮助)

import matplotlib.pyplot as plt


def draw_line(startx,starty):

    ax = plt.gca()
    xy = plt.ginput(1)
    x = [startx,xy[0][0]]
    y = [starty,xy[0][1]]
    line = ax.plot(x,y, picker=True , pickradius = 5 , color = "blue")
    ax.figure.canvas.draw()        


def onclick(event):

    """
    This implements click functionality.  If it's a double click do something,
    else ignore.
    Once in the double click block, if its a left click, wait for a further 
    click and draw a line between the double click co-ordinates and that click
    (using ginput(1) - the 1 means wait for one mouse input - a higher number
    is used to get multiple clicks to define a polyline)
    """
    ax = plt.gca()

    if event.dblclick:
        if event.button == 1:
            # Draw line
            draw_line(event.xdata,event.ydata) # here you click on the plot
        else:
            pass # Do nothing

    if event.button == 1:
        pass


def onpick(event):    

    ax = plt.gca()

    """

    Handles the pick event - if an object has been picked, store a
    reference to it.  We do this by simply adding a reference to it
    named 'stored_pick' to the axes object.  Note that in python we
    can dynamically add an attribute variable (stored_pick) to an 
    existing object - even one that is produced by a library as in this
    case

    """

    this_artist = event.artist  # the picked object is available as event.artist
    ax.picked_object = this_artist

def on_key(event):

    """
    Function to be bound to the key press event
    If the key pressed is delete and there is a picked object,
    remove that object from the canvas
    """

    if event.key == u'delete':
        ax = plt.gca()
        if ax.picked_object:
            ax.picked_object.remove()
            ax.picked_object = None
            ax.figure.canvas.draw()


def applyplt():

    fig = plt.gcf()
    ax = plt.gca()

    cidonclic = fig.canvas.mpl_connect('button_press_event', onclick)
    cidonpic = fig.canvas.mpl_connect('pick_event', onpick)
    cidonkey = fig.canvas.mpl_connect('key_press_event', on_key)

"""
Basic Plot to test the function.
"""

fig1 = plt.figure(figsize = (10,10))
gs = fig1.add_gridspec(10,10)
ax101 = fig1.add_subplot(gs[:,:])
ax101.set_ylim(0,10)
ax101.set_xlim(0,10)

applyplt()

plt.show()

我想将这些事件函数归为一个class name(object)(例如:class Drawer(object)

如果可以进行任何其他优化,也请提出建议。谢谢!

【问题讨论】:

【参考方案1】:

下面是分组到一个类中的函数:

import matplotlib.pyplot as plt


class Interactivity:
    def __init__(self, fig = None):
        self.fig = plt.gcf() if fig is None else fig
        self.ax = self.fig.gca()
        self.connections = ()

    def __enter__(self):
        self.connect()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.disconnect()

    def connect(self):
        """ Install the event handlers for the plot. """
        self.connections = (
            self.fig.canvas.mpl_connect('button_press_event', self.onclick),
            self.fig.canvas.mpl_connect('pick_event', self.onpick),
            self.fig.canvas.mpl_connect('key_press_event', self.on_key),
        )

    def disconnect(self):
        """ Uninstall the event handlers for the plot. """
        for connection in self.connections:
            self.fig.canvas.mpl_disconnect(connection)

    def draw_line(self, startx, starty):
        xy = plt.ginput(1)
        x = [startx, xy[0][0]]
        y = [starty, xy[0][1]]
        self.ax.plot(x, y, picker=True, pickradius=5, color='blue')
        self.ax.figure.canvas.draw()

    def onclick(self, event):
        """
        This implements click functionality. If it's a double click do
        something, else ignore.
        Once in the double click block, if its a left click, wait for a further
        click and draw a line between the double click co-ordinates and that
        click (using ginput(1) - the 1 means wait for one mouse input - a
        higher number is used to get multiple clicks to define a polyline)
        """
        print('onclick')
        if event.dblclick:
            if event.button == 1:
                self.draw_line(event.xdata, event.ydata)


    def onpick(self, event):
        """
        Handles the pick event - if an object has been picked, store a
        reference to it.  We do this by simply adding a reference to it
        named 'picked_object' to the axes object.
        """
        print('onpick')
        this_artist = event.artist
        # the picked object is available as event.artist
        self.ax.picked_object = this_artist

    def on_key(self, event):
        """
        Function to be bound to the key press event
        If the key pressed is delete and there is a picked object,
        remove that object from the canvas
        """
        print('onkey: ', event.key)
        if event.key == 'delete' and self.ax.picked_object:
            self.ax.picked_object.remove()
            self.ax.picked_object = None
            self.ax.figure.canvas.draw()

用法:

# Basic plot to test the functionality
fig = plt.figure(figsize = (10,10))
gs = fig.add_gridspec(10,10)
ax101 = fig.add_subplot(gs[:,:])
ax101.set_ylim(0,10)
ax101.set_xlim(0,10)
with Interactivity():
    plt.show()

如您所见,它可以用作上下文处理程序来自动安装和卸载处理程序,或者您可以创建它的实例,然后调用其connect 方法手动安装处理程序(然后稍后可选地调用disconnect 方法来卸载处理程序)。

【讨论】:

以上是关于如何在 matplotlib 中为交互式绘图的 onclick(event) 函数创建类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 matplotlib 中为 3d plot_surface 设置动画

使用 matplotlib 在 Spyder 中进行交互式(?)绘图

Python之神奇的绘图库matplotlib

matplotlib如何生成纯黑幕布

如何使用 python/matplotlib 为 3d 绘图设置“相机位置”?

如何在 IPython 笔记本中打开交互式 matplotlib 窗口?