我正在编写一个将matplotlib图集合到wxPython GUI中的程序。我希望能够区分一些绘图元素上的简单单击,OPTION单击,COMMAND-单击等(为了根据在单击绘图元素时按下哪个键来执行不同的操作)。
我的代码如下:
将matplotlib“pick_event”绑定到处理程序:
self.figure.canvas.mpl_connect('pick_event', self.onClick)
,检查哪个/是否按下了任何键
def onClick(self, event):
"""handle clicking on different objects in the plotarea"""
## get relevant event data
## ... other stuff here ...
pressedKey = None
pressedKey = event.mouseevent.key ## key pressed while clicking
print "You pressed key '%s' while clicking!" % pressedKey
## ... more stuff here ...
if pressedKey == "alt+alt":
self.onOptionClick(...)
...然后继续其他功能
然而,我的问题是,matplotlib返回的内容是简单的 - 错误。
例如,当我打开我的程序时,绘制我的数据,点击点(没有按任何键),我一直点击“你按下键'ctrl + control'点击!”。如果我选择 - 单击一个数据点,那么无论我是否按下选项键,这都会变为永久的“你按下的键'alt + alt',同时点击!”。只有在命令点击数据点后,它才会返回正确的“按下的键'无'点击!”用于简单点击。
(更不用说pressedKey返回非常不直观:如果我只是按下一个“Alt / Option”键,为什么“alt + alt”?为什么“ctrl + control”用于命令?)
对于我的程序的正确运行而言,能够区分不同类型的点击非常重要。
更新#1:
亲爱的,亲爱的。这变得越来越混乱。我的下面的示例代码很好,我的主程序仍然没有。怎么可能呢?此外,我的下面的小例子确实在“无”响应和简单点击的“”响应之间交替。 (我无法重现它;目前它只给我“无”回复 - 即“你按下键'没有'点击!”以下是示例代码:
#!/bin/usr/env python
import wx
import matplotlib as mpl
mpl.use('WXAgg')
from matplotlib.figure import Figure as mplFigure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as mplCanvas
class PlotPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = mplFigure(figsize=(9, 6))
self.ax = self.figure.add_subplot(111)
self.ax.plot([1, 2, 3, 4], [2, 3, 5, 8], marker="o", markersize=20, picker=10, linestyle="None")
self.canvas = mplCanvas(self, -1, self.figure)
self.figure.canvas.mpl_connect('pick_event', self.onClick)
def onClick(self, event):
pressedKey = None
pressedKey = event.mouseevent.key ## key pressed while clicking
print "You pressed key '%s' while clicking!" % pressedKey
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "matplotlib pick_event problem")
self.plotarea = PlotPanel(self)
self.mainSizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainSizer.Add(self.plotarea, 1, wx.EXPAND)
self.SetSizer(self.mainSizer)
self.mainSizer.Fit(self)
if __name__ == "__main__":
app = wx.App(False)
mainFrame = MainFrame()
mainFrame.Show()
app.MainLoop()
更新#2:
好的,所以问题似乎是我的处理程序有时打开其他窗口,并且密钥释放事件在这些窗口中“丢失”。也就是说,matplotlib永远不会知道有问题的密钥已经发布,等等下一次点击,仍然会出现按键被按下的印象,即使它不是。如果您将上面的处理程序更改为
def onClick(self, event):
pressedKey = None
pressedKey = event.mouseevent.key ## key pressed while clicking
wx.MessageBox("You pressed key '%s' while clicking!" % pressedKey)
它实际上再现了这个问题。
所以我想现在我的问题变成了:我如何(手动)告诉matplotlib密钥被释放了? “event.Skip()”不起作用; python告诉我
"PickEvent instance has no attribute 'Skip'"
答案 0 :(得分:1)
此处最简单的解决方案是放弃mouseevent.key
并使用wx.GetKeyState
功能:
def onClick(self, event):
print event
keys = ""
if wx.GetKeyState(wx.WXK_CONTROL):
keys += "ctrl "
if wx.GetKeyState(wx.WXK_ALT):
keys += "alt "
wx.MessageBox("You pressed key '%s' while clicking!" % keys)
如果这对您不起作用,最好自己跟踪上下按键。 但,要做到这一点,您需要在 EVERY 窗口中进行操作,以便在您进行跟踪时获得焦点,这是一个非常大的痛苦。
以下是一个例子:
class PlotPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = mplFigure(figsize=(9, 6))
self.ax = self.figure.add_subplot(111)
self.ax.plot([1, 2, 3, 4], [2, 3, 5, 8], marker="o", markersize=20, picker=10, linestyle="None")
self.canvas = mplCanvas(self, -1, self.figure)
self.figure.canvas.mpl_connect('pick_event', self.onClick)
self.canvas.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
self.canvas.Bind(wx.EVT_KEY_UP, self._on_key_up)
self.states = {"cmd":False, "ctrl":False, "shift":False}
def onClick(self, event):
print event
#print "You pressed key '%s' while clicking!" % pressedKey
print "Pressed keys:", [k for k in self.states if self.states[k]]
dlg = TestDialog(self)
dlg.ShowModal()
def _on_key_down(self, evt):
self._set_state(evt)
evt.Skip()
def _on_key_up(self, evt):
self._set_state(evt)
evt.Skip()
def _set_state(self, evt):
self.states["cmd"] = evt.CmdDown()
self.states["ctrl"] = evt.ControlDown()
self.states["shift"] = evt.ShiftDown()
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "matplotlib pick_event problem")
self.plotarea = PlotPanel(self)
self.mainSizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainSizer.Add(self.plotarea, 1, wx.EXPAND)
self.SetSizer(self.mainSizer)
self.mainSizer.Fit(self)
class TestDialog(wx.Dialog):
def __init__(self, parent):
pre = wx.PreDialog()
pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
pre.Create(parent, -1, "sample dialog", size=(200, 100), style=wx.CAPTION|wx.RESIZE_BORDER)
self.PostCreate(pre)
self.parent = parent
self.Bind(wx.EVT_KEY_DOWN, self.parent._on_key_down)
self.Bind(wx.EVT_KEY_UP, self.parent._on_key_up)
btn = wx.Button(self, -1, "OK")
btn.Bind(wx.EVT_BUTTON, self._OnClick)
def _OnClick(self, evt):
self.EndModal(wx.ID_OK)
一般来说,我发现matplotlib的wx画布非常有用,但它也不是一个适用于所有角落案例的完整解决方案。
答案 1 :(得分:0)
所以问题是我的密钥发布事件正在被某个地方吃掉 - 而且由于我的程序包含许多面板,小部件,对话框,我从未设法找到确切位置。但是我在“onClick”处理程序的末尾添加了一个手动创建的“key up”事件,并将其发送到绘图面板,结果很好!
这是我的示例代码的(部分)解决方案:
def onClick(self, event):
pressedKey = None
pressedKey = event.mouseevent.key ## key pressed while clicking
wx.MessageBox("You pressed key '%s' while clicking!" % pressedKey)
## manually send a "key up" event to the figure canvas
## since the other one gets eaten up somewhere...
## (comment out the two lines below to see the problem)
manual_key_up_event = wx.KeyEvent(wx.EVT_KEY_UP.typeId)
wx.PostEvent(self.figure.canvas, manual_key_up_event)