[wxPython-users] calling wx.CallAfter after wx.Frame.Destroy
Aaron Brady
castironpi at comcast.net
Mon Jan 21 20:46:05 PST 2008
> >>> Will MainLoop exit if other events are added to the event queue after
> or
> >>> during delete, or will it address those first? IOW in other words,
> when
> >>> does it return?
> >> IIRC, it's a little different depending on how the platform specific
> >> event loops are handled, but in general when a TLW is finally destroyed
> >> wx checks if is was the last one in the top level window list, if so
> >> then it sets a flag that is checked in the next iteration of the main
> >> loop.
> >>
> >>> If one calls CallAfter while or after the pending delete queue is
> being
> >>> processed, are further references are invalid?
> >>
> >> The order of execution is essentially something like this pseudo-code:
> >>
> >> while not exitFlag:
> >> while native events waiting:
> >> get next native event
> >> process native event
> >>
> >> process posted events
> >> send idle event
> >> delete destroyed TLWs
> >>
> >>
> >> The wx.CallAfter's are implemented by posting events, so they are
> called
> >> before the pending delete queue is checked. So yes it is possible that
> >> a TLW or widget used by a CallAfter function is not valid at the time
> >> the function is called, however if that was the last TLW and the main
> >> loop is exiting then it is probably not likely.
> >
> > Fine. Where is exitFlag set?
>
> In the top-level window destructor if that TLW is the last one. Or from
> the wx.App's ExitMainLoop method.
>
> >> If in doubt it is easy
> >> to check. When a C++ widget object is deleted and it knows what it's
> >> Python proxy object is, andif there is more than one reference to that
> >> proxy then wxPython will replace the __class__ of that proxy object
> with
> >> one that raises an exception if you try to evaluate any of it's
> >> attributes (the PyDeadObjectError exception) and it also has a
> >> __nonzero__ method that returns False. That means that you can use a
> >> simple if test to see if the widget is still alive, like this:
> >>
> >> def MyCallAfterFunction(frame):
> >> if frame:
> >> frame.DoSomething()
> >
> > It's only if it might be running in a second thread that awry might it
> go;
> > if between 'if frame' and 'frame.What', MainLoop destroys, or worse,
> begins
> > to destroy frame.
> >
> > (*) def MyCallAfterFunction(frame):
> > if frame:
> > <thread turnover to MainLoop destroys frame>
> > frame.DoSomething()
>
> But the point is that if you are calling this function via wx.CallAfter
> then it is impossible for the frame to be destroyed after the if and
> before the DoSomething(), because this is already executing in the GUI
> thread via a posted event, and so it is impossible[1] for this thread to
> jump back into the main loop and do the final destruction of the frame
> until after this function has returned.
However, if you block on MyCallAfterFunction's turn in the event queue, a
previously posted and sunk event, which blocks on the same synchronization
object, halts the queue, deadlocking it.
(*) def MyCallAfterFunction(frame):
if frame:
return frame.GetSomething()
> [1] Except, of course, if you use one of the wx Yield functions here,
> which is one of the reasons that it is recommended that they not be used
> unless absolutely necessary.
More information about the wxpython-users
mailing list