Multiple threads in a GUI app (wxPython), communication between worker thread and app?

John Perks and Sarah Mount johnandsarah at estragon.freeserve.co.uk
Sun May 1 17:44:57 EDT 2005


"fooooo" <phark52 at yahoo.com> wrote in message
news:1114966991.337599.194500 at f14g2000cwb.googlegroups.com...
> This is a network app, written in wxPython and the socket module. This
> is what I want to happen:

I'm not sure if this will help you, but it solved what was, for me, a
more general problem: not (normally) being able to issue wxPython calls
outside the GUI thread.

I came up with a general-purpose thread-switcher, which, given a
callable, would on invocation:
  queue itself up on the GUI event queue
  call its callable in the GUI thread (allowing arbitrary wxPython
calls)
  pass its result back to the calling thread (or re-raise any exception
there).

Instead of having a dedicated queue, it uses one already in place.
Because all calls using it are serialized, it had the beneficial
side-effect (for me, anyway)of avoiding certain concurrency issues.

(The calls to my locks module, CheckPause() and CheckCancel(), were
there so the user could suspend, resume, and cancel worker threads at
will, which the Python threading module does not naturally support(my
locks module held some state that could be set via the GUI.) If you have
no need of that, delete those lines and everything should still work
(they were a late addition).

import wx, threading, types
import locks # my code, see remark above

#-------------------------------------
# decorator used to call a method (or other callable)
# from the wxPython main thread (with appropriate switching)
#--------------------------------------
class wxThreadSwitch(object):
    def __init__(self, callable):
        object.__init__(self)
        self.callable = callable

    def __get__(self, inst, owner=None):
        c = self.callable
        # if c is a descriptor then wrap it around
        # the instance as would have happened normally
        if not isinstance(c, types.InstanceType):
            try:
                get = c.__get__
                args = [inst]
                if owner is not None:
                    args.append(owner)
                return wxThreadSwitch(get(*args))
            except AttributeError:
                pass
        # if we get here, then not a descriptor,
        # so return self unchanged
        return self

    def __call__(self, *args, **kwargs):
        if wx.Thread_IsMain():
            return self.callable(*args, **kwargs)

        locks.CheckPause()
        c = self.__wxThreadCall(self.callable)
        wx.CallAfter(c, *args, **kwargs)
        return c.Result()

    class __wxThreadCall(object):
        def __init__(self, callable):
            assert not wx.Thread_IsMain()
            object.__init__(self)
            self.callable = callable
            self.result = None
            self.exc_info = None
            self.event = threading.Event()

        def __call__(self, *args, **kwargs):
            try:
                try:
                    assert wx.Thread_IsMain()
                    assert not self.event.isSet()
                    locks.CheckCancel()
                    self.result = self.callable(*args, **kwargs)
                except:
                    self.exc_info = sys.exc_info()
            finally:
                self.event.set()

        def Result(self):
            self.event.wait()
            if self.exc_info:
                type, value, traceback = self.exc_info
                raise type, value, traceback
            return self.result


A usage example would be to decorate a function or method with it:

class Something:
    @wxThreadSwitch
    def someGUICallOrOther():
    ....


Here the method call would run via the wxThreadSwitch decorator which
would do any necessary thread switching.

Hope this helps

John





More information about the Python-list mailing list