Skip to content

Commit

Permalink
Revert "Merge remove-qtreactor-2130"
Browse files Browse the repository at this point in the history
This reverts parts of commit 5e1c1a6.

Reintegrating the Qt Reactor
  • Loading branch information
michaelnt committed Jan 22, 2011
1 parent 6dfc832 commit c5c6e29
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 59 deletions.
98 changes: 98 additions & 0 deletions doc/core/examples/qtdemo.py
@@ -0,0 +1,98 @@
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
# See LICENSE for details.


"""Qt demo.
Fetch a URL's contents.
"""

import sys, urlparse
from qt import *

from twisted.internet import qtreactor, protocol
app = QApplication([])
qtreactor.install(app)

from twisted.web import http


class TwistzillaClient(http.HTTPClient):
def __init__(self, edit, urls):
self.urls = urls
self.edit = edit

def connectionMade(self):
print 'Connected.'

self.sendCommand('GET', self.urls[2])
self.sendHeader('Host', '%s:%d' % (self.urls[0], self.urls[1]) )
self.sendHeader('User-Agent', 'Twistzilla')
self.endHeaders()

def handleResponse(self, data):
print 'Got response.'
self.edit.setText(data)



class TwistzillaWindow(QMainWindow):
def __init__(self, *args):
QMainWindow.__init__(self, *args)

self.setCaption("Twistzilla")

vbox = QVBox(self)
vbox.setMargin(2)
vbox.setSpacing(3)

hbox = QHBox(vbox)
label = QLabel("Address: ", hbox)

self.line = QLineEdit("http://www.twistedmatrix.com/", hbox)
self.connect(self.line, SIGNAL('returnPressed()'), self.fetchURL)

self.edit = QMultiLineEdit(vbox)
self.edit.setEdited(0)

self.setCentralWidget(vbox)

def fetchURL(self):
u = urlparse.urlparse(str(self.line.text()))

pos = u[1].find(':')

if pos == -1:
host, port = u[1], 80
else:
host, port = u[1][:pos], int(u[1][pos+1:])

if u[2] == '':
file = '/'
else:
file = u[2]

print 'Connecting to.'
from twisted.internet import reactor
protocol.ClientCreator(reactor, TwistzillaClient, self.edit, (host, port, file)).connectTCP(host, port)


def main():
"""Run application."""
# hook up Qt application to Twisted
from twisted.internet import reactor

win = TwistzillaWindow()
win.show()

# make sure stopping twisted event also shuts down QT
reactor.addSystemEventTrigger('after', 'shutdown', app.quit )

# shutdown twisted when window is closed
app.connect(app, SIGNAL("lastWindowClosed()"), reactor.stop)

reactor.run()


if __name__ == '__main__':
main()
193 changes: 179 additions & 14 deletions twisted/internet/qtreactor.py
@@ -1,19 +1,184 @@
# -*- test-case-name: twisted.internet.test.test_qtreactor -*-
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
# See LICENSE for details.

try:
# 'import qtreactor' would have imported this file instead of the
# top-level qtreactor. __import__ does the right thing
# (kids, don't repeat this at home)
install = __import__('qtreactor').install
except ImportError:
from twisted.plugins.twisted_qtstub import errorMessage
raise ImportError(errorMessage)
else:
import warnings
warnings.warn("Please use qtreactor instead of twisted.internet.qtreactor",
category=DeprecationWarning)

"""
This module provides support for Twisted to interact with the PyQt mainloop.
In order to use this support, simply do the following::
| from twisted.internet import qtreactor
| qtreactor.install()
Then use twisted.internet APIs as usual. The other methods here are not
intended to be called directly.
API Stability: stable
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
"""

__all__ = ['install']

# System Imports
from qt import QSocketNotifier, QObject, SIGNAL, QTimer, QApplication
import sys

# Twisted Imports
from twisted.python import log, failure
from twisted.internet import posixbase

reads = {}
writes = {}
hasReader = reads.has_key
hasWriter = writes.has_key


class TwistedSocketNotifier(QSocketNotifier):
'''Connection between an fd event and reader/writer callbacks'''

def __init__(self, reactor, watcher, type):
QSocketNotifier.__init__(self, watcher.fileno(), type)
self.reactor = reactor
self.watcher = watcher
self.fn = None
if type == QSocketNotifier.Read:
self.fn = self.read
elif type == QSocketNotifier.Write:
self.fn = self.write
QObject.connect(self, SIGNAL("activated(int)"), self.fn)

def shutdown(self):
QObject.disconnect(self, SIGNAL("activated(int)"), self.fn)
self.setEnabled(0)
self.fn = self.watcher = None

def read(self, sock):
why = None
w = self.watcher
try:
why = w.doRead()
except:
why = sys.exc_info()[1]
log.msg('Error in %s.doRead()' % w)
log.deferr()
if why:
self.reactor._disconnectSelectable(w, why, True)
self.reactor.simulate()

def write(self, sock):
why = None
w = self.watcher
self.setEnabled(0)
try:
why = w.doWrite()
except:
why = sys.exc_value
log.msg('Error in %s.doWrite()' % w)
log.deferr()
if why:
self.reactor.removeReader(w)
self.reactor.removeWriter(w)
try:
w.connectionLost(failure.Failure(why))
except:
log.deferr()
elif self.watcher:
self.setEnabled(1)
self.reactor.simulate()


class QTReactor(posixbase.PosixReactorBase):
"""Qt based reactor."""

# Reference to a DelayedCall for self.crash() when the reactor is
# entered through .iterate()
_crashCall = None

_timer = None

def __init__(self, app=None):
self.running = 0
posixbase.PosixReactorBase.__init__(self)
if app is None:
app = QApplication([])
self.qApp = app
self.addSystemEventTrigger('after', 'shutdown', self.cleanup)

def addReader(self, reader):
if not hasReader(reader):
reads[reader] = TwistedSocketNotifier(self, reader, QSocketNotifier.Read)

def addWriter(self, writer):
if not hasWriter(writer):
writes[writer] = TwistedSocketNotifier(self, writer, QSocketNotifier.Write)

def removeReader(self, reader):
if hasReader(reader):
reads[reader].shutdown()
del reads[reader]

def removeWriter(self, writer):
if hasWriter(writer):
writes[writer].shutdown()
del writes[writer]

def removeAll(self):
return self._removeAll(reads, writes)

def simulate(self):
if self._timer is not None:
self._timer.stop()
self._timer = None

if not self.running:
self.running = 1
self.qApp.exit_loop()
return
self.runUntilCurrent()

if self._crashCall is not None:
self._crashCall.reset(0)

# gah
timeout = self.timeout()
if timeout is None:
timeout = 1.0
timeout = min(timeout, 0.1) * 1010

if self._timer is None:
self._timer = QTimer()
QObject.connect(self._timer, SIGNAL("timeout()"), self.simulate)
self._timer.start(timeout, 1)

def cleanup(self):
if self._timer is not None:
self._timer.stop()
self._timer = None

def iterate(self, delay=0.0):
log.msg(channel='system', event='iteration', reactor=self)
self._crashCall = self.callLater(delay, self.crash)
self.run()

def run(self, installSignalHandlers=1):
self.running = 1
self.startRunning(installSignalHandlers=installSignalHandlers)
self.simulate()
self.qApp.enter_loop()

def crash(self):
if self._crashCall is not None:
if self._crashCall.active():
self._crashCall.cancel()
self._crashCall = None
self.running = 0


def install(app=None):
"""Configure the twisted mainloop to be run inside the qt mainloop.
"""
from twisted.internet import main

reactor = QTReactor(app=app)
main.installReactor(reactor)
45 changes: 0 additions & 45 deletions twisted/plugins/twisted_qtstub.py

This file was deleted.

3 changes: 3 additions & 0 deletions twisted/plugins/twisted_reactors.py
Expand Up @@ -36,3 +36,6 @@
iocp = Reactor(
'iocp', 'twisted.internet.iocpreactor',
'Win32 IO Completion Ports-based reactor.')
qt = Reactor(
'qt', 'twisted.internet.qtreactor',
'Qt based reactor using PySide or PyQt')

0 comments on commit c5c6e29

Please sign in to comment.