Phoenix icon indicating copy to clipboard operation
Phoenix copied to clipboard

MouseWheelEvent is broken if MotionEvent is saved

Open chiga17 opened this issue 3 years ago • 9 comments
trafficstars

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:

  1. Generate mouse wheel on main frame. Logs are printed: Wheel rotation: -120 Wheel rotation: 120 This is correct (as expected)
  2. Then over the main frame press left mouse button and move mouse. The event will be saved as main frame member.
  3. Generate wheel again. Logs are printed: Wheel rotation: 552 Wheel rotation: 552 This 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

chiga17 avatar Nov 22 '21 15:11 chiga17

That is strange. Do you mind testing this with version 4.0.7 on Windows to see if you see the same behavior there?

swt2c avatar Nov 22 '21 16:11 swt2c

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

chiga17 avatar Nov 22 '21 16:11 chiga17

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.

komoto48g avatar Nov 25 '21 11:11 komoto48g

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).

chiga17 avatar Nov 25 '21 16:11 chiga17

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.

komoto48g avatar Nov 27 '21 12:11 komoto48g

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.

chiga17 avatar Nov 29 '21 13:11 chiga17

Thank you for the information. I will look into the motion event.

komoto48g avatar Nov 30 '21 16:11 komoto48g

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

RobinD42 avatar Jan 12 '22 12:01 RobinD42

@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.

komoto48g avatar Jan 27 '22 14:01 komoto48g