wx.CallAfter leads to builtins.TypeError: can't pickle PyEvent objects

Today I upgraded my ArchLinux box and now I am on Python 3.6.0. Installed Phoenix from your snapshots and - got an error when calling CallAfter;

wx.CallAfter( self.SetItemCount, UsrEventLog().num_events())

Leads to

builtins.TypeError: can't pickle PyEvent objects.

Relevant part of the stacktrace (copied from Wing IDE excpetion window):

File "/usr/lib/python3.6/site-packages/tau4/wxp/__init__.py", line 233, in _tau4s_on_eventlog_changed_
   wx.CallAfter( self.SetItemCount, UsrEventLog().num_events())
File "/usr/lib/python3.6/site-packages/wx/core.py", line 2966, in CallAfter
   wx.PostEvent(app, evt)
File "/usr/lib/python3.6/site-packages/wx/core.py", line 1557, in _PyEvent_Clone
   clone = copy.copy(self)
File "/usr/lib/python3.6/copy.py", line 96, in copy
   rv = reductor(4)

builtins.TypeError: can't pickle PyEvent objects

I use CallAfter heavily in my software, can't be w/o it.

Any ideas?

Cheers Paul

Don’t know what is UsrEventLog and what type self is.

Try to make a lambda or a callable class and put all arguments and a function call inside it. And use CallAffter with this object. It will be easier to debug in this case!

Actually CallAfter gets called with these params

wx.CallAfter( self.SetItemCount, 42)

SetItemCount is a method of ListCtrl, 42 ist returned by my code.

This code worked under Python 3.5.x and wx Phoenix. It does not under Python 3.6.0 and the latest Phoenix.

The problem seems to lie here (have marked the relevant lines w/ # <===):

def _PyEvent_Clone(self):
     """
     Make a new instance of the event that is a copy of self.

     Through the magic of Python this implementation should work for
     this and all derived classes.
     """
     # Create a new instance
     import copy
     clone = copy.copy(self) # <===
     # and then invoke the C++ copy constructor to copy the C++ bits too.
     wx.PyEvent.__init__(clone, self)
     return clone
PyEvent.Clone = _PyEvent_Clone

This function is called when PostEvent ist called, which seems to clone PyEvents.

What is self? It's a wx.PyEvent:

def CallAfter(callableObj, *args, **kw):
     """
     Call the specified function after the current and pending event
     handlers have been completed. This is also good for making GUI
     method calls from non-GUI threads. Any extra positional or
     keyword args are passed on to the callable when it is called.

     :param PyObject callableObj: the callable object
     :param args: arguments to be passed to the callable object
     :param kw: keywords to be passed to the callable object

     .. seealso::
         :ref:`wx.CallLater`
     """
     assert callable(callableObj), "callableObj is not callable"
     app = wx.GetApp()
     assert app is not None, 'No wx.App created yet'

     if not hasattr(app, "_CallAfterId"):
         app._CallAfterId = wx.NewEventType()
         app.Connect(-1, -1, app._CallAfterId,
                     lambda event: event.callable(*event.args, **event.kw) )
     evt = wx.PyEvent()
     evt.SetEventType(app._CallAfterId)
     evt.callable = callableObj
     evt.args = args
     evt.kw = kw
     wx.PostEvent(app, evt) # <===

Paul

···

On 13/01/17 17:43, Michael Salin wrote:

Don't know what is UsrEventLog and what type self is.

It's definitely not my code, that causes the error. No CallAfter works, be it in my app or in wx.

So the question would be: Any wx-user using Python 3.6 already? Experiencing the same problem (likeley)?

Did a bit of debugging. No idea, why PyEvent shouldn't be picklable under 3.6, though.

Paul

···

On 13/01/17 18:07, poseidon wrote:

On 13/01/17 17:43, Michael Salin wrote:

Don't know what is UsrEventLog and what type self is.

Actually CallAfter gets called with these params

wx.CallAfter( self.SetItemCount, 42)

SetItemCount is a method of ListCtrl, 42 ist returned by my code.

This code worked under Python 3.5.x and wx Phoenix. It does not under
Python 3.6.0 and the latest Phoenix.

The problem seems to lie here (have marked the relevant lines w/ # <===):

def _PyEvent_Clone(self):
    """
    Make a new instance of the event that is a copy of self.

    Through the magic of Python this implementation should work for
    this and all derived classes.
    """
    # Create a new instance
    import copy
    clone = copy.copy(self) # <===
    # and then invoke the C++ copy constructor to copy the C++ bits too.
    wx.PyEvent.__init__(clone, self)
    return clone
PyEvent.Clone = _PyEvent_Clone

This function is called when PostEvent ist called, which seems to clone
PyEvents.

What is self? It's a wx.PyEvent:

def CallAfter(callableObj, *args, **kw):
    """
    Call the specified function after the current and pending event
    handlers have been completed. This is also good for making GUI
    method calls from non-GUI threads. Any extra positional or
    keyword args are passed on to the callable when it is called.

    :param PyObject callableObj: the callable object
    :param args: arguments to be passed to the callable object
    :param kw: keywords to be passed to the callable object

    .. seealso::
        :ref:`wx.CallLater`
    """
    assert callable(callableObj), "callableObj is not callable"
    app = wx.GetApp()
    assert app is not None, 'No wx.App created yet'

    if not hasattr(app, "_CallAfterId"):
        app._CallAfterId = wx.NewEventType()
        app.Connect(-1, -1, app._CallAfterId,
                    lambda event: event.callable(*event.args, **event.kw) )
    evt = wx.PyEvent()
    evt.SetEventType(app._CallAfterId)
    evt.callable = callableObj
    evt.args = args
    evt.kw = kw
    wx.PostEvent(app, evt) # <===

Paul

Worx again after an update of Phoenix today.

Thanx

···

On 13/01/17 15:06, poseidon wrote:

Today I upgraded my ArchLinux box and now I am on Python 3.6.0.
Installed Phoenix from your snapshots and - got an error when calling
CallAfter;

wx.CallAfter( self.SetItemCount, UsrEventLog().num_events())

Leads to

builtins.TypeError: can't pickle PyEvent objects.

Relevant part of the stacktrace (copied from Wing IDE excpetion window):

File "/usr/lib/python3.6/site-packages/tau4/wxp/__init__.py", line 233,
in _tau4s_on_eventlog_changed_
  wx.CallAfter( self.SetItemCount, UsrEventLog().num_events())
File "/usr/lib/python3.6/site-packages/wx/core.py", line 2966, in CallAfter
  wx.PostEvent(app, evt)
File "/usr/lib/python3.6/site-packages/wx/core.py", line 1557, in
_PyEvent_Clone
  clone = copy.copy(self)
File "/usr/lib/python3.6/copy.py", line 96, in copy
  rv = reductor(4)

builtins.TypeError: can't pickle PyEvent objects

I use CallAfter heavily in my software, can't be w/o it.

Any ideas?

Cheers Paul