Python GUI 绘制点击散点图

106 阅读2分钟

在使用 matplotlib 绘制交互式图时,用户希望能够在点击图上的某个点时,在该位置绘制一个散点,同时希望能够方便地实现撤销上一个绘制的散点、清除所有散点和保存图像的功能。

2、解决方案

为了满足用户的需求,可以采用以下解决方案:

  1. 使用 matplotlib 的 button_press_event 事件来捕获鼠标点击事件。
  2. 在事件处理函数中,根据鼠标点击的按钮来决定是绘制红色散点还是蓝色散点。
  3. 将绘制的散点存储在一个列表中,以便能够轻松地撤销或清除。
  4. 使用按钮来实现撤销、清除和保存功能。

以下是如何实现上述解决方案的代码示例:

import matplotlib.pyplot as plt
import numpy as np

class ClickerClass:
    def __init__(self, ax, pix_err=1):
        self.canvas = ax.get_figure().canvas
        self.cid = None
        self.pt_lst = []
        self.pt_plot = ax.plot([], [], marker='o',
                               linestyle='none', zorder=5)[0]
        self.pix_err = pix_err
        self.connect_sf()

    def set_visible(self, visible):
        '''sets if the curves are visible '''
        self.pt_plot.set_visible(visible)

    def clear(self):
        '''Clears the points'''
        self.pt_lst = []
        self.redraw()

    def connect_sf(self):
        if self.cid is None:
            self.cid = self.canvas.mpl_connect('button_press_event',
                                               self.click_event)

    def disconnect_sf(self):
        if self.cid is not None:
            self.canvas.mpl_disconnect(self.cid)
            self.cid = None

    def click_event(self, event):
        ''' Extracts locations from the user'''
        if event.key == 'shift':
            self.pt_lst = []
            return
        if event.xdata is None or event.ydata is None:
            return
        if event.button == 1:
            self.pt_lst.append((event.xdata, event.ydata))
        elif event.button == 3:
            self.remove_pt((event.xdata, event.ydata))

        self.redraw()

    def remove_pt(self, loc):
        if len(self.pt_lst) > 0:
            self.pt_lst.pop(np.argmin(map(lambda x:
                                          np.sqrt((x[0] - loc[0]) ** 2 +
                                                  (x[1] - loc[1]) ** 2),
                                          self.pt_lst)))

    def redraw(self):
        if len(self.pt_lst) > 0:
            x, y = zip(*self.pt_lst)
        else:
            x, y = [], []
        self.pt_plot.set_xdata(x)
        self.pt_plot.set_ydata(y)

        self.canvas.draw()

    def return_points(self):
        '''Returns the clicked points in the format the rest of the
        code expects'''
        return np.vstack(self.pt_lst).T

ax = plt.gca()
cc = ClickerClass(ax)

在这个代码示例中,我们定义了一个 ClickerClass 类来处理鼠标点击事件。ClickerClass 类具有以下方法:

  • connect_sf:连接鼠标点击事件。
  • disconnect_sf:断开鼠标点击事件。
  • click_event:处理鼠标点击事件。
  • remove_pt:从列表中移除最近的散点。
  • redraw:重新绘制散点图。
  • return_points:返回绘制的散点坐标。

在主程序中,我们创建一个 ClickerClass 类的实例,并将它与绘图轴关联起来。然后,我们使用 show() 方法显示图像。

当用户点击图像时,ClickerClass 类的 click_event 方法就会被调用。这个方法会根据鼠标点击的按钮来决定是绘制红色散点还是蓝色散点。然后,将绘制的散点存储在列表中并重新绘制散点图。

用户可以通过按住 Shift 键并点击鼠标来撤销上一个绘制的散点。也可以通过点击“清除”按钮来清除所有散点。还可以通过点击“保存”按钮来将图像保存为文件。