~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/internet/wxreactor.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
1
# Copyright (c) 2001-2006 Twisted Matrix Laboratories.
2
2
# See LICENSE for details.
3
3
 
4
 
 
5
4
"""
6
 
This module provides support for Twisted to interact with the wxPython.
 
5
This module provides wxPython event loop support for Twisted.
7
6
 
8
7
In order to use this support, simply do the following::
9
8
 
17
16
    | reactor.run()
18
17
 
19
18
Then use twisted.internet APIs as usual. Stop the event loop using
20
 
reactor.stop().
21
 
 
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
25
 
 
26
 
 
27
 
API Stability: unstable
 
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
28
26
 
29
27
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
30
28
"""
31
29
 
32
 
import time
33
 
from twisted.python.runtime import seconds
34
 
from twisted.python import log
35
 
from twisted.internet import threadedselectreactor
36
 
 
37
 
from wxPython.wx import wxApp, wxCallAfter, wxEventLoop, wxFrame, NULL
38
 
 
39
 
 
40
 
class DummyApp(wxApp):
41
 
    
42
 
    def OnInit(self):
43
 
        return True
44
 
 
45
 
 
46
 
class WxReactor(threadedselectreactor.ThreadedSelectReactor):
47
 
    """wxPython reactor.
48
 
 
49
 
    wx drives the event loop, and calls Twisted every millisecond, and
50
 
    Twisted then iterates until a ms has passed.
51
 
    """
52
 
 
53
 
    stopping = False
54
 
    
 
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
 
55
71
    def registerWxApp(self, wxapp):
56
 
        """Register wxApp instance with the reactor."""
57
 
        self.wxapp = wxapp                    
58
 
 
59
 
    def crash(self):
60
 
        threadedselectreactor.ThreadedSelectReactor.crash(self)
61
 
        if hasattr(self, "wxapp"):
62
 
            self.wxapp.ExitMainLoop()
 
72
        """
 
73
        Register wxApp instance with the reactor.
 
74
        """
 
75
        self.wxapp = wxapp
63
76
 
64
77
    def _installSignalHandlersAgain(self):
65
 
        # stupid wx removes our own signal handlers, so re-add them
 
78
        """
 
79
        wx sometimes removes our own signal handlers, so re-add them.
 
80
        """
66
81
        try:
 
82
            # make _handleSignals happy:
67
83
            import signal
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:
70
86
            return
71
87
        self._handleSignals()
72
88
 
73
89
    def stop(self):
74
 
        if self.stopping:
 
90
        """
 
91
        Stop the reactor.
 
92
        """
 
93
        if self._stopping:
75
94
            return
76
 
        self.stopping = True
77
 
        threadedselectreactor.ThreadedSelectReactor.stop(self)
78
 
    
79
 
    def run(self, installSignalHandlers=1):
 
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()
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())
 
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
        
86
147
        self.wxapp.MainLoop()
87
 
        
88
 
        if not self.stopping: # wx exited without reactor.stop(), bah
 
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.
89
157
            self.stop()
90
 
 
91
 
        # temporary event loop for dealing with shutdown events:
92
 
        ev = wxEventLoop()
93
 
        wxEventLoop.SetActive(ev)
94
 
        while self.workerThread:
95
 
            while ev.Pending():
96
 
                ev.Dispatch()
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
 
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()
99
171
 
100
172
 
101
173
def install():
102
 
    """Configure the twisted mainloop to be run inside the wxPython mainloop.
 
174
    """
 
175
    Configure the twisted mainloop to be run inside the wxPython mainloop.
103
176
    """
104
177
    reactor = WxReactor()
105
178
    from twisted.internet.main import installReactor