1

我正在编写一个将 matplotlib 图合并到 wxPython GUI 中的程序。我希望能够区分一些绘图元素上的简单单击、选项单击、命令单击等(以便根据单击绘图元素时按下的键来执行不同的操作)。

我的代码如下:

  • 将 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()”不工作;蟒蛇告诉我

"PickEvent instance has no attribute 'Skip'"
4

2 回答 2

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)

如果这对您不起作用,最好自己跟踪向上和向下按键。 但是,要做到这一点,您需要在每个可以在您进行跟踪时获得焦点的窗口中执行此操作,这是一个巨大的痛苦。

这是一个例子:

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 画布非常有用,但它也不是所有极端情况的完整解决方案。

于 2013-09-22T04:02:55.563 回答
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)
于 2013-09-17T22:10:39.183 回答