Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / internet / wxreactor.py
1 # Copyright (c) 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
26 import Queue
27 try:
28     from wx import PySimpleApp as wxPySimpleApp, CallAfter as wxCallAfter, \
29          Timer as wxTimer
30 except ImportError:
31     # older version of wxPython:
32     from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer
33
34 from twisted.python import log, runtime
35 from twisted.internet import _threadedselect
36
37
38 class ProcessEventsTimer(wxTimer):
39     """
40     Timer that tells wx to process pending events.
41
42     This is necessary on OS X, probably due to a bug in wx, if we want
43     wxCallAfters to be handled when modal dialogs, menus, etc.  are open.
44     """
45     def __init__(self, wxapp):
46         wxTimer.__init__(self)
47         self.wxapp = wxapp
48     
49
50     def Notify(self):
51         """
52         Called repeatedly by wx event loop.
53         """
54         self.wxapp.ProcessPendingEvents()
55
56
57
58 class WxReactor(_threadedselect.ThreadedSelectReactor):
59     """
60     wxPython reactor.
61
62     wxPython drives the event loop, select() runs in a thread.
63     """
64
65     _stopping = False
66
67     def registerWxApp(self, wxapp):
68         """
69         Register wxApp instance with the reactor.
70         """
71         self.wxapp = wxapp
72
73
74     def _installSignalHandlersAgain(self):
75         """
76         wx sometimes removes our own signal handlers, so re-add them.
77         """
78         try:
79             # make _handleSignals happy:
80             import signal
81             signal.signal(signal.SIGINT, signal.default_int_handler)
82         except ImportError:
83             return
84         self._handleSignals()
85
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
97     def _runInMainThread(self, f):
98         """
99         Schedule function to run in main wx/Twisted thread.
100
101         Called by the select() thread.
102         """
103         if hasattr(self, "wxapp"):
104             wxCallAfter(f)
105         else:
106             # wx shutdown but twisted hasn't
107             self._postQueue.put(f)
108
109
110     def _stopWx(self):
111         """
112         Stop the wx event loop if it hasn't already been stopped.
113
114         Called during Twisted event loop shutdown.
115         """
116         if hasattr(self, "wxapp"):
117             self.wxapp.ExitMainLoop()
118
119
120     def run(self, installSignalHandlers=True):
121         """
122         Start the reactor.
123         """
124         self._postQueue = Queue.Queue()
125         if not hasattr(self, "wxapp"):
126             log.msg("registerWxApp() was not called on reactor, "
127                     "registering my own wxApp instance.")
128             self.registerWxApp(wxPySimpleApp())
129
130         # start select() thread:
131         self.interleave(self._runInMainThread,
132                         installSignalHandlers=installSignalHandlers)
133         if installSignalHandlers:
134             self.callLater(0, self._installSignalHandlersAgain)
135
136         # add cleanup events:
137         self.addSystemEventTrigger("after", "shutdown", self._stopWx)
138         self.addSystemEventTrigger("after", "shutdown",
139                                    lambda: self._postQueue.put(None))
140
141         # On Mac OS X, work around wx bug by starting timer to ensure
142         # wxCallAfter calls are always processed. We don't wake up as
143         # often as we could since that uses too much CPU.
144         if runtime.platform.isMacOSX():
145             t = ProcessEventsTimer(self.wxapp)
146             t.Start(2) # wake up every 2ms
147
148         self.wxapp.MainLoop()
149         wxapp = self.wxapp
150         del self.wxapp
151
152         if not self._stopping:
153             # wx event loop exited without reactor.stop() being
154             # called.  At this point events from select() thread will
155             # be added to _postQueue, but some may still be waiting
156             # unprocessed in wx, thus the ProcessPendingEvents()
157             # below.
158             self.stop()
159             wxapp.ProcessPendingEvents() # deal with any queued wxCallAfters
160             while 1:
161                 try:
162                     f = self._postQueue.get(timeout=0.01)
163                 except Queue.Empty:
164                     continue
165                 else:
166                     if f is None:
167                         break
168                     try:
169                         f()
170                     except:
171                         log.err()
172
173
174 def install():
175     """
176     Configure the twisted mainloop to be run inside the wxPython mainloop.
177     """
178     reactor = WxReactor()
179     from twisted.internet.main import installReactor
180     installReactor(reactor)
181     return reactor
182
183
184 __all__ = ['install']