2016-04-21 1 views
0

я сводился моей проблема следующего простого приложения в WxWidgets (я думаю, что это примерно так же, как маленький я могу получить его ...):Matplotlib event.mouseevent.key получает «застряли»

import wx 
import matplotlib 

matplotlib.use('WXAgg') 
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 

class GraphPanel(wx.Panel): 
    def __init__(self, *args, **kwargs): 
     super(GraphPanel, self).__init__(*args, **kwargs) 
     self._win    = None 
     self._figure   = matplotlib.figure.Figure() 
     self._canvas   = FigureCanvas(self, -1, self._figure) 
     self._ax    = self._figure.add_subplot(111) 
     self._canvas.mpl_connect('pick_event', self._OnGraphPickEvent) 
     x = [1,2,3] 
     y = [ 3,1,2] 
     self._ax.scatter(x, y, picker=True) 

    def _OnGraphPickEvent(self, event): 
     print event.mouseevent.key, event.guiEvent.ControlDown() 
     if event.mouseevent.key == 'control': 
     if self._win is None: 
      self._win = SecondFrame(self) 
      self._win.Show() 
     self._win.SetFocus() 
     self._win.Raise() 

class SecondFrame(wx.Frame): 
    def __init__(self, *args, **kwargs): 
     wx.Frame.__init__(self, *args, **kwargs) 
     self.Show() 

class FirstFrame(wx.Frame): 
    def __init__(self, *args, **kwargs): 
     wx.Frame.__init__(self, *args, **kwargs) 
     self._graph = GraphPanel(self, wx.ID_ANY) 
     szr = wx.BoxSizer(wx.VERTICAL) 
     szr.Add(self._graph, 1, wx.EXPAND) 
     self.SetSizerAndFit(szr) 
     self.Show() 

if __name__ == "__main__": 
    app = wx.App(False) 
    frame = FirstFrame(None) 
    app.MainLoop() 

Теперь интересная вещь для меня заключается в том, что оказывается, что значение для event.mouseevent.key застревает. В первый раз, когда я нажимаю точку разброса, удерживая ctrl, я получаю значение None. Если я снова нажму ctrl, тогда я получу ожидаемое значение и покажут новое окно. Но тогда, если я нажму, без удерживая ctrl, любые другие точки разброса, event.mouseevent.key застревает и постоянно сообщает control.

Единственная работа, которую я нашел, - это спуститься вниз по слою wx и использовать event.guiEvent.ControlDown(), что всегда кажется точным.

У этого есть что-то делать с отображением нового кадра, потому что если вы удалите код, который создает и показывает второй кадр, эта проблема не возникает.

Кто-нибудь знает, если я неправильно использую события matplotlib здесь? Я только что проводил онлайн-примеры, поэтому, возможно, я что-то не понял или упустил что-то, что связано с новыми кадрами и циклами событий?

Я использую WxWidgets 2.8.12.1 с питоном 2.7.3 на Ubuntu 12.04.3 LTS

Спасибо :)

+0

попробуйте вызвать event.Skip() в конце обработчика. – Igor

+0

Увы: AttributeError: объект 'PickEvent' не имеет атрибута 'Skip' Если я делаю event.guiEvent.Skip(), это не помогает :( Спасибо в любом случае :) – Jimbo

+0

Я написал запись.Skip() и не event.guiEvent.Skip(), правильно? ;-) – Igor

ответ

0

Т.Л., д-р

Хорошо, так что я сделал немного рыть. Причина, по которой ключ получает «застревание» в событии matplotlib, заключается в том, что бэкенд matplotlib использует события нажатия клавиш и клавиш, чтобы выяснить, какая клавиша нажата при нажатии мыши. Когда клавиша нажата, ее запоминают до тех пор, пока она не будет отпущена, поэтому, если пользователь делает key press --> mouse click --> key release, они будут нажаты и запоминаются и передаются обработчику событий щелчка мыши.

Поэтому, когда я запускаю новое окно и фокусирую его на событии клика, последующее событие релиза ключа «потеряно»: первый кадр никогда не получает его, поэтому бэкенд не сбрасывает его кешированное нажатие клавиши и сообщает устаревшее значение для всех последующих щелчков мыши до тех пор, пока не будет создано новое событие нажатия клавиши.

Там не кажется хорошим решением для конкретной задачи, в которой главное окно теряет фокус между нажатием клавиши и отпустить событие ...

Остальная часть объяснения

Этот ключ кеширование можно найти в backend_bases.py, который по моей системе Ubuntu находится в /usr/local/lib/python2.7/dist-packages/matplotlib/backends. Событие нажатия клавиши спрятало текущий ключ в self._key и сбрасывает его до None, когда ключ отпущен.

Трассировка стека от события подборщика моего первого кадра заключается в следующем:

File "test.py", line 57, in <module> 
    app.MainLoop() 
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/_core.py", line 8010, in MainLoop 
    wx.PyApp.MainLoop(self) 
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/_core.py", line 7306, in MainLoop 
    return _core_.PyApp_MainLoop(*args, **kwargs) 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/backends/backend_wx.py", line 1069, in _onLeftButtonDown 
    FigureCanvasBase.button_press_event(self, x, y, 1, guiEvent=evt) 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/backend_bases.py", line 1910, in button_press_event 
    self.callbacks.process(s, mouseevent) 
<snip> 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/backend_bases.py", line 1787, in pick 
    self.figure.pick(mouseevent) 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/artist.py", line 450, in pick 
    a.pick(mouseevent) 
<snip> 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/artist.py", line 436, in pick 
    self.figure.canvas.pick_event(mouseevent, self, **prop) 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/backend_bases.py", line 1874, in pick_event 
    self.callbacks.process(s, event) 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/cbook.py", line 563, in process 
    proxy(*args, **kwargs) 
File "/usr/local/lib/python2.7/dist-packages/matplotlib/cbook.py", line 430, in __call__ 
    return mtd(*args, **kwargs) 
File "test.py", line 22, in _OnGraphPickEvent 
    for line in traceback.format_stack(): 

Спускаясь уровень в классе FigureCanvasWx функции и _onKeyUp() привязанные к ключевым пресс-конференции WxWidgets. Они вызывают базовый класс FigureCanvasBase соответствующих функций обработчика событий.Как сказано, это FigureCanvasBase, который затем кэширует информацию о нажатии клавиши, чтобы она могла передать текущую клавишу, нажатую на события мыши, которые в конечном итоге достигают события выбора.

Я добавил несколько отладочных отпечатков в вышеуказанные классы. Дает следующее ...

Тест 1: показан первый кадр. Я нажимаю на график, но не на точку, потому что я не хочу запускать событие выбора, которое показывает новый кадр.

FigureCanvasWx:onLeftButtonDown: Ctrl down is "False" 
FigureCanvasBase:button_press_event: KEY IS None 
Artist:Pick:MPL MouseEvent: xy=(475,156) xydata=(2.88911290323,1.34375) button=1 dblclick=False inaxes=Axes(0.125,0.1;0.775x0.8) None 

Хорошо, что работает должным образом. wxPython и matplot lib согласны с нажатием клавиши управления.

Test 2: Теперь нажмите клавишу управления и нажмите на графике, но не на точке, просто пустое место, еще раз, чтобы увидеть эффект без стрельбы события подборщика, в котором новый кадр показано на рисунке.

FigureCanvasBase:key_press_event: SETTING KEY TO control 
FigureCanvasWx:onLeftButtonDown: Ctrl down is "True" 
FigureCanvasBase:button_press_event: KEY IS control 
Artist:Pick:MPL MouseEvent: xy=(440,144) xydata=(2.67741935484,1.25) button=1 dblclick=False inaxes=Axes(0.125,0.1;0.775x0.8) control 
FigureCanvasBase:key_release_event: RESETTING KEY #<<!! THE IMPORTANT BIT 

При нажатии клавиши управления Направьте FigureCanvasBase кэширует нажатие клавиши. Затем, когда событие mouse из wxPython завернуто в событие matplotlib, это кешированное значение ключа добавляется к событию matplotlib. На данный момент matplotlib и wxpython все еще согласны.

После моего нажатия я отпускаю управляющий ключ, а FigureCanvasBase правильно сбрасывает кеш ключа. Поэтому, когда я нажимаю снова, не удерживая клавишу управления, событие matplotlib правильно сообщает, что нажатие клавиши не выполняется.

Тест 3: * Теперь удерживайте клавишу управления вниз и нажмите на фактическую точку на моем графике, который выстреливает мой пикап событие, в котором создается новое окно, отображаются и данный фокус:

FigureCanvasBase:key_press_event: SETTING KEY TO control 
FigureCanvasWx:onLeftButtonDown: Ctrl down is "True" 
FigureCanvasBase:button_press_event: KEY IS control 
Artist:Pick:MPL MouseEvent: xy=(494,238) xydata=(3.00403225806,1.984375) button=1 dblclick=False inaxes=Axes(0.125,0.1;0.775x0.8) control 
FigureCanvasBase:pick_event: CREATED PICK EVENT <matplotlib.backend_bases.PickEvent object at 0xaf631ec> control 
control True 

Этот путь в порядке ... matplotlib и wxpython все еще согласны .... но подождите ...

О нет ... Я отключаю управляющий ключ, но в первом кадре больше нет фокуса ... нет событие выпуска ключа

Итак, теперь, когда я нажимаю любую точку на графике, пустое пространство события, я буду видеть, что клавиша управления нажата!

FigureCanvasWx:onLeftButtonDown: Ctrl down is "False" ##<< !!!! 
FigureCanvasBase:button_press_event: KEY IS control ##<< !!!! 
ARTIST PICK MPL MouseEvent: xy=(475,475) xydata=(None,None) button=1 dblclick=False inaxes=None control 

Единственный способ очистить это сделать первый фокус кадра и намеренно нажать клавишу, так что событие сброса может очистить кэш.

Интересно, что второй кадр, как представляется, не получает событие релиза, если я не намеренно нажимаю на него, прежде чем отпускать управляющий ключ, который помешает мне отправить это событие в первое окно и обойти его таким образом.

Итак ... похоже, что ключевая схема кэширования была проблемой в этом конкретном сценарии. Работа вокруг, кажется, единственный способ справиться с этим. Если бы мы могли заставить wxPython перечитать состояние клавиатуры вручную, мы могли бы исправить это, но в большинстве случаев, даже для функции wx.GetKeyState(), она будет сообщать только о том, не были ли клавиши управления, а не какой-либо ключ, чего хочет matplotlib.

 Смежные вопросы

  • Нет связанных вопросов^_^