1 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
2 # See LICENSE for details.
4 # The referred licence file contains:
6 #Copyright (c) 2001-2010
15 #Christopher Armstrong
20 #Itamar Shtull-Trauring
33 #Massachusetts Institute of Technology
39 #Software Freedom Conservancy
45 #Permission is hereby granted, free of charge, to any person obtaining
46 #a copy of this software and associated documentation files (the
47 #"Software"), to deal in the Software without restriction, including
48 #without limitation the rights to use, copy, modify, merge, publish,
49 #distribute, sublicense, and/or sell copies of the Software, and to
50 #permit persons to whom the Software is furnished to do so, subject to
51 #the following conditions:
53 #The above copyright notice and this permission notice shall be
54 #included in all copies or substantial portions of the Software.
56 #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
57 #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
58 #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
59 #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
60 #LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
61 #OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
62 #WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
65 This module provides support for Twisted to be driven by the Qt mainloop.
67 In order to use this support, simply do the following::
68 | app = QApplication(sys.argv) # your code to init Qt
70 | qt4reactor.install()
74 | from twisted.application import reactors
75 | reactors.installReactor('qt4')
77 Then use twisted.internet APIs as usual. The other methods here are not
78 intended to be called directly.
80 If you don't instantiate a QApplication or QCoreApplication prior to
81 installing the reactor, a QCoreApplication will be constructed
82 by the reactor. QCoreApplication does not require a GUI so trial testing
85 Twisted can be initialized after QApplication.exec_() with a call to
86 reactor.runReturn(). calling reactor.stop() will unhook twisted but
87 leave your Qt application running
91 Maintainer: U{Glenn H Tarbox, PhD<mailto:glenn@tarbox.org>}
93 Previous maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
94 Original port to QT4: U{Gabe Rudy<mailto:rudy@goldenhelix.com>}
95 Subsequent port by therve
104 from zope.interface import implements
106 print('+++ Python Zope interface module is required\n')
111 from OVEStandard import globalForcePySide
112 if globalForcePySide: raise Exception()
113 from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
114 from PyQt4.QtCore import QEventLoop
116 from PySide.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
117 from PySide.QtCore import QEventLoop
120 from twisted.internet.interfaces import IReactorFDSet
121 from twisted.python import log
122 from twisted.internet.posixbase import PosixReactorBase
124 print('+++ Python Twisted Conch module is required\n')
127 class TwistedSocketNotifier(QSocketNotifier):
129 Connection between an fd event and reader/writer callbacks.
132 def __init__(self, reactor, watcher, type):
133 QSocketNotifier.__init__(self, watcher.fileno(), type)
134 self.reactor = reactor
135 self.watcher = watcher
137 if type == QSocketNotifier.Read:
139 elif type == QSocketNotifier.Write:
141 QObject.connect(self, SIGNAL("activated(int)"), self.fn)
145 QObject.disconnect(self, SIGNAL("activated(int)"), self.fn)
146 self.setEnabled(False)
147 self.fn = self.watcher = None
151 def read(self, sock):
153 #self.setEnabled(False) # ??? do I need this?
160 why = sys.exc_info()[1]
162 self.reactor._disconnectSelectable(w, why, True)
165 #self.setEnabled(True)
166 log.callWithLogger(w, _read)
167 self.reactor.reactorInvocation()
169 def write(self, sock):
171 self.setEnabled(False)
178 why = sys.exc_info()[1]
180 self.reactor._disconnectSelectable(w, why, False)
182 self.setEnabled(True)
183 log.callWithLogger(w, _write)
184 self.reactor.reactorInvocation()
186 class fakeApplication(QEventLoop):
188 QEventLoop.__init__(self)
191 QEventLoop.exec_(self)
193 class QTReactor(PosixReactorBase):
197 implements(IReactorFDSet)
205 self._timer.setSingleShot(True)
206 if QCoreApplication.startingUp():
207 self.qApp=QCoreApplication([])
210 self.qApp = QCoreApplication.instance()
212 self._blockApp = None
215 """ some debugging instrumentation """
216 self._doSomethingCount=0
218 PosixReactorBase.__init__(self)
220 def addReader(self, reader):
221 if not reader in self._reads:
222 self._reads[reader] = TwistedSocketNotifier(self, reader,
223 QSocketNotifier.Read)
226 def addWriter(self, writer):
227 if not writer in self._writes:
228 self._writes[writer] = TwistedSocketNotifier(self, writer,
229 QSocketNotifier.Write)
232 def removeReader(self, reader):
233 if reader in self._reads:
234 #self._reads[reader].shutdown()
235 #del self._reads[reader]
236 self._reads.pop(reader).shutdown()
238 def removeWriter(self, writer):
239 if writer in self._writes:
240 self._writes[writer].shutdown()
241 #del self._writes[writer]
242 self._writes.pop(writer)
246 return self._removeAll(self._reads, self._writes)
249 def getReaders(self):
250 return self._reads.keys()
253 def getWriters(self):
254 return self._writes.keys()
256 def callLater(self,howlong, *args, **kargs):
257 rval = super(QTReactor,self).callLater(howlong, *args, **kargs)
258 self.reactorInvocation()
262 super(QTReactor,self).crash()
264 def iterate(self,delay=0.0):
265 t=self.running # not sure I entirely get the state of running
267 self._timer.stop() # in case its not (rare?)
270 self.reactorInvokePrivate()
271 self._timer.stop() # supports multiple invocations
273 endTime = delay + time.time()
274 self.reactorInvokePrivate()
276 t = endTime - time.time()
278 self.qApp.processEvents(QEventLoop.AllEvents |
279 QEventLoop.WaitForMoreEvents,t*1010)
283 def addReadWrite(self,t):
284 self._readWriteQ.append(t)
286 def runReturn(self, installSignalHandlers=True):
287 QObject.connect(self._timer, SIGNAL("timeout()"),
288 self.reactorInvokePrivate)
289 self.startRunning(installSignalHandlers=installSignalHandlers)
292 def run(self, installSignalHandlers=True):
295 self._blockApp=self.qApp
297 self._blockApp = fakeApplication()
298 self.runReturn(installSignalHandlers)
299 self._blockApp.exec_()
301 self._timer.stop() # should already be stopped
303 def reactorInvocation(self):
304 self._timer.setInterval(0)
306 def reactorInvokePrivate(self):
308 if self._blockApp is None:
309 # Andy's fix for Ctrl-C quit
312 self._blockApp.quit()
313 self._doSomethingCount += 1
314 self.runUntilCurrent()
318 self._timer.setInterval(int(t*1010))
319 self.qApp.processEvents() # could change interval
322 def doIteration(self):
323 assert False, "doiteration is invalid call"
327 Configure the twisted mainloop to be run inside the qt mainloop.
329 from twisted.internet import main
330 reactor = QTReactor()
331 main.installReactor(reactor)