Ticket #3977: qtreactor-3977.patch

File qtreactor-3977.patch, 14.3 KB (added by michaelnt, 6 years ago)
  • new file doc/core/examples/qtdemo.py

    diff --git doc/core/examples/qtdemo.py doc/core/examples/qtdemo.py
    new file mode 100644
    index 0000000..b16709f
    - +  
     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
    class ReactorBuilder: 
    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
  • deleted file twisted/plugins/twisted_qtstub.py

    diff --git twisted/plugins/twisted_qtstub.py twisted/plugins/twisted_qtstub.py
    deleted file mode 100644
    index 1b9b08a..0000000
    + -  
    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
    kqueue = Reactor( 
    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')