Phoenix
Phoenix copied to clipboard
MouseWheelEvent is broken if MotionEvent is saved
Operating system: Windows 10 wxPython version & source: 4.1.1 (installed via pip) Python version & source: 3.8
Description of the problem: If I save motion events inside main window (actual place does not matter) and then generate wheel event, then wheel event has corrupted data. For some reason the issue is not reproduced on Linux (although there is a different python version there). To reproduce on example code one should perform the following steps:
- Generate mouse wheel on main frame. Logs are printed:
Wheel rotation: -120 Wheel rotation: 120This is correct (as expected) - Then over the main frame press left mouse button and move mouse. The event will be saved as main frame member.
- Generate wheel again. Logs are printed:
Wheel rotation: 552 Wheel rotation: 552This is wrong!
Code Example (click to expand)
import sys
import wx
print('sys.version:', sys.version)
print('wx.__version__:', wx.__version__)
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1)
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
self.last_event = None
def OnMouseMove(self, evt):
if evt.leftIsDown:
print("Saving MouseMove event")
self.last_event = evt
def OnMouseWheel(self, evt):
print("Wheel rotation: " + str(evt.GetWheelRotation()))
app = wx.App(0)
frame = MyFrame(None)
frame.Show()
app.MainLoop()
Result on Win10:
sys.version: 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
wx.__version__: 4.1.1
Wheel rotation: -120
Wheel rotation: 120
Saving MouseMove event
Saving MouseMove event
Saving MouseMove event
Wheel rotation: 552
Wheel rotation: 552
Result on Linux:
sys.version: 3.7.7 (default, May 7 2020, 21:25:33)
[GCC 7.3.0]
wx.__version__: 4.1.1
Wheel rotation: -120
Wheel rotation: 120
Saving MouseMove event
Saving MouseMove event
Wheel rotation: 120
Wheel rotation: -120
That is strange. Do you mind testing this with version 4.0.7 on Windows to see if you see the same behavior there?
Looks like 4.0.7 works fine! Might it be related to some changes in WxWidget's MouseEvent for MacOS between 4.0.7 and 4.1.0?
sys.version: 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
wx.__version__: 4.0.7
Wheel rotation: -120
Wheel rotation: 120
Saving MouseMove event
Saving MouseMove event
Wheel rotation: 120
Wheel rotation: -120
My opinion is 'Don't keep an event reference'.
The Event object seems to live within a limited frame (similar to PaintDC). Your self.last_event.XXX could be a reference of dead c++ object, and the program will crash easily. If you need event information, save them as primitve types such as int, float, etc.
If you still want to save an event, however, the following hack will work:
self.last_event = evt.__class__(evt)
This calls implicit copy-constructor. This is not documented, and I'm not sure it will work in the future version.
Of course there are a lot of ways to overcome this issue. But still, if event reference cannot be kept this should be at least stated somewhere in the docs. Otherwise it might confuse wx users. Another thing is that matplotlib saves event reference inside it's FigureCanvasWxAgg (actually I faced this issue using matplotlib and then began go deeper).
I don't know if it will be improved in the future, but as you say, if this issue is going to be persistent, I think it should be documented (maybe in wx.EventObject ?)
Btw, I would like to know what kind of problem you were facing with FigureCanvasWxAgg. I am using a copy of event <matplotlib.backend_bases.MouseEvent> in my program to refer to the original click-point (xdata, ydata), but there seems to be no problem for now. I'm not sure that matplotlib keeps wx.Object referece. If there were problems, they should be reported to the matplotlib development team.
Here are the links to matplotlib where they store mouse events: https://github.com/matplotlib/matplotlib/blob/54cf7bc44ade7aa3b664b20b175e21294084bd9c/lib/matplotlib/backend_bases.py#L1926 https://github.com/matplotlib/matplotlib/blob/54cf7bc44ade7aa3b664b20b175e21294084bd9c/lib/matplotlib/backend_bases.py#L1361
There is motion_notify_event that handles mouse movements. It creates MouseEvent, which calls parent's LocationEvent._update_enter_leave that saves self into class field. Self instance contains wx mouse event as guiEvent field.
Regarding reporting to matplotlib - I'm not sure that it should be reported until there is a statement in the doc that event cannot be stored. As of now this more looks like degradation in wxpython (between 4.0.7 and 4.1) rather than a bug in matplotlib.
If I have time I'll try to compose minimal example with matplotlib that reproduces the issue.
Thank you for the information. I will look into the motion event.
This issue has been mentioned on Discuss wxPython. There might be relevant details there:
https://discuss.wxpython.org/t/a-problem-with-event-objects/35766/1
@chiga17, I put the following snippet code in my program. It is like a monkey patch but it works fine.
## patch for matplotlib 3.4/WXAgg
if 1:
from matplotlib.backend_bases import Event
def __init__(self, name, canvas, guiEvent=None):
self.name = name
self.canvas = canvas
self.guiEvent = None
Event.__init__ = __init__
del __init__
@Robin, @swt2c, @DietmarSchwertberger, I would appreciate any comments (hopefully an announcement) about this issue.