1
# Copyright (c) 2001-2006 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
This module provides wxPython event loop support for Twisted.
7
In order to use this support, simply do the following::
9
| from twisted.internet import wxreactor
12
Then, when your root wxApp has been created::
14
| from twisted.internet import reactor
15
| reactor.registerWxApp(yourApp)
18
Then use twisted.internet APIs as usual. Stop the event loop using
19
reactor.stop(), not yourApp.ExitMainLoop().
21
IMPORTANT: tests will fail when run under this reactor. This is
22
expected and probably does not reflect on the reactor's ability to run
27
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
32
from wx import PySimpleApp as wxPySimpleApp, CallAfter as wxCallAfter, \
35
# older version of wxPython:
36
from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer
38
from twisted.python import log, runtime
39
from twisted.internet import _threadedselect
42
class ProcessEventsTimer(wxTimer):
44
Timer that tells wx to process pending events.
46
This is necessary on OS X, probably due to a bug in wx, if we want
47
wxCallAfters to be handled when modal dialogs, menus, etc. are open.
49
def __init__(self, wxapp):
50
wxTimer.__init__(self)
56
Called repeatedly by wx event loop.
58
self.wxapp.ProcessPendingEvents()
62
class WxReactor(_threadedselect.ThreadedSelectReactor):
66
wxPython drives the event loop, select() runs in a thread.
71
def registerWxApp(self, wxapp):
73
Register wxApp instance with the reactor.
77
def _installSignalHandlersAgain(self):
79
wx sometimes removes our own signal handlers, so re-add them.
82
# make _handleSignals happy:
84
signal.signal(signal.SIGINT, signal.default_int_handler)
96
_threadedselect.ThreadedSelectReactor.stop(self)
98
def _runInMainThread(self, f):
100
Schedule function to run in main wx/Twisted thread.
102
Called by the select() thread.
104
if hasattr(self, "wxapp"):
107
# wx shutdown but twisted hasn't
108
self._postQueue.put(f)
112
Stop the wx event loop if it hasn't already been stopped.
114
Called during Twisted event loop shutdown.
116
if hasattr(self, "wxapp"):
117
self.wxapp.ExitMainLoop()
119
def run(self, installSignalHandlers=True):
123
self._postQueue = Queue.Queue()
124
if not hasattr(self, "wxapp"):
125
log.msg("registerWxApp() was not called on reactor, "
126
"registering my own wxApp instance.")
127
self.registerWxApp(wxPySimpleApp())
129
# start select() thread:
130
self.interleave(self._runInMainThread,
131
installSignalHandlers=installSignalHandlers)
132
if installSignalHandlers:
133
self.callLater(0, self._installSignalHandlersAgain)
135
# add cleanup events:
136
self.addSystemEventTrigger("after", "shutdown", self._stopWx)
137
self.addSystemEventTrigger("after", "shutdown",
138
lambda: self._postQueue.put(None))
140
# On Mac OS X, work around wx bug by starting timer to ensure
141
# wxCallAfter calls are always processed. We don't wake up as
142
# often as we could since that uses too much CPU.
143
if runtime.platform.isMacOSX():
144
t = ProcessEventsTimer(self.wxapp)
145
t.Start(2) # wake up every 2ms
147
self.wxapp.MainLoop()
151
if not self._stopping:
152
# wx event loop exited without reactor.stop() being
153
# called. At this point events from select() thread will
154
# be added to _postQueue, but some may still be waiting
155
# unprocessed in wx, thus the ProcessPendingEvents()
158
wxapp.ProcessPendingEvents() # deal with any queued wxCallAfters
161
f = self._postQueue.get(timeout=0.01)
175
Configure the twisted mainloop to be run inside the wxPython mainloop.
177
reactor = WxReactor()
178
from twisted.internet.main import installReactor
179
installReactor(reactor)
183
__all__ = ['install']