matplotlib pick_event返回错误的按键

时间:2013-09-14 20:09:10

标签: matplotlib plot wxpython mouseevent

我正在编写一个将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'"

2 个答案:

答案 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)