A problem with Event objects

Hi there :wave:

This post is related to the open issue of Phoenix MouseWheelEvent is broken if MotionEvent is saved, where OP pointed out that there are similar bugs with matplotlib/WXAgg. I have already opened the issue in [Matplotlib issue tracker] and shown options for resolving it, but a few things are not clear to me.

This issue is caused by keeping a reference to the event object.
Here’s an example with a bit of modification of the original code from the [Phoenix issue tracker] that reproduces the problem.

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, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        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()))
        test(self.last_event)

def test(obj):
    d = {}
    for key in dir(obj):
        try:
            print("key =", key)
            d[key] = getattr(obj, key) # Windows fatal exception: access violation
        except Exception as e:
            print(e)
            pass
    return d

app = wx.App(0)
frame = MyFrame(None)
frame.Show()
app.MainLoop()

To reproduce the bug and messages

  1. Launch this script with debug options
    $ python -Xdev -Xtracemalloc ‘script.py’
  2. Press LBtn and move the mouse.
    Then, the last wx.MouseEvent object is saved.
  3. Scroll the mouse wheel to show outputs.
    Then, a fault occurs when accessing evt.ClassInfo, and it crashes with Windows fatal exception: access violation.

I confirmed this for Windows 10 64 bit &
PY38 - wx 4.0.7 and wx 4.1.1,
PY39 - wx 4.1.1.

In my understanding, the reference of the event object should not be kept outside the event chain.
Therefore, the statement such as self.last_event = evt should be avoided. Otherwise, it will refer to the dead c++ object, and the program will crash easily. If you need event information, you need to save them as primitve types such as int, float, etc.

To avoid this, you can change it as follows.

-           self.last_event = evt
+           self.last_event = evt.__class__(evt)

This calls the implicit copy constructor, but it’s tricky and undocumented. I’m not sure it will work in future versions.

So, my questions are,

  • Is this issue likely to be improved in a future version?
  • Or should it be stated in wx.Event doc?
  • Can the above ‘tricky’ method be used in future versions?

Kind Regards