root / trunk / twisted / internet / wxreactor.py

Revision 24441, 5.1 kB (checked in by thijs, 1 year ago)

Merge maintainer-email-2438: Get rid of references to maintainer email addresses from code.

Author: thijs
Reviewer: exarkun
Fixes: #2438

Line 
1 # Copyright (c) 2001-2006 Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 This module provides wxPython event loop support for Twisted.
6
7 In order to use this support, simply do the following::
8
9     |  from twisted.internet import wxreactor
10     |  wxreactor.install()
11
12 Then, when your root wxApp has been created::
13
14     | from twisted.internet import reactor
15     | reactor.registerWxApp(yourApp)
16     | reactor.run()
17
18 Then use twisted.internet APIs as usual. Stop the event loop using
19 reactor.stop(), not yourApp.ExitMainLoop().
20
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
23 real applications.
24
25 Maintainer: Itamar Shtull-Trauring
26 """
27
28 import Queue
29 try:
30     from wx import PySimpleApp as wxPySimpleApp, CallAfter as wxCallAfter, \
31          Timer as wxTimer
32 except ImportError:
33     # older version of wxPython:
34     from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer
35
36 from twisted.python import log, runtime
37 from twisted.internet import _threadedselect
38
39
40 class ProcessEventsTimer(wxTimer):
41     """
42     Timer that tells wx to process pending events.
43
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.
46     """
47     def __init__(self, wxapp):
48         wxTimer.__init__(self)
49         self.wxapp = wxapp
50    
51
52     def Notify(self):
53         """
54         Called repeatedly by wx event loop.
55         """
56         self.wxapp.ProcessPendingEvents()
57
58
59
60 class WxReactor(_threadedselect.ThreadedSelectReactor):
61     """
62     wxPython reactor.
63
64     wxPython drives the event loop, select() runs in a thread.
65     """
66
67     _stopping = False
68
69     def registerWxApp(self, wxapp):
70         """
71         Register wxApp instance with the reactor.
72         """
73         self.wxapp = wxapp
74
75     def _installSignalHandlersAgain(self):
76         """
77         wx sometimes removes our own signal handlers, so re-add them.
78         """
79         try:
80             # make _handleSignals happy:
81             import signal
82             signal.signal(signal.SIGINT, signal.default_int_handler)
83         except ImportError:
84             return
85         self._handleSignals()
86
87     def stop(self):
88         """
89         Stop the reactor.
90         """
91         if self._stopping:
92             return
93         self._stopping = True
94         _threadedselect.ThreadedSelectReactor.stop(self)
95
96     def _runInMainThread(self, f):
97         """
98         Schedule function to run in main wx/Twisted thread.
99
100         Called by the select() thread.
101         """
102         if hasattr(self, "wxapp"):
103             wxCallAfter(f)
104         else:
105             # wx shutdown but twisted hasn't
106             self._postQueue.put(f)
107
108     def _stopWx(self):
109         """
110         Stop the wx event loop if it hasn't already been stopped.
111
112         Called during Twisted event loop shutdown.
113         """
114         if hasattr(self, "wxapp"):
115             self.wxapp.ExitMainLoop()
116
117     def run(self, installSignalHandlers=True):
118         """
119         Start the reactor.
120         """
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())
126
127         # start select() thread:
128         self.interleave(self._runInMainThread,
129                         installSignalHandlers=installSignalHandlers)
130         if installSignalHandlers:
131             self.callLater(0, self._installSignalHandlersAgain)
132
133         # add cleanup events:
134         self.addSystemEventTrigger("after", "shutdown", self._stopWx)
135         self.addSystemEventTrigger("after", "shutdown",
136                                    lambda: self._postQueue.put(None))
137
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
144         
145         self.wxapp.MainLoop()
146         wxapp = self.wxapp
147         del self.wxapp
148
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()
154             # below.
155             self.stop()
156             wxapp.ProcessPendingEvents() # deal with any queued wxCallAfters
157             while 1:
158                 try:
159                     f = self._postQueue.get(timeout=0.01)
160                 except Queue.Empty:
161                     continue
162                 else:
163                     if f is None:
164                         break
165                     try:
166                         f()
167                     except:
168                         log.err()
169
170
171 def install():
172     """
173     Configure the twisted mainloop to be run inside the wxPython mainloop.
174     """
175     reactor = WxReactor()
176     from twisted.internet.main import installReactor
177     installReactor(reactor)
178     return reactor
179
180
181 __all__ = ['install']
Note: See TracBrowser for help on using the browser.