19
18
Then use twisted.internet APIs as usual. Stop the event loop using
22
IMPORTANT: tests will fail when run under this reactor. This is expected
23
and does not reflect on the reactor's ability to run real applications,
24
I think. Talk to me if you have questions. -- itamar
27
API Stability: unstable
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
29
27
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
33
from twisted.python.runtime import seconds
34
from twisted.python import log
35
from twisted.internet import threadedselectreactor
37
from wxPython.wx import wxApp, wxCallAfter, wxEventLoop, wxFrame, NULL
40
class DummyApp(wxApp):
46
class WxReactor(threadedselectreactor.ThreadedSelectReactor):
49
wx drives the event loop, and calls Twisted every millisecond, and
50
Twisted then iterates until a ms has passed.
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.
55
71
def registerWxApp(self, wxapp):
56
"""Register wxApp instance with the reactor."""
60
threadedselectreactor.ThreadedSelectReactor.crash(self)
61
if hasattr(self, "wxapp"):
62
self.wxapp.ExitMainLoop()
73
Register wxApp instance with the reactor.
64
77
def _installSignalHandlersAgain(self):
65
# stupid wx removes our own signal handlers, so re-add them
79
wx sometimes removes our own signal handlers, so re-add them.
82
# make _handleSignals happy:
68
signal.signal(signal.SIGINT, signal.default_int_handler) # make _handleSignals happy
84
signal.signal(signal.SIGINT, signal.default_int_handler)
69
85
except ImportError:
71
87
self._handleSignals()
77
threadedselectreactor.ThreadedSelectReactor.stop(self)
79
def run(self, installSignalHandlers=1):
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()
80
124
if not hasattr(self, "wxapp"):
81
log.msg("registerWxApp() was not called on reactor, this is probably an error.")
82
self.registerWxApp(DummyApp(0))
83
self.startRunning(installSignalHandlers=installSignalHandlers)
84
self.interleave(wxCallAfter)
85
self.callLater(0, self._installSignalHandlersAgain)
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
86
147
self.wxapp.MainLoop()
88
if not self.stopping: # wx exited without reactor.stop(), bah
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()
91
# temporary event loop for dealing with shutdown events:
93
wxEventLoop.SetActive(ev)
94
while self.workerThread:
97
time.sleep(0.0001) # so we don't use 100% CPU, bleh
98
self.wxapp.ProcessIdle()
158
wxapp.ProcessPendingEvents() # deal with any queued wxCallAfters
161
f = self._postQueue.get(timeout=0.01)
102
"""Configure the twisted mainloop to be run inside the wxPython mainloop.
175
Configure the twisted mainloop to be run inside the wxPython mainloop.
104
177
reactor = WxReactor()
105
178
from twisted.internet.main import installReactor