结合matplotlib的鼠标按键事件和pick事件

Combining matplotlib's mouse button events with pick events

基于鼠标按钮和按键事件的组合,将不同的功能应用于散点图的点。当按下鼠标左键时,调用 matplotlib 的 Lasso 小部件,并使用包含的点功能 1 发生。当按下 Shift+LMB 时,会绘制一个 Lasso 并且功能 2 会与包含的点一起发生。当按下 Alt+LMB 时,将绘制 Lasso 并使用包含的点功能 3 发生。最后但并非最不重要的一点是,当我按下 RMB 时,会触发一个拾取事件,并给出散点图中所选点的索引。

自从我添加了 pick 事件后,上述功能正常工作,直到 第一次 触发了 pick 事件。当它被触发时,似乎 canvas 被锁定,我无法使用任何其他功能。尽管我得到了所选点的索引,但我没有收到任何错误,并且 canvas 变得没有响应。

我修改了从问题中提取的代码,这实际上是我想做的。

代码:

import logging
import matplotlib
from matplotlib.widgets import Lasso
from matplotlib.colors import colorConverter
from matplotlib.collections import RegularPolyCollection
from matplotlib import path
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import rand

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)


class Datum(object):
      colorin = colorConverter.to_rgba('red')
      colorShift = colorConverter.to_rgba('cyan')
      colorCtrl = colorConverter.to_rgba('pink')
      colorout = colorConverter.to_rgba('blue')

      def __init__(self, x, y, include=False):
         self.x = x
         self.y = y
         if include:
            self.color = self.colorin
         else:
            self.color = self.colorout


class LassoManager(object):
      def __init__(self, ax, data):
         self.axes = ax
         self.canvas = ax.figure.canvas
         self.data = data

         self.Nxy = len(data)

         facecolors = [d.color for d in data]
         self.xys = [(d.x, d.y) for d in data]
         fig = ax.figure
         self.collection = RegularPolyCollection(fig.dpi, 6, sizes=(100,),facecolors=facecolors, offsets = self.xys, transOffset = ax.transData)

         ax.add_collection(self.collection)

         self.pick=self.canvas.mpl_connect('pick_event', self.onpick)
         self.cid = self.canvas.mpl_connect('button_press_event', self.onpress)
         self.keyPress = self.canvas.mpl_connect('key_press_event', self.onKeyPress)
         self.keyRelease = self.canvas.mpl_connect('key_release_event', self.onKeyRelease)
         self.lasso = None
         self.shiftKey = False
         self.ctrlKey = False

      def callback(self, verts):
          logging.debug('in LassoManager.callback(). Shift: %s, Ctrl: %s' % (self.shiftKey, self.ctrlKey))
          facecolors = self.collection.get_facecolors()
          p = path.Path(verts)
          ind = p.contains_points(self.xys)
          for i in range(len(self.xys)):
              if ind[i]:
                 if self.shiftKey:
                    facecolors[i] = Datum.colorShift
                 elif self.ctrlKey:
                    facecolors[i] = Datum.colorCtrl
                 else:
                    facecolors[i] = Datum.colorin
                    print self.xys[i]
              else:
                   facecolors[i] = Datum.colorout

          self.canvas.draw_idle()
          self.canvas.widgetlock.release(self.lasso)
          del self.lasso

      def onpress(self, event):
           if self.canvas.widgetlock.locked(): return
           if event.inaxes is None:    return
           self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback)
           # acquire a lock on the widget drawing
           self.canvas.widgetlock(self.lasso)

      def onKeyPress(self, event):
          logging.debug('in LassoManager.onKeyPress(). Event received: %s (key: %s)' % (event, event.key))
          if event.key == 'alt':
             self.ctrlKey = True
          if event.key == 'shift':
             self.shiftKey = True

      def onKeyRelease(self, event):
          logging.debug('in LassoManager.onKeyRelease(). Event received: %s (key: %s)' % (event, event.key))
          if event.key == 'alt':
             self.ctrlKey = False
          if event.key == 'shift':
             self.shiftKey = False


      def onpick(self,event):

          if event.mouseevent.button == 3:

             index = event.ind
             print('onpick scatter:', index, np.take(x, index), np.take(y, index))

      if __name__ == '__main__':

         x,y =rand(2,100)
         data = [Datum(*xy) for xy in zip(x,y)]
         fig = plt.figure()
         ax = plt.axes()

         ax.scatter(x,y,picker=True)

         lman = LassoManager(ax, data)
         plt.show()

关于可能导致此故障的原因有什么建议吗?提前致谢。

您这次遇到的问题是,当您点击一位艺术家时,您同时生成了 PickEventMouseEventMouseEvent 锁定 canvas 并阻止您之后做任何其他事情。

最好的解决方案是防止 MouseEventPickEvent 之后被触发,但我不知道是否有办法做到这一点。相反,我添加了一个测试来检查 onpress() 是否在 onpick() 之后调用以禁用锁定机制。

import logging
import matplotlib
from matplotlib.widgets import Lasso
from matplotlib.colors import colorConverter
from matplotlib.collections import RegularPolyCollection
from matplotlib import path
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import rand

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)


class Datum(object):
      colorin = colorConverter.to_rgba('red')
      colorShift = colorConverter.to_rgba('cyan')
      colorCtrl = colorConverter.to_rgba('pink')
      colorout = colorConverter.to_rgba('blue')

      def __init__(self, x, y, include=False):
         self.x = x
         self.y = y
         if include:
            self.color = self.colorin
         else:
            self.color = self.colorout


class LassoManager(object):
      def __init__(self, ax, data):
         self.axes = ax
         self.canvas = ax.figure.canvas
         self.data = data

         self.Nxy = len(data)

         facecolors = [d.color for d in data]
         self.xys = [(d.x, d.y) for d in data]
         fig = ax.figure
         self.collection = RegularPolyCollection(fig.dpi, 6, sizes=(100,),facecolors=facecolors, offsets = self.xys, transOffset = ax.transData)

         ax.add_collection(self.collection)

         self.cid = self.canvas.mpl_connect('button_press_event', self.onpress)
         self.keyPress = self.canvas.mpl_connect('key_press_event', self.onKeyPress)
         self.keyRelease = self.canvas.mpl_connect('key_release_event', self.onKeyRelease)
         self.pick=self.canvas.mpl_connect('pick_event', self.onpick)
         self.lasso = None
         self.shiftKey = False
         self.ctrlKey = False
         self.pickEvent = False

      def callback(self, verts):
          logging.debug('in LassoManager.callback(). Shift: %s, Ctrl: %s' % (self.shiftKey, self.ctrlKey))
          facecolors = self.collection.get_facecolors()
          p = path.Path(verts)
          ind = p.contains_points(self.xys)
          for i in range(len(self.xys)):
              if ind[i]:
                 if self.shiftKey:
                    facecolors[i] = Datum.colorShift
                 elif self.ctrlKey:
                    facecolors[i] = Datum.colorCtrl
                 else:
                    facecolors[i] = Datum.colorin
                    print self.xys[i]
              else:
                   facecolors[i] = Datum.colorout

          self.canvas.draw_idle()
          self.canvas.widgetlock.release(self.lasso)
          del self.lasso

      def onpress(self, event):
            logging.debug('in LassoManager.onpress(). Event received: %s' % event)
            if self.pickEvent:
                self.pickEvent = False
                return
            if self.canvas.widgetlock.locked(): return
            if event.inaxes is None: return
            self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback)
            # acquire a lock on the widget drawing
            self.canvas.widgetlock(self.lasso)

      def onKeyPress(self, event):
          logging.debug('in LassoManager.onKeyPress(). Event received: %s (key: %s)' % (event, event.key))
          if event.key == 'alt':
             self.ctrlKey = True
          if event.key == 'shift':
             self.shiftKey = True

      def onKeyRelease(self, event):
          logging.debug('in LassoManager.onKeyRelease(). Event received: %s (key: %s)' % (event, event.key))
          if event.key == 'alt':
             self.ctrlKey = False
          if event.key == 'shift':
             self.shiftKey = False

      def onpick(self, event):
          logging.debug('in LassoManager.onpick(). Event received: %s' % event)
          self.pickEvent = True
          if event.mouseevent.button == 3:
             index = event.ind
             print 'onpick scatter: ', index, np.take(x, index), np.take(y, index)


if __name__ == '__main__':
    x,y =rand(2,100)
    data = [Datum(*xy) for xy in zip(x,y)]
    fig = plt.figure()
    ax = plt.axes()

    ax.scatter(x,y,picker=True)

    lman = LassoManager(ax, data)
    plt.show()