comparison lib/qt4reactor.py @ 463:60b49f6c2027

start porting lightsim to qt
author drewp@bigasterisk.com
date Mon, 01 Sep 2008 00:41:29 +0000
parents
children
comparison
equal deleted inserted replaced
462:cd4d7b878550 463:60b49f6c2027
1 # http://twistedmatrix.com/trac/browser/sandbox/therve/qt4reactor.py
2 # with some fixes by drewp
3
4 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
5 # See LICENSE for details.
6
7
8 """
9 This module provides support for Twisted to interact with the PyQt mainloop.
10
11 In order to use this support, simply do the following::
12
13 | import qt4reactor
14 | qt4reactor.install()
15
16 Then use twisted.internet APIs as usual. The other methods here are not
17 intended to be called directly.
18
19 API Stability: stable
20
21 Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
22 Port to QT4: U{Gabe Rudy<mailto:rudy@goldenhelix.com>}
23 """
24
25 __all__ = ['install']
26
27
28 import sys
29
30 from zope.interface import implements
31
32 from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer
33 from PyQt4.QtGui import QApplication
34
35 from twisted.internet.interfaces import IReactorFDSet
36 from twisted.python import log
37 from twisted.internet.posixbase import PosixReactorBase
38
39
40
41 class TwistedSocketNotifier(QSocketNotifier):
42 """
43 Connection between an fd event and reader/writer callbacks.
44 """
45
46 def __init__(self, reactor, watcher, type):
47 QSocketNotifier.__init__(self, watcher.fileno(), type)
48 self.reactor = reactor
49 self.watcher = watcher
50 self.fn = None
51 if type == QSocketNotifier.Read:
52 self.fn = self.read
53 elif type == QSocketNotifier.Write:
54 self.fn = self.write
55 QObject.connect(self, SIGNAL("activated(int)"), self.fn)
56
57
58 def shutdown(self):
59 QObject.disconnect(self, SIGNAL("activated(int)"), self.fn)
60 self.setEnabled(0)
61 self.fn = self.watcher = None
62
63
64 def read(self, sock):
65 w = self.watcher
66 def _read():
67 why = None
68 try:
69 why = w.doRead()
70 except:
71 log.err()
72 why = sys.exc_info()[1]
73 if why:
74 self.reactor._disconnectSelectable(w, why, True)
75 log.callWithLogger(w, _read)
76 self.reactor.simulate()
77
78
79 def write(self, sock):
80 w = self.watcher
81 def _write():
82 why = None
83 self.setEnabled(0)
84 try:
85 why = w.doWrite()
86 except:
87 log.err()
88 why = sys.exc_info()[1]
89 if why:
90 self.reactor._disconnectSelectable(w, why, False)
91 elif self.watcher:
92 self.setEnabled(1)
93 log.callWithLogger(w, _write)
94 self.reactor.simulate()
95
96
97
98 class QTReactor(PosixReactorBase):
99 """
100 Qt based reactor.
101 """
102 implements(IReactorFDSet)
103
104 # Reference to a DelayedCall for self.crash() when the reactor is
105 # entered through .iterate()
106 _crashCall = None
107
108 _timer = None
109
110 def __init__(self, app=None):
111 self._reads = {}
112 self._writes = {}
113 if app is None:
114 app = QApplication([])
115 self.qApp = app
116 PosixReactorBase.__init__(self)
117 self.addSystemEventTrigger('after', 'shutdown', self.cleanup)
118
119
120
121 def addReader(self, reader):
122 if not reader in self._reads:
123 self._reads[reader] = TwistedSocketNotifier(self, reader,
124 QSocketNotifier.Read)
125
126
127 def addWriter(self, writer):
128 if not writer in self._writes:
129 self._writes[writer] = TwistedSocketNotifier(self, writer,
130 QSocketNotifier.Write)
131
132
133 def removeReader(self, reader):
134 if reader in self._reads:
135 self._reads[reader].shutdown()
136 del self._reads[reader]
137
138
139 def removeWriter(self, writer):
140 if writer in self._writes:
141 self._writes[writer].shutdown()
142 del self._writes[writer]
143
144
145 def removeAll(self):
146 return self._removeAll(self._reads, self._writes)
147
148
149 def getReaders(self):
150 return self._reads.keys()
151
152
153 def getWriters(self):
154 return self._writes.keys()
155
156
157 def simulate(self):
158 self._lastTimer = self._timer # put off the __del__
159 if self._timer is not None:
160 self._timer.stop()
161 self._timer = None
162
163 if not self.running:
164 self.qApp.exit()
165 return
166 self.runUntilCurrent()
167
168 if self._crashCall is not None:
169 self._crashCall.reset(0)
170
171 timeout = self.timeout()
172 if timeout is None:
173 timeout = 1.0
174 timeout = min(timeout, 0.01) * 1010
175
176 if self._timer is None:
177 self._timer = QTimer()
178 self._timer.setObjectName("simulateTimer")
179 QObject.connect(self._timer, SIGNAL("timeout()"), self.simulate)
180 self._timer.start(timeout)
181
182 def cleanup(self):
183 if self._timer is not None:
184 self._timer.stop()
185 self._timer = None
186
187
188 def iterate(self, delay=0.0):
189 self._crashCall = self.callLater(delay, self._crash)
190 self.run()
191
192
193 def mainLoop(self):
194 self.simulate()
195 self.qApp.exec_()
196
197
198 def _crash(self):
199 if self._crashCall is not None:
200 if self._crashCall.active():
201 self._crashCall.cancel()
202 self._crashCall = None
203 self.running = False
204
205
206
207 def install(app=None):
208 """
209 Configure the twisted mainloop to be run inside the qt mainloop.
210 """
211 from twisted.internet import main
212 reactor = QTReactor(app=app)
213 main.installReactor(reactor)