问题
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