1

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'を押しました!」という永続的なメッセージに変わります。データ ポイントをコマンド クリックした場合にのみ、正しい「クリック中にキー 'None' を押しました!」が返されます。簡単なクリックのために。

(言うまでもなく、pressedKey の戻り値は非常に直感的ではありません。「Alt/Option」キーを 1 つ押すだけなのに、なぜ「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:

OK、問題は、ハンドラーが他のウィンドウを開くことがあり、それらのウィンドウでキーリリースイベントが「失われる」ことです。つまり、問題のキーが離されたことを 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」ハンドラーの最後に追加し、それをプロット パネルに送信したところ、うまくいきました。

これが私のコード例に対する(部分的な)解決策です:

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 に答える