Combine key and mouse button events in wxpython panel using matplotlib

对着背影说爱祢 提交于 2019-12-13 01:06:16

问题


In a wxPython panel I want to use matplotlib's Lasso widget. In my implementation Lasso is used in three different functionalities. Nevertheless, in order to accomplish these functionalities I must combine key events with mouse button events.

By default, the initial Lasso function can be used by pressing the left mouse button. So, for my first functionality I press the left mouse button, select a region of interest and do something with the points included. For the second functionality, I would like to select another region of interest and do something else with these points.I am trying to do that by pressing shift+LMB. Finally, in my third functionality I would like to do something else with the selected points by pressing Ctrl+LMB.

Schematically, I would like to do the following:

 if left mouse button is pressed:
    use Lasso and with the included points do the 1st functionality

 if shift is press and left mouse button is pressed:

   use Lasso and with the included points do the 2nd functionality

 if ctrl is press and lmb is also pressed :
    call Lasso and with the included points do the 3rd functionality

Unfortunately, I can not achieve my goal and I get the following error: if self.shift_is_held == True: AttributeError: object has no attribute 'shift_is_held'. It seems that it can not recognize the button events, while in other cases the widgetlock command seems not to make the axes available.

Here are parts of my code:

def scatter(self,data):
#create scatter plot
    fig = self._view_frame.figure
    fig.clf()

    Data = []
    self.lmans = []
    self.points_colors = []

    Data2 = []
    self.lmans_2 = []

    Data3 = []
    self.lmans_2 = []


    ax = fig.add_subplot('111')

    datum = []
    datum_2 = []
    datum_3 = []

    for x,y in zip(self.data[:,0],self.data[:,1]):
        datum.append(Datum(x,y))
        datum_2.append(Datum2(x,y))
        datum_3.append(Datum3(x,y))


    ax.scatter(self.data[:, 0], self.data[:, 1], s=150, marker='d')
    ax.set_xlim((min(self.data[:,0]), max(self.data[:,0])))
    ax.set_ylim((min(self.data[:,1]), max(self.data[:,1])))
    ax.set_aspect('auto')

    Data.append(datum)
    Data2.append(datum_2)
    Data3.append(datum_3)
    lman = self.LassoManager(ax, datum, Data)
    lman_2 = self.LassoManager2(ax, datum_2, Data2)
    lman_3 = self.LassoManager3(ax, datum_3, Data3)
    fig.canvas.draw()
    self.lmans.append(lman)
    self.lmans_2.append(lman_2)
    self.lmans_3.append(lman_3)
    fig.canvas.mpl_connect('axes_enter_event', self.enter_axes)


class Lasso(AxesWidget):


"""Selection curve of an arbitrary shape.

The selected path can be used in conjunction with
:func:`~matplotlib.path.Path.contains_point` to select data points
from an image.

Unlike :class:`LassoSelector`, this must be initialized with a starting
point `xy`, and the `Lasso` events are destroyed upon release.

Parameters:

*ax* : :class:`~matplotlib.axes.Axes`
    The parent axes for the widget.
*xy* : array
    Coordinates of the start of the lasso.
*callback* : function
    Whenever the lasso is released, the `callback` function is called and
    passed the vertices of the selected path.

"""

def __init__(self, ax, xy, callback=None, useblit=True):
    AxesWidget.__init__(self, ax)

    self.useblit = useblit and self.canvas.supports_blit
    if self.useblit:
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)

    x, y = xy
    self.verts = [(x, y)]
    self.line = Line2D([x], [y], linestyle='-', color='black', lw=2)
    self.ax.add_line(self.line)
    self.callback = callback
    self.connect_event('button_release_event', self.onrelease)
    self.connect_event('motion_notify_event', self.onmove)

def onrelease(self, event):
    if self.ignore(event):
        return
    if self.verts is not None:
        self.verts.append((event.xdata, event.ydata))
        if len(self.verts) > 2:
            self.callback(self.verts)
        self.ax.lines.remove(self.line)
    self.verts = None
    self.disconnect_events()

def onmove(self, event):
    if self.ignore(event):
        return
    if self.verts is None:
        return
    if event.inaxes != self.ax:
        return
    if event.button != 1:
        return
    self.verts.append((event.xdata, event.ydata))

    self.line.set_data(list(zip(*self.verts)))

    if self.useblit:
        self.canvas.restore_region(self.background)
        self.ax.draw_artist(self.line)
        self.canvas.blit(self.ax.bbox)
    else:
        self.canvas.draw_idle()

def enter_axes(self, event):
    self.idfig = event.inaxes.colNum
    self._view_frame.figure.canvas.mpl_connect('button_press_event', self.onpress_2)
    self._view_frame.figure.canvas.mpl_connect('key_press_event', self.onkey_press_2)
    self._view_frame.figure.canvas.mpl_connect('key_release_event', self.onkey_release_2)
    self._view_frame.figure.canvas.mpl_connect('button_press_event', self.onpress_3)
    self._view_frame.figure.canvas.mpl_connect('key_press_event', self.onkey_press_3)
    self._view_frame.figure.canvas.mpl_connect('key_release_event', self.onkey_release_3)

def LassoManager(self, ax, data, Data):
    self.axes = ax
    self.canvas = ax.figure.canvas
    self.data = data
    self.Data = Data
    self.Nxy = len(data)

    # self.facecolors = [d.color for d in data]
    self.xys = [(d.x, d.y) for d in data]
    fig = ax.figure
    #self.cid = self.canvas.mpl_connect('button_press_event', self.onpress)



def callback(self, verts):
    #facecolors = self.facecolors#collection.get_facecolors()
    #colorin = colorConverter.to_rgba('red')
    #colorout = colorConverter.to_rgba('blue')
    p = path.Path(verts)
    self.ind = p.contains_points([(d.x, d.y) for d in self.Data[self.where.colNum]])
    self._view_frame.figure.canvas.mpl_connect('button_press_event', self.onpress)

    #Functionality 1

    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)
        self.where = event.inaxes
        # acquire a lock on the widget drawing
        self.canvas.widgetlock(self.lasso)

   def onkey_press_2(self,event):
    if event.key =='shift':
        self.merge_is_held = True


def onkey_press_3(self,event):
    if event.key == 'control':
        self.split_is_held = True

def onkey_release_2(self, event):
       if event.key == 'shift':
           self.merge_is_held = False


def onkey_release_3(self, event):
   if event.key == 'control':
        self.split_is_held = False

def LassoManagerMerge(self, ax, data2, Data2):
    self.axes = ax
    self.canvas = ax.figure.canvas
    self.data2 = data2
    self.Data2 = Data2
    self.Nxy = len(dataMerge)

    self.facecolors_2 = [d.color for d in data2]
    # print "facecolors",self.facecolors
    self.xys = [(d.x, d.y) for d in data2]
    # print "xys",self.xys
    fig = ax.figure
    self.collection_2 = RegularPolyCollection(
        fig.dpi, 6, sizes=(0,),
        facecolors=self.facecolors_2,
        offsets = self.xys,
        transOffset = ax.transData)

    ax.add_collection(self.collection_2)





def callback_2(self, verts):

    self.facecolors_2 = self.collection_2.get_facecolors()
    #colorin = colorConverter.to_rgba('red')
    #colorout = colorConverter.to_rgba('blue')
    p = path.Path(verts)
    self.ind = p.contains_points([(d.x, d.y) for d in self.Data2[self.where.colNum]])   
    #Functionality 2             

    self.canvas.draw_idle()
    self.canvas.widgetlock.release(self.lasso_2)

    del self.lasso_2

 def onpress_2(self, event):


            if self.canvas.widgetlock.locked():  return
            if event.inaxes is None: return
            if event.button == 1:
               if self.shift_is_held == True:
                    print "Shift pressed"
                    self.lasso_2 = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback_2)
                    print "Shift pressed"
                    self.where = event.inaxes
                    self.canvas.widgetlock(self.lasso_2)

              def LassoManager3(self, ax, data3, Data3):
    self.axes = ax
    self.canvas = ax.figure.canvas
    self.data3 = data3
    self.Data3 = Data3
    self.Nxy = len(data3)

    self.facecolors_3= [d.color for d in data3]
    # print "facecolors",self.facecolors
    self.xys = [(d.x, d.y) for d in data3]

    # print "xys",self.xys
    fig = ax.figure
    self.collection_3 = RegularPolyCollection(
        fig.dpi, 6, sizes=(0,),
        facecolors=self.facecolors_3,
        offsets = self.xys,
        transOffset = ax.transData)

    ax.add_collection(self.collection_3)



def callback_3(self, verts):
    self.facecolors_3 = self.collection_3.get_facecolors()
    #colorin = colorConverter.to_rgba('red')
    #colorout = colorConverter.to_rgba('blue')
    p = path.Path(verts)
    self.ind = p.contains_points([(d.x, d.y) for d in self.Data3[self.where.colNum]])

    #Functionality 3
    self.canvas.draw_idle()
    self.canvas.widgetlock.release(self.lasso_3)

    del self.lasso_3

def onpress_3(self, event):

            if self.canvas.widgetlock.locked(): return
            if event.inaxes is None: return
            if event.button == 1:
                if self.split_is_held == True:
                    print " Split pressed"
                    self.lasso_3 = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback_3)
                    print "Split pressed"
                    self.where = event.inaxes
                    # acquire a lock on the widget drawing
                    self.canvas.widgetlock(self.lasso_3)

Any suggestions?


回答1:


I'm not entirely sure what you're doing wrong because your code looks incomplete. I think your general idea was correct, but you seem to have been mixing up classes and trying to access the property shift_is_held from the wrong class or something.

I wrote this simple example using the lasso_example.py code from matplotlib examples. I did run into some complications trying to use the control key. When I try to drag with the mouse using the control key, the Lasso manager becomes unresponsive (including in the original code from matplotlib). I could not figure out why, so I used the shift and alt keys as modifiers in the present code.

You'll see that the logic of what action to perform depending on which key is held down at the moment when you release the lasso is performed in LassoManager.callback()

import logging
import matplotlib
from matplotlib.widgets import Lasso
from matplotlib.colors import colorConverter
from matplotlib.collections import RegularPolyCollection
from matplotlib import path

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.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
            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+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

if __name__ == '__main__':

    data = [Datum(*xy) for xy in rand(100, 2)]

    ax = plt.axes(xlim=(0,1), ylim=(0,1), autoscale_on=False)
    lman = LassoManager(ax, data)

    plt.show()


来源:https://stackoverflow.com/questions/32322090/combine-key-and-mouse-button-events-in-wxpython-panel-using-matplotlib

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!