Ticket #3977: qtreactor-3977.patch

File qtreactor-3977.patch, 14.3 KB (added by michaelnt, 3 years ago)
  • (a) /dev/null vs. (b) doc/core/examples/qtdemo.py

    diff --git doc/core/examples/qtdemo.py doc/core/examples/qtdemo.py
    new file mode 100644
    index 0000000..b16709f
    a b  
     1# Copyright (c) 2001-20011 Twisted Matrix Laboratories. 
     2# See LICENSE for details. 
     3 
     4 
     5""" 
     6Qt demo. 
     7 
     8Fetch a URL's contents and display it in a Webkit window. 
     9""" 
     10 
     11import sys, urlparse 
     12 
     13from PySide import QtGui, QtCore 
     14from PySide.QtWebKit import QWebView 
     15 
     16from twisted.internet import protocol 
     17 
     18app = QtGui.QApplication(sys.argv) 
     19from twisted.internet import qtreactor 
     20qtreactor.install() 
     21 
     22# The reactor must be installed before this import 
     23from twisted.web import http 
     24 
     25 
     26class TwistzillaClient(http.HTTPClient): 
     27    def __init__(self, web, urls): 
     28        self.urls = urls 
     29        self.web = web 
     30 
     31    def connectionMade(self): 
     32        self.sendCommand('GET', self.urls[2]) 
     33        self.sendHeader('Host', '%s:%d' % (self.urls[0], self.urls[1]) ) 
     34        self.sendHeader('User-Agent', 'Twistzilla') 
     35        self.endHeaders() 
     36 
     37    def handleResponse(self, data): 
     38        self.web.setHtml(data) 
     39 
     40 
     41class TwistzillaWindow(QtGui.QMainWindow): 
     42    def __init__(self, *args): 
     43        QtGui.QMainWindow.__init__(self, *args) 
     44 
     45        self.centralwidget = QtGui.QWidget(self) 
     46 
     47        vbox = QtGui.QVBoxLayout(self.centralwidget) 
     48 
     49        hbox = QtGui.QHBoxLayout() 
     50        label = QtGui.QLabel("Address: ") 
     51        self.line  = QtGui.QLineEdit("http://www.twistedmatrix.com/") 
     52        self.connect(self.line, QtCore.SIGNAL('returnPressed()'), self.fetchURL) 
     53        hbox.addWidget(label) 
     54        hbox.addWidget(self.line) 
     55 
     56        self.web = QWebView() 
     57 
     58        vbox.addLayout(hbox) 
     59        vbox.addWidget(self.web) 
     60        vbox.setMargin(2) 
     61        vbox.setSpacing(3) 
     62 
     63        self.setCentralWidget(self.centralwidget) 
     64        self.fetchURL() 
     65 
     66    def fetchURL(self): 
     67        u = urlparse.urlparse(str(self.line.text())) 
     68 
     69        pos = u[1].find(':') 
     70 
     71        if pos == -1: 
     72            host, port = u[1], 80 
     73        else: 
     74            host, port = u[1][:pos], int(u[1][pos+1:]) 
     75 
     76        if u[2] == '': 
     77            file = '/' 
     78        else: 
     79            file = u[2] 
     80 
     81        from twisted.internet import reactor 
     82        protocol.ClientCreator(reactor, TwistzillaClient, self.web, (host, port, file)).connectTCP(host, port) 
     83 
     84    def closeEvent(self, event=None): 
     85        from twisted.internet import reactor 
     86        reactor.stop() 
     87 
     88 
     89def main(): 
     90    win = TwistzillaWindow() 
     91    win.show() 
     92 
     93    from twisted.internet import reactor 
     94    sys.exit(reactor.run()) 
     95 
     96if __name__ == '__main__': 
     97    main() 
  • twisted/internet/qtreactor.py

    diff --git twisted/internet/qtreactor.py twisted/internet/qtreactor.py
    index abbd3ba..a235b1f 100644
     
    1 # -*- test-case-name: twisted.internet.test.test_qtreactor -*- 
    2 # Copyright (c) 2001-2009 Twisted Matrix Laboratories. 
     1# Copyright (c) 2001-2011 Twisted Matrix Laboratories. 
    32# See LICENSE for details. 
    43 
     4 
     5""" 
     6This module provides support for Twisted to be driven by the Qt mainloop. 
     7 
     8In order to use this support, simply do the following:: 
     9    |  app = QApplication(sys.argv) # your code to init Qt 
     10    |  import qt4reactor 
     11    |  qt4reactor.install() 
     12     
     13alternatively: 
     14 
     15    |  from twisted.application import reactors 
     16    |  reactors.installReactor('qt4') 
     17 
     18Then use twisted.internet APIs as usual.  The other methods here are not 
     19intended to be called directly. 
     20 
     21If you don't instantiate a QApplication or QCoreApplication prior to 
     22installing the reactor, a QCoreApplication will be constructed 
     23by the reactor.  QCoreApplication does not require a GUI so trial testing 
     24can occur normally. 
     25 
     26Twisted can be initialized after QApplication.exec_() with a call to 
     27reactor.runReturn().  calling reactor.stop() will unhook twisted but 
     28leave your Qt application running 
     29""" 
     30 
     31import sys 
     32from zope.interface import implements 
     33from twisted.internet.interfaces import IReactorFDSet 
     34from twisted.python import log 
     35from twisted.internet import posixbase 
     36 
    537try: 
    6     # 'import qtreactor' would have imported this file instead of the 
    7     # top-level qtreactor. __import__ does the right thing 
    8     # (kids, don't repeat this at home) 
    9     install = __import__('qtreactor').install 
     38    from PySide.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication 
     39    from PySide.QtCore import QEventLoop 
    1040except ImportError: 
    11     from twisted.plugins.twisted_qtstub import errorMessage 
    12     raise ImportError(errorMessage) 
    13 else: 
    14     import warnings 
    15     warnings.warn("Please use qtreactor instead of twisted.internet.qtreactor", 
    16                   category=DeprecationWarning) 
    17  
    18 __all__ = ['install'] 
     41    from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication 
     42    from PyQt4.QtCore import QEventLoop 
     43 
     44 
     45 
     46class TwistedSocketNotifier(QObject): 
     47    """ 
     48    Connection between an fd event and reader/writer callbacks. 
     49    """ 
     50 
     51    def __init__(self, parent, reactor, watcher, socketType): 
     52        QObject.__init__(self, parent) 
     53        self.reactor = reactor 
     54        self.watcher = watcher 
     55        fd = watcher.fileno() 
     56        self.notifier = QSocketNotifier(fd, socketType, parent) 
     57        self.notifier.setEnabled(True) 
     58        if socketType == QSocketNotifier.Read: 
     59            self.fn = self.read 
     60        else: 
     61            self.fn = self.write 
     62        QObject.connect(self.notifier, SIGNAL("activated(int)"), self.fn) 
     63 
     64 
     65    def shutdown(self): 
     66        self.notifier.setEnabled(False) 
     67        self.disconnect(self.notifier, SIGNAL("activated(int)"), self.fn) 
     68        self.fn = self.watcher = None 
     69        self.notifier.deleteLater() 
     70        self.deleteLater() 
     71 
     72 
     73    def read(self, fd): 
     74        if not self.watcher: 
     75            return 
     76        w = self.watcher 
     77        # doRead can cause self.shutdown to be called so keep a reference to self.watcher 
     78        def _read(): 
     79            #Don't call me again, until the data has been read 
     80            self.notifier.setEnabled(False) 
     81            why = None 
     82            try: 
     83                why = w.doRead() 
     84                inRead = True 
     85            except: 
     86                inRead = False 
     87                log.err() 
     88                why = sys.exc_info()[1] 
     89            if why: 
     90                self.reactor._disconnectSelectable(w, why, inRead) 
     91            elif self.watcher: 
     92                self.notifier.setEnabled(True) # Re enable notification following sucessfull read 
     93            self.reactor._iterate(fromqt=True) 
     94        log.callWithLogger(w, _read) 
     95 
     96 
     97    def write(self, sock): 
     98        if not self.watcher: 
     99            return 
     100        w = self.watcher 
     101        def _write(): 
     102            why = None 
     103            self.notifier.setEnabled(False) 
     104             
     105            try: 
     106                why = w.doWrite() 
     107            except: 
     108                log.err() 
     109                why = sys.exc_info()[1] 
     110            if why: 
     111                self.reactor._disconnectSelectable(w, why, False) 
     112            elif self.watcher: 
     113                self.notifier.setEnabled(True) 
     114            self.reactor._iterate(fromqt=True) 
     115        log.callWithLogger(w, _write) 
     116 
     117 
     118 
     119class QtReactor(posixbase.PosixReactorBase): 
     120    implements(IReactorFDSet) 
     121 
     122 
     123    def __init__(self): 
     124        self._reads = {} 
     125        self._writes = {} 
     126        self._notifiers = {} 
     127        self._timer = QTimer() 
     128        self._timer.setSingleShot(True) 
     129        QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate) 
     130 
     131        if QCoreApplication.startingUp(): 
     132            # Application Object has not been started yet 
     133            self.qApp=QCoreApplication([]) 
     134            self._ownApp=True 
     135        else: 
     136            self.qApp = QCoreApplication.instance() 
     137            self._ownApp=False 
     138        self._blockApp = None 
     139        posixbase.PosixReactorBase.__init__(self) 
     140 
     141 
     142    def _add(self, xer, primary, type): 
     143        """ 
     144        Private method for adding a descriptor from the event loop. 
     145 
     146        It takes care of adding it if  new or modifying it if already added 
     147        for another state (read -> read/write for example). 
     148        """ 
     149        if xer not in primary: 
     150            primary[xer] = TwistedSocketNotifier(None, self, xer, type) 
     151 
     152 
     153    def addReader(self, reader): 
     154        """ 
     155        Add a FileDescriptor for notification of data available to read. 
     156        """ 
     157        self._add(reader, self._reads, QSocketNotifier.Read) 
     158 
     159 
     160    def addWriter(self, writer): 
     161        """ 
     162        Add a FileDescriptor for notification of data available to write. 
     163        """ 
     164        self._add(writer, self._writes, QSocketNotifier.Write) 
     165 
     166 
     167    def _remove(self, xer, primary): 
     168        """ 
     169        Private method for removing a descriptor from the event loop. 
     170 
     171        It does the inverse job of _add, and also add a check in case of the fd 
     172        has gone away. 
     173        """ 
     174        if xer in primary: 
     175            notifier = primary.pop(xer) 
     176            notifier.shutdown() 
     177 
     178         
     179    def removeReader(self, reader): 
     180        """ 
     181        Remove a Selectable for notification of data available to read. 
     182        """ 
     183        self._remove(reader, self._reads) 
     184 
     185 
     186    def removeWriter(self, writer): 
     187        """ 
     188        Remove a Selectable for notification of data available to write. 
     189        """ 
     190        self._remove(writer, self._writes) 
     191 
     192 
     193    def removeAll(self): 
     194        """ 
     195        Remove all selectables, and return a list of them. 
     196        """ 
     197        rv = self._removeAll(self._reads, self._writes) 
     198        return rv 
     199 
     200 
     201    def getReaders(self): 
     202        return self._reads.keys() 
     203 
     204 
     205    def getWriters(self): 
     206        return self._writes.keys() 
     207 
     208 
     209    def callLater(self,howlong, *args, **kargs): 
     210        rval = super(QtReactor,self).callLater(howlong, *args, **kargs) 
     211        self.reactorInvocation() 
     212        return rval 
     213 
     214 
     215    def reactorInvocation(self): 
     216        self._timer.stop() 
     217        self._timer.setInterval(0) 
     218        self._timer.start() 
     219         
     220 
     221    def _iterate(self, delay=None, fromqt=False): 
     222        """ 
     223        See twisted.internet.interfaces.IReactorCore.iterate. 
     224        """ 
     225        self.runUntilCurrent() 
     226        self.doIteration(delay, fromqt) 
     227 
     228 
     229    iterate = _iterate 
     230 
     231 
     232    def doIteration(self, delay=None, fromqt=False): 
     233        """ 
     234        This method is called by a Qt timer or by network activity on 
     235        a file descriptor.  
     236 
     237        If called becuase of network activiy then control should not 
     238        be handed back to Qt as this would cause recursion. 
     239        """ 
     240         
     241        if not self.running and self._blockApp: 
     242            self._blockApp.quit() 
     243 
     244        self._timer.stop() 
     245        delay = max(delay, 1) 
     246        if not fromqt: 
     247            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000) 
     248        if self.timeout() is None: 
     249            timeout = 0.1 
     250        elif self.timeout() == 0: 
     251            timeout = 0 
     252        else: 
     253            timeout = self.timeout() 
     254        self._timer.setInterval(timeout * 1000) 
     255        self._timer.start() 
     256 
     257 
     258    def runReturn(self, installSignalHandlers=True): 
     259        self.startRunning(installSignalHandlers=installSignalHandlers) 
     260        self.reactorInvocation() 
     261 
     262 
     263    def stop(self): 
     264        super(QtReactor, self).stop() 
     265        self.iterate(0) 
     266 
     267 
     268    def run(self, installSignalHandlers=True): 
     269        if self._ownApp: 
     270            self._blockApp = self.qApp 
     271        else: 
     272            self._blockApp = QEventLoop() 
     273        self.runReturn() 
     274        self._blockApp.exec_() 
     275 
     276 
     277 
     278def install(): 
     279    """ 
     280    Install the Qt reactor. 
     281    """ 
     282    p = QtReactor() 
     283    from twisted.internet.main import installReactor 
     284    installReactor(p) 
     285 
     286__all__ = ["install"] 
    19287 
  • twisted/internet/test/reactormixins.py

    diff --git twisted/internet/test/reactormixins.py twisted/internet/test/reactormixins.py
    index 1ba5dbc..0fa10cd 100644
     
    5151                 "twisted.internet.kqreactor.KQueueReactor", 
    5252                 "twisted.internet.win32eventreactor.Win32Reactor", 
    5353                 "twisted.internet.iocpreactor.reactor.IOCPReactor", 
    54                  "twisted.internet.cfreactor.CFReactor"] 
     54                 "twisted.internet.cfreactor.CFReactor", 
     55                 "twisted.internet.qtreactor.QtReactor"] 
    5556 
    5657    reactorFactory = None 
    5758    originalHandler = None 
  • (a) twisted/plugins/twisted_qtstub.py vs. (b) /dev/null

    diff --git twisted/plugins/twisted_qtstub.py twisted/plugins/twisted_qtstub.py
    deleted file mode 100644
    index 1b9b08a..0000000
    a b  
    1 # Copyright (c) 2006 Twisted Matrix Laboratories. 
    2 # See LICENSE for details. 
    3  
    4 """ 
    5 Backwards-compatibility plugin for the Qt reactor. 
    6  
    7 This provides a Qt reactor plugin named C{qt} which emits a deprecation 
    8 warning and a pointer to the separately distributed Qt reactor plugins. 
    9 """ 
    10  
    11 import warnings 
    12  
    13 from twisted.application.reactors import Reactor, NoSuchReactor 
    14  
    15 wikiURL = 'http://twistedmatrix.com/trac/wiki/QTReactor' 
    16 errorMessage = ('qtreactor is no longer a part of Twisted due to licensing ' 
    17                 'issues. Please see %s for details.' % (wikiURL,)) 
    18  
    19 class QTStub(Reactor): 
    20     """ 
    21     Reactor plugin which emits a deprecation warning on the successful 
    22     installation of its reactor or a pointer to further information if an 
    23     ImportError occurs while attempting to install it. 
    24     """ 
    25     def __init__(self): 
    26         super(QTStub, self).__init__( 
    27             'qt', 'qtreactor', 'QT integration reactor') 
    28  
    29  
    30     def install(self): 
    31         """ 
    32         Install the Qt reactor with a deprecation warning or try to point 
    33         the user to further information if it cannot be installed. 
    34         """ 
    35         try: 
    36             super(QTStub, self).install() 
    37         except (ValueError, ImportError): 
    38             raise NoSuchReactor(errorMessage) 
    39         else: 
    40             warnings.warn( 
    41                 "Please use -r qt3 to import qtreactor", 
    42                 category=DeprecationWarning) 
    43  
    44  
    45 qt = QTStub() 
  • twisted/plugins/twisted_reactors.py

    diff --git twisted/plugins/twisted_reactors.py twisted/plugins/twisted_reactors.py
    index 428e96c..8b58c80 100644
     
    3636iocp = Reactor( 
    3737    'iocp', 'twisted.internet.iocpreactor', 
    3838    'Win32 IO Completion Ports-based reactor.') 
     39qt = Reactor( 
     40    'qt', 'twisted.internet.qtreactor', 
     41    'Qt based reactor using PySide or PyQt')