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
25
Maintainer: Itamar Shtull-Trauring
30
from wx import PySimpleApp as wxPySimpleApp, CallAfter as wxCallAfter, \
33
# older version of wxPython:
34
from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer
36
from twisted.python import log, runtime
37
from twisted.internet import _threadedselect
40
class ProcessEventsTimer(wxTimer):
42
Timer that tells wx to process pending events.
44
This is necessary on OS X, probably due to a bug in wx, if we want
45
wxCallAfters to be handled when modal dialogs, menus, etc. are open.
47
def __init__(self, wxapp):
48
wxTimer.__init__(self)
54
Called repeatedly by wx event loop.
56
self.wxapp.ProcessPendingEvents()
60
class WxReactor(_threadedselect.ThreadedSelectReactor):
64
wxPython drives the event loop, select() runs in a thread.
69
def registerWxApp(self, wxapp):
71
Register wxApp instance with the reactor.
75
def _installSignalHandlersAgain(self):
77
wx sometimes removes our own signal handlers, so re-add them.
80
# make _handleSignals happy:
82
signal.signal(signal.SIGINT, signal.default_int_handler)
94
_threadedselect.ThreadedSelectReactor.stop(self)
96
def _runInMainThread(self, f):
98
Schedule function to run in main wx/Twisted thread.
100
Called by the select() thread.
102
if hasattr(self, "wxapp"):
105
# wx shutdown but twisted hasn't
106
self._postQueue.put(f)
110
Stop the wx event loop if it hasn't already been stopped.
112
Called during Twisted event loop shutdown.
114
if hasattr(self, "wxapp"):
115
self.wxapp.ExitMainLoop()
117
def run(self, installSignalHandlers=True):
121
self._postQueue = Queue.Queue()
122
if not hasattr(self, "wxapp"):
123
log.msg("registerWxApp() was not called on reactor, "
124
"registering my own wxApp instance.")
125
self.registerWxApp(wxPySimpleApp())
127
# start select() thread:
128
self.interleave(self._runInMainThread,
129
installSignalHandlers=installSignalHandlers)
130
if installSignalHandlers:
131
self.callLater(0, self._installSignalHandlersAgain)
133
# add cleanup events:
134
self.addSystemEventTrigger("after", "shutdown", self._stopWx)
135
self.addSystemEventTrigger("after", "shutdown",
136
lambda: self._postQueue.put(None))
138
# On Mac OS X, work around wx bug by starting timer to ensure
139
# wxCallAfter calls are always processed. We don't wake up as
140
# often as we could since that uses too much CPU.
141
if runtime.platform.isMacOSX():
142
t = ProcessEventsTimer(self.wxapp)
143
t.Start(2) # wake up every 2ms
145
self.wxapp.MainLoop()
149
if not self._stopping:
150
# wx event loop exited without reactor.stop() being
151
# called. At this point events from select() thread will
152
# be added to _postQueue, but some may still be waiting
153
# unprocessed in wx, thus the ProcessPendingEvents()
156
wxapp.ProcessPendingEvents() # deal with any queued wxCallAfters
159
f = self._postQueue.get(timeout=0.01)
173
Configure the twisted mainloop to be run inside the wxPython mainloop.
175
reactor = WxReactor()
176
from twisted.internet.main import installReactor
177
installReactor(reactor)
181
__all__ = ['install']