~landscape/zope3/newer-from-ztk

« back to all changes in this revision

Viewing changes to src/twisted/internet/wxreactor.py

  • Committer: Thomas Hervé
  • Date: 2009-07-08 13:52:04 UTC
  • Revision ID: thomas@canonical.com-20090708135204-df5eesrthifpylf8
Remove twisted copy

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
API Stability: stable
26
 
 
27
 
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
28
 
"""
29
 
 
30
 
import Queue
31
 
try:
32
 
    from wx import PySimpleApp as wxPySimpleApp, CallAfter as wxCallAfter, \
33
 
         Timer as wxTimer
34
 
except ImportError:
35
 
    # older version of wxPython:
36
 
    from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer
37
 
 
38
 
from twisted.python import log, runtime
39
 
from twisted.internet import _threadedselect
40
 
 
41
 
 
42
 
class ProcessEventsTimer(wxTimer):
43
 
    """
44
 
    Timer that tells wx to process pending events.
45
 
 
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.
48
 
    """
49
 
    def __init__(self, wxapp):
50
 
        wxTimer.__init__(self)
51
 
        self.wxapp = wxapp
52
 
    
53
 
 
54
 
    def Notify(self):
55
 
        """
56
 
        Called repeatedly by wx event loop.
57
 
        """
58
 
        self.wxapp.ProcessPendingEvents()
59
 
 
60
 
 
61
 
 
62
 
class WxReactor(_threadedselect.ThreadedSelectReactor):
63
 
    """
64
 
    wxPython reactor.
65
 
 
66
 
    wxPython drives the event loop, select() runs in a thread.
67
 
    """
68
 
 
69
 
    _stopping = False
70
 
 
71
 
    def registerWxApp(self, wxapp):
72
 
        """
73
 
        Register wxApp instance with the reactor.
74
 
        """
75
 
        self.wxapp = wxapp
76
 
 
77
 
    def _installSignalHandlersAgain(self):
78
 
        """
79
 
        wx sometimes removes our own signal handlers, so re-add them.
80
 
        """
81
 
        try:
82
 
            # make _handleSignals happy:
83
 
            import signal
84
 
            signal.signal(signal.SIGINT, signal.default_int_handler)
85
 
        except ImportError:
86
 
            return
87
 
        self._handleSignals()
88
 
 
89
 
    def stop(self):
90
 
        """
91
 
        Stop the reactor.
92
 
        """
93
 
        if self._stopping:
94
 
            return
95
 
        self._stopping = True
96
 
        _threadedselect.ThreadedSelectReactor.stop(self)
97
 
 
98
 
    def _runInMainThread(self, f):
99
 
        """
100
 
        Schedule function to run in main wx/Twisted thread.
101
 
 
102
 
        Called by the select() thread.
103
 
        """
104
 
        if hasattr(self, "wxapp"):
105
 
            wxCallAfter(f)
106
 
        else:
107
 
            # wx shutdown but twisted hasn't
108
 
            self._postQueue.put(f)
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
 
    def run(self, installSignalHandlers=True):
120
 
        """
121
 
        Start the reactor.
122
 
        """
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())
128
 
 
129
 
        # start select() thread:
130
 
        self.interleave(self._runInMainThread,
131
 
                        installSignalHandlers=installSignalHandlers)
132
 
        if installSignalHandlers:
133
 
            self.callLater(0, self._installSignalHandlersAgain)
134
 
 
135
 
        # add cleanup events:
136
 
        self.addSystemEventTrigger("after", "shutdown", self._stopWx)
137
 
        self.addSystemEventTrigger("after", "shutdown",
138
 
                                   lambda: self._postQueue.put(None))
139
 
 
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
146
 
        
147
 
        self.wxapp.MainLoop()
148
 
        wxapp = self.wxapp
149
 
        del self.wxapp
150
 
 
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()
156
 
            # below.
157
 
            self.stop()
158
 
            wxapp.ProcessPendingEvents() # deal with any queued wxCallAfters
159
 
            while 1:
160
 
                try:
161
 
                    f = self._postQueue.get(timeout=0.01)
162
 
                except Queue.Empty:
163
 
                    continue
164
 
                else:
165
 
                    if f is None:
166
 
                        break
167
 
                    try:
168
 
                        f()
169
 
                    except:
170
 
                        log.err()
171
 
 
172
 
 
173
 
def install():
174
 
    """
175
 
    Configure the twisted mainloop to be run inside the wxPython mainloop.
176
 
    """
177
 
    reactor = WxReactor()
178
 
    from twisted.internet.main import installReactor
179
 
    installReactor(reactor)
180
 
    return reactor
181
 
 
182
 
 
183
 
__all__ = ['install']