failed twisted integration....just stick w/oyoyo for now

This commit is contained in:
Stephen Dranger 2011-02-15 03:44:33 -06:00
parent 666c2b88c0
commit 5de02c65b5
33 changed files with 984 additions and 181 deletions

1
TODO
View file

@ -1,4 +1,5 @@
Bugs:
* memos need to test for max time
* color swatch text doesnt disappear
* X and _ buttons move around all crazy like

BIN
convo.pyc

Binary file not shown.

Binary file not shown.

Binary file not shown.

299
irc.py
View file

@ -1,10 +1,9 @@
from PyQt4 import QtGui, QtCore
from oyoyo.client import IRCClient
from oyoyo.cmdhandler import DefaultCommandHandler
from oyoyo import helpers
from twisted.internet.protocol import ClientFactory
from twisted.words.protocols.irc import IRCClient
from twisted.internet import reactor
import logging
import random
import socket
from dataobjs import Mood, PesterProfile
from generic import PesterList
@ -18,31 +17,32 @@ class PesterIRC(QtCore.QObject):
self.config = config
def IRCConnect(self):
server = self.config.server()
port = self.config.port()
self.cli = IRCClient(PesterHandler, host=server, port=int(port), nick=self.mainwindow.profile().handle, real_name='pcc30', blocking=True)
self.cli.command_handler.parent = self
self.cli.command_handler.mainwindow = self.mainwindow
self.conn = self.cli.connect()
self.brokenConnection = False
port = int(self.config.port())
nick = self.mainwindow.profile()
self.cli = PesterIRCFactory(nick, self)
logging.info("---> Logging on...")
reactor.connectTCP(server, port, self.cli)
reactor.run()
def closeConnection(self):
self.cli.close()
def setConnectionBroken(self, broken=True):
self.brokenConnection = True
#logging.info("---> Logging on...")
# self.cli.close()
pass
@QtCore.pyqtSlot(PesterProfile)
def getMood(self, *chums):
self.cli.command_handler.getMood(*chums)
self.cli.getMood(*chums)
@QtCore.pyqtSlot(PesterList)
def getMoods(self, chums):
self.cli.command_handler.getMood(*chums)
self.cli.getMood(*chums)
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
def sendMessage(self, text, handle):
h = unicode(handle)
textl = [unicode(text)]
CMD_LENGTH = 450
def splittext(l):
if len(l[0]) > 400:
space = l[0].rfind(" ", 0,400)
if len(l[0]) > CMD_LENGTH:
space = l[0].rfind(" ", 0,CMD_LENGTH)
if space == -1:
space = 400
space = CMD_LENGTH
a = l[0][0:space]
b = l[0][space:]
if len(b) > 0:
@ -52,117 +52,72 @@ class PesterIRC(QtCore.QObject):
else:
return l
textl = splittext(textl)
try:
for t in textl:
helpers.msg(self.cli, h, t)
except socket.error:
self.setConnectionBroken()
for t in textl:
self.cli.msg(h, t)
@QtCore.pyqtSlot(QtCore.QString, bool)
def startConvo(self, handle, initiated):
h = unicode(handle)
try:
if initiated:
helpers.msg(self.cli, h, "PESTERCHUM:BEGIN")
helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
except socket.error:
self.setConnectionBroken()
if initiated:
self.cli.msg(h, "PESTERCHUM:BEGIN")
self.cli.msg(h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
@QtCore.pyqtSlot(QtCore.QString)
def endConvo(self, handle):
h = unicode(handle)
try:
helpers.msg(self.cli, h, "PESTERCHUM:CEASE")
except socket.error:
self.setConnectionBroken()
self.cli.msg(h, "PESTERCHUM:CEASE")
@QtCore.pyqtSlot()
def updateProfile(self):
me = self.mainwindow.profile()
handle = me.handle
try:
helpers.nick(self.cli, handle)
except socket.error:
self.setConnectionBroken()
self.cli.setNick(handle)
self.updateMood()
@QtCore.pyqtSlot()
def updateMood(self):
me = self.mainwindow.profile()
try:
helpers.msg(self.cli, "#pesterchum", "MOOD >%d" % (me.mood.value()))
except socket.error:
self.setConnectionBroken()
self.cli.msg("#pesterchum", "MOOD >%d" % (me.mood.value()))
@QtCore.pyqtSlot()
def updateColor(self):
me = self.mainwindow.profile()
for h in self.mainwindow.convos.keys():
try:
helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
except socket.error:
self.setConnectionBroken()
self.cli.msg(h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
@QtCore.pyqtSlot(QtCore.QString)
def blockedChum(self, handle):
h = unicode(handle)
try:
helpers.msg(self.cli, h, "PESTERCHUM:BLOCK")
except socket.error:
self.setConnectionBroken()
self.cli.msg(h, "PESTERCHUM:BLOCK")
@QtCore.pyqtSlot(QtCore.QString)
def unblockedChum(self, handle):
h = unicode(handle)
try:
helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK")
except socket.error:
self.setConnectionBroken()
self.cli.msg(h, "PESTERCHUM:UNBLOCK")
@QtCore.pyqtSlot(QtCore.QString)
def requestNames(self, channel):
c = unicode(channel)
try:
helpers.names(self.cli, c)
except socket.error:
self.setConnectionBroken()
self.cli.sendMessage("NAMES", c)
@QtCore.pyqtSlot()
def requestChannelList(self):
try:
helpers.channel_list(self.cli)
except socket.error:
self.setConnectionBroken()
self.cli.sendMessage("LIST")
@QtCore.pyqtSlot(QtCore.QString)
def joinChannel(self, channel):
c = unicode(channel)
try:
helpers.join(self.cli, c)
except socket.error:
self.setConnectionBroken()
self.cli.join(c)
@QtCore.pyqtSlot(QtCore.QString)
def leftChannel(self, channel):
c = unicode(channel)
try:
helpers.part(self.cli, c)
except socket.error:
self.setConnectionBroken()
self.cli.leave(c)
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
def kickUser(self, handle, channel):
c = unicode(channel)
h = unicode(handle)
try:
helpers.kick(self.cli, h, c)
except socket.error:
self.setConnectionBroken()
self.cli.kick(h, c)
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
def setChannelMode(self, channel, mode, command):
c = unicode(channel)
m = unicode(mode)
m = unicode(mode).replace("+", "")
cmd = unicode(command)
if cmd == "":
cmd = None
try:
helpers.mode(self.cli, c, m, cmd)
except socket.error:
self.setConnectionBroken()
self.cli.mode(c, True, m, cmd)
@QtCore.pyqtSlot()
def reconnectIRC(self):
self.setConnectionBroken()
def updateIRC(self):
self.conn.next()
pass
moodUpdated = QtCore.pyqtSignal(QtCore.QString, Mood)
colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor)
@ -174,10 +129,48 @@ class PesterIRC(QtCore.QObject):
nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
connected = QtCore.pyqtSignal()
userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString,
QtCore.QString)
QtCore.QString)
class PesterIRCClient(IRCClient):
realname = "pcc30"
username = "pcc30"
def __init__(self, nick, qobj):
self.nickname = nick
self.parent = qobj
self.mainwindow = qobj.mainwindow
qobj.irc = self
def msg(self, user, message):
logging.info("---> send PRIVMSG %s %s" % (user, message))
IRCClient.msg(self, user, message)
def signedOn(self):
logging.info("---> recv WELCOME")
self.parent.connected.emit()
self.join("#pesterchum")
mychumhandle = self.mainwindow.profile().handle
mymood = self.mainwindow.profile().mood.value()
self.msg("#pesterchum", "MOOD >%d" % (mymood))
chums = self.mainwindow.chumList.chums
self.getMood(*chums)
def getMood(self, *chums):
chumglub = "GETMOOD "
for c in chums:
chandle = c.handle
if len(chumglub+chandle) >= 350:
self.msg("#pesterchum", chumglub)
chumglub = "GETMOOD "
chumglub += chandle
if chumglub != "GETMOOD ":
self.msg("#pesterchum", chumglub)
class PesterHandler(DefaultCommandHandler):
def privmsg(self, nick, chan, msg):
# do we still need this?
#msg = msg.decode("utf-8")
# display msg, do other stuff
if len(msg) == 0:
return
@ -185,7 +178,7 @@ class PesterHandler(DefaultCommandHandler):
if msg[0] == '\x01':
return
handle = nick[0:nick.find("!")]
logging.info("---> recv \"PRIVMSG %s :%s\"" % (handle, msg))
logging.info("---> recv PRIVMSG %s :%s" % (handle, msg))
if chan == "#pesterchum":
# follow instructions
if msg[0:6] == "MOOD >":
@ -198,8 +191,7 @@ class PesterHandler(DefaultCommandHandler):
mychumhandle = self.mainwindow.profile().handle
mymood = self.mainwindow.profile().mood.value()
if msg.find(mychumhandle, 8) != -1:
helpers.msg(self.client, "#pesterchum",
"MOOD >%d" % (mymood))
self.msg("#pesterchum", "MOOD >%d" % (mymood))
elif chan[0] == '#':
if msg[0:16] == "PESTERCHUM:TIME>":
self.parent.timeCommand.emit(chan, handle, msg[16:])
@ -220,89 +212,88 @@ class PesterHandler(DefaultCommandHandler):
self.parent.colorUpdated.emit(handle, color)
else:
self.parent.messageReceived.emit(handle, msg)
def welcome(self, server, nick, msg):
self.parent.connected.emit()
helpers.join(self.client, "#pesterchum")
mychumhandle = self.mainwindow.profile().handle
mymood = self.mainwindow.profile().mood.value()
helpers.msg(self.client, "#pesterchum", "MOOD >%d" % (mymood))
chums = self.mainwindow.chumList.chums
self.getMood(*chums)
def nicknameinuse(self, server, cmd, nick, msg):
def irc_ERR_NICKNAMEINUSE(self, prefix, params):
logging.info("---> recv NICKINUSE %s %s" % (prefix, params))
newnick = "pesterClient%d" % (random.randint(100,999))
helpers.nick(self.client, newnick)
self.setNick(newnick)
self.parent.nickCollision.emit(nick, newnick)
def quit(self, nick, reason):
def userQuit(self, nick, reason):
logging.info("---> recv QUIT %s %s" % (nick, reason))
handle = nick[0:nick.find("!")]
self.parent.userPresentUpdate.emit(handle, "", "quit")
self.parent.moodUpdated.emit(handle, Mood("offline"))
def kick(self, opnick, channel, handle, op):
self.parent.userPresentUpdate.emit(handle, channel, "kick:%s" % (op))
def userKicked(self, kickee, channel, kicker, msg):
logging.info("---> recv KICK %s %s %s %s" % (kickee, channel, kicker, msg))
self.parent.userPresentUpdate.emit(kickee, channel, "kick:%s" % (op))
# ok i shouldnt be overloading that but am lazy
def part(self, nick, channel, reason="nanchos"):
def userLeft(self, nick, channel, reason="nanchos"):
logging.info("---> recv LEFT %s %s" % (nick, channel))
handle = nick[0:nick.find("!")]
self.parent.userPresentUpdate.emit(handle, channel, "left")
if channel == "#pesterchum":
self.parent.moodUpdated.emit(handle, Mood("offline"))
def join(self, nick, channel):
def userJoined(self, nick, channel):
logging.info("---> recv JOIN %s %s" % (nick, channel))
handle = nick[0:nick.find("!")]
self.parent.userPresentUpdate.emit(handle, channel, "join")
if channel == "#pesterchum":
self.parent.moodUpdated.emit(handle, Mood("chummy"))
def mode(self, op, channel, mode, handle=""):
def modeChannel(self, op, channel, set, modes, args):
logging.info("---> recv MODE %s %s %s %s %s" % (op, channel, set, modes, args))
if set:
modes += "+"
else:
modes += "-"
handle = ""
print args
self.parent.userPresentUpdate.emit(handle, channel, mode)
def nick(self, oldnick, newnick):
oldhandle = oldnick[0:oldnick.find("!")]
def userRenamed(self, oldnick, newnick):
logging.info("---> recv RENAME %s %s" % (oldnick, newnick))
newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb)
self.parent.moodUpdated.emit(oldhandle, Mood("offline"))
self.parent.userPresentUpdate.emit("%s:%s" % (oldhandle, newnick), "", "nick")
self.parent.moodUpdated.emit(oldnick, Mood("offline"))
self.parent.userPresentUpdate.emit("%s:%s" % (oldnick, newnick), "", "nick")
if newnick in self.mainwindow.chumList.chums:
self.getMood(newchum)
def namreply(self, server, nick, op, channel, names):
namelist = names.split(" ")
logging.info("---> recv \"NAMES %s: %d names\"" % (channel, len(namelist)))
if not hasattr(self, 'channelnames'):
self.channelnames = {}
if not self.channelnames.has_key(channel):
self.channelnames[channel] = []
self.channelnames[channel].extend(namelist)
def endofnames(self, server, nick, channel, msg):
namelist = self.channelnames[channel]
pl = PesterList(namelist)
del self.channelnames[channel]
self.parent.namesReceived.emit(channel, pl)
def liststart(self, server, handle, *info):
self.channel_list = []
info = list(info)
self.channel_field = info.index("Channel") # dunno if this is protocol
def list(self, server, handle, *info):
channel = info[self.channel_field]
if channel not in self.channel_list and channel != "#pesterchum":
self.channel_list.append(channel)
def listend(self, server, handle, msg):
pl = PesterList(self.channel_list)
self.parent.channelListReceived.emit(pl)
self.channel_list = []
def irc_RPL_NAMREPLY(self, prefix, params):
logging.info("---> recv NAMREPLY %s %s" % (prefix, params))
# namelist = names.split(" ")
# logging.info("---> recv \"NAMES %s: %d names\"" % (channel, len(namelist)))
# if not hasattr(self, 'channelnames'):
# self.channelnames = {}
# if not self.channelnames.has_key(channel):
# self.channelnames[channel] = []
# self.channelnames[channel].extend(namelist)
def irc_RPL_ENDOFNAMES(self, prefix, params):
logging.info("---> recv ENDOFNAMES %s %s" % (prefix, params))
# namelist = self.channelnames[channel]
# pl = PesterList(namelist)
# del self.channelnames[channel]
# self.parent.namesReceived.emit(channel, pl)
def irc_RPL_LISTSTART(self, prefix, params):
logging.info("---> recv LISTSTART %s %s" % (prefix, params))
# self.channel_list = []
# info = list(info)
# self.channel_field = info.index("Channel") # dunno if this is protocol
def irc_RPL_LIST(self, prefix, params):
logging.info("---> recv LIST %s %s" % (prefix, params))
# channel = info[self.channel_field]
# if channel not in self.channel_list and channel != "#pesterchum":
# self.channel_list.append(channel)
def irc_RPL_LISTEND(self, prefix, params):
logging.info("---> recv LISTEND %s %s" % (prefix, params))
# pl = PesterList(self.channel_list)
# self.parent.channelListReceived.emit(pl)
# self.channel_list = []
def getMood(self, *chums):
chumglub = "GETMOOD "
for c in chums:
chandle = c.handle
if len(chumglub+chandle) >= 350:
try:
helpers.msg(self.client, "#pesterchum", chumglub)
except socket.error:
self.parent.setConnectionBroken()
chumglub = "GETMOOD "
chumglub += chandle
if chumglub != "GETMOOD ":
try:
helpers.msg(self.client, "#pesterchum", chumglub)
except socket.error:
self.parent.setConnectionBroken()
class PesterIRCFactory(ClientFactory):
protocol = PesterIRCClient
def __init__(self, nick, qobj):
self.irc = self.protocol(nick, qobj)
def buildProtocol(self, addr=None):
return self.irc
def clientConnectionLost(self, conn, reason):
conn.connect()
def clientConnectionFailed(self, conn, reason):
conn.connect()

BIN
irc.pyc

Binary file not shown.

BIN
memos.pyc

Binary file not shown.

View file

@ -649,6 +649,6 @@ class LoadingScreen(QtGui.QDialog):
class AboutPesterchum(QtGui.QMessageBox):
def __init__(self, parent=None):
QtGui.QMessageBox.__init__(self, parent)
self.setText("P3ST3RCHUM V. 0.1.2")
self.setText("P3ST3RCHUM V. 3.14 alpha 4")
self.setInformativeText("Programming by illuminatedwax (ghostDunk), art by Grimlive (aquaMarinist)")
self.mainwindow = parent

BIN
menus.pyc

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -29,6 +29,8 @@ from oyoyo.parse import *
from oyoyo import helpers
from oyoyo.cmdhandler import CommandError
from datetime import *
# Python < 3 compatibility
if sys.version_info < (3,):
class bytes(object):

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
{"tabs": true, "soundon": true, "server": "irc.tymoon.eu", "chums": ["unknownTraveler", "tentacleTherapist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador", "cuttlefishCuller", "rageInducer", "gallowsCalibrator", "caligulasAquarium", "terminallyCapricious", "illuminatedWax", "aquaMarinist", "elegantDiversion", "moirailBunp", "uroborosUnbound", "androidTechnician", "midnightSparrow", "apocalypseArisen", "anguillaNuntia", "oilslickOrchid", "confusedTransient", "pretentiousFantasia", "aquaticMarinist", "lyricalKeraunoscopic", "counterRealist", "ectoBiologist", "percipientPedestrian", "asceticClinician", "doctectiveMiracles", "noSense", "obliviousCrafter", "ircMonster"], "defaultprofile": "ghostDunk", "block": []}
{"tabs": true, "soundon": true, "server": "irc.tymoon.eu", "chums": ["unknownTraveler", "tentacleTherapist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador", "cuttlefishCuller", "rageInducer", "gallowsCalibrator", "caligulasAquarium", "terminallyCapricious", "illuminatedWax", "aquaMarinist", "elegantDiversion", "moirailBunp", "uroborosUnbound", "androidTechnician", "midnightSparrow", "apocalypseArisen", "anguillaNuntia", "oilslickOrchid", "confusedTransient", "pretentiousFantasia", "aquaticMarinist", "lyricalKeraunoscopic", "counterRealist", "ectoBiologist", "percipientPedestrian", "asceticClinician", "doctectiveMiracles", "noSense", "obliviousCrafter", "ircMonster"], "defaultprofile": "testProfile", "block": []}

View file

@ -21,7 +21,6 @@ from generic import PesterIcon, RightClickList, MultiTextDialog, PesterList
from convo import PesterTabWindow, PesterText, PesterInput, PesterConvo
from parsetools import convertTags, addTimeInitial
from memos import PesterMemo, MemoTabWindow, TimeTracker
from irc import PesterIRC
class waitingMessageHolder(object):
def __init__(self, mainwindow, **msgfuncs):
@ -1657,26 +1656,6 @@ class PesterWindow(MovingWindow):
closeSignal = QtCore.pyqtSignal()
reconnectIRC = QtCore.pyqtSignal()
class IRCThread(QtCore.QThread):
def __init__(self, ircobj):
QtCore.QThread.__init__(self)
self.irc = ircobj
def run(self):
irc = self.irc
irc.IRCConnect()
while 1:
if irc.brokenConnection:
irc.brokenConnection = False
self.restartIRC.emit()
irc.closeConnection()
irc.IRCConnect()
try:
irc.updateIRC()
except socket.error:
irc.setConnectionBroken()
restartIRC = QtCore.pyqtSignal()
class PesterTray(QtGui.QSystemTrayIcon):
def __init__(self, icon, mainwindow, parent):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
@ -1696,6 +1675,9 @@ class MainProgram(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
self.app = QtGui.QApplication(sys.argv)
from qt4reactor import qt4reactor
qt4reactor.install()
from irc import PesterIRC
if pygame.mixer:
# we could set the frequency higher but i love how cheesy it sounds
try:
@ -1744,9 +1726,7 @@ class MainProgram(QtCore.QObject):
self.irc = PesterIRC(self.widget.config, self.widget)
self.connectWidgets(self.irc, self.widget)
self.ircapp = IRCThread(self.irc)
self.connect(self.ircapp, QtCore.SIGNAL('restartIRC()'),
self, QtCore.SLOT('restartIRC()'))
self.irc.IRCConnect()
def connectWidgets(self, irc, widget):
irc.connect(widget, QtCore.SIGNAL('sendMessage(QString, QString)'),
@ -1866,7 +1846,6 @@ class MainProgram(QtCore.QObject):
sys.exit(0)
def run(self):
self.ircapp.start()
self.widget.loadingscreen = LoadingScreen(self.widget)
self.connect(self.widget.loadingscreen, QtCore.SIGNAL('rejected()'),
self.widget, QtCore.SLOT('close()'))

9
qt4reactor/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
*.[oa]
*~
*.pyc
*.pyo
*.class
.project
.pydevproject
*.cache
_trial_temp

57
qt4reactor/LICENSE Normal file
View file

@ -0,0 +1,57 @@
Copyright (c) 2001-2010
Allen Short
Andy Gayton
Andrew Bennetts
Antoine Pitrou
Apple Computer, Inc.
Benjamin Bruheim
Bob Ippolito
Canonical Limited
Christopher Armstrong
David Reid
Donovan Preston
Eric Mangold
Eyal Lotem
Itamar Shtull-Trauring
James Knight
Jason A. Mobarak
Jean-Paul Calderone
Jessica McKellar
Jonathan Jacobs
Jonathan Lange
Jonathan D. Simms
Jürgen Hermann
Kevin Horn
Kevin Turner
Mary Gardiner
Matthew Lefkowitz
Massachusetts Institute of Technology
Moshe Zadka
Paul Swartz
Pavel Pergamenshchik
Ralph Meijer
Sean Riley
Software Freedom Conservancy
Travis B. Hartwell
Thijs Triemstra
Thomas Herve
Timothy Allen
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

55
qt4reactor/README Normal file
View file

@ -0,0 +1,55 @@
Unpack this directory into your PYTHONPATH.
test with:
trial --reactor=qt4 twisted (or twisted.test or twisted.test.test_internet)
= Contributors =
Many thanks to Darren Dale who provided the patch to fix the reactor for Qt4.4
= Using the Qt4Reactor =
In your own code, BEFORE you import the reactor...
app = QApplication(sys.argv)
import qt4reactor
qt4reactor.install()
= Gui =
There is a way to run trial using a gui... in bin, there is a routine
gtrial. Put that in the same directory as trial and it pops up a
trivial gui... hit the buton and it all runs the same... don't use the
--reactor option when calling gtrial... but all the other options
appear to work. This was just to make sure there wasn't anything
strange with guis which there doesn't appear to be
If you're writing a conventional Qt application and just want twisted
as an addon, you can get that by calling reactor.runReturn() instead
of run(). This call needs to occur after your installation of of the
reactor and after QApplication.exec_() (or QCoreApplication.exec_()
whichever you are using.
reactor.run() will also work as expected in a typical twisted application
more docs in qt4reactor.py
Note that if a QApplication or QCoreApplication instance isn't
constructed prior to calling reactor run, an internally owned
QCoreApplication is created and destroyed. This won't work if you call
runReturn instead of run unless you take responsibility for wacking
QCoreApplication yourself...
However, most users want this reactor to do gui stuff so this
shouldn't be an issue.
Performance impact of Qt has been reduced by minimizing use of
signaling which is expensive. 186s for qt4reactor vs 180s for select
for entire twisted trial suite.
-glenn
--
Glenn H. Tarbox, PhD
glenn@tarbox.org

0
qt4reactor/__init__.py Normal file
View file

24
qt4reactor/bin/gtrial Executable file
View file

@ -0,0 +1,24 @@
#!/usr/bin/python
# Twisted, the Framework of Your Internet
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
# See LICENSE for details.
### Twisted Preamble
# This makes sure that users don't have to set up their environment
# specially in order to run these programs from bin/.
import sys, os, string
if string.find(os.path.abspath(sys.argv[0]), os.sep+'Twisted') != -1:
sys.path.insert(0, os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir)))
if hasattr(os, "getuid") and os.getuid() != 0:
sys.path.insert(0, os.curdir)
### end of preamble
# begin chdir armor
sys.path[:] = map(os.path.abspath, sys.path)
# end chdir armor
from gtrial import run
run()

View file

@ -0,0 +1,57 @@
import sys
from PySide import QtGui, QtScript
from PySide.QtCore import QTimer, SIGNAL, QObject
import qt4reactor
app = QtGui.QApplication(sys.argv)
qt4reactor.install()
from twisted.internet import reactor, task
from twisted.python import log
log.startLogging(sys.stdout)
class doNothing(QObject):
def __init__(self):
self.count = 0
self.looping=False
task.LoopingCall(self.printStat).start(1.0)
QObject.__init__(self)
def doSomething(self):
if not self.looping: return
self.count += 1
reactor.callLater(0.003,self.doSomething)
def buttonClick(self):
if self.looping:
self.looping=False
log.msg('looping stopped....')
else:
self.looping=True
self.doSomething()
log.msg('looping started....')
def printStat(self):
log.msg(' c: ' + str(self.count) +
' st: ' + str(reactor._doSomethingCount))
t=doNothing()
engine = QtScript.QScriptEngine()
button = QtGui.QPushButton()
scriptButton = engine.newQObject(button)
engine.globalObject().setProperty("button", scriptButton)
app.connect(button, SIGNAL("clicked()"), t.buttonClick)
engine.evaluate("button.text = 'Hello World!'")
engine.evaluate("button.styleSheet = 'font-style: italic'")
engine.evaluate("button.show()")
reactor.runReturn()
app.exec_()
log.msg('fell off the bottom?...')

View file

@ -0,0 +1,51 @@
import sys
from PySide import QtGui, QtScript
from PySide.QtCore import QTimer, SIGNAL, QEventLoop
import qt4reactor
app = QtGui.QApplication(sys.argv)
qt4reactor.install()
from twisted.internet import reactor, task
class doNothing(object):
def __init__(self):
self.count = 0
self.running=False
task.LoopingCall(self.printStat).start(1.0)
def buttonClick(self):
if self.running:
self.running=False
print 'CLICK: calling reactor stop...'
reactor.stop()
print 'reactor stop called....'
else:
self.running=True
print 'CLICK: entering run'
reactor.run()
print 'reactor run returned...'
def printStat(self):
print 'tick...'
t=doNothing()
engine = QtScript.QScriptEngine()
button = QtGui.QPushButton()
scriptButton = engine.newQObject(button)
engine.globalObject().setProperty("button", scriptButton)
app.connect(button, SIGNAL("clicked()"), t.buttonClick)
engine.evaluate("button.text = 'Hello World!'")
engine.evaluate("button.styleSheet = 'font-style: italic'")
engine.evaluate("button.show()")
app.exec_()
print 'fell off the bottom?...'

View file

@ -0,0 +1,106 @@
from PySide.QtCore import *
from PySide.QtGui import *
import sys, qt4reactor
app = QApplication(sys.argv)
qt4reactor.install()
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.python import log
import time, sys
class IRCCore(irc.IRCClient):
nickname = 'dosdsdssd'
def connectionMade(self):
self.nickname = self.factory.window.nickName.text().encode('ascii')
self.factory.window.protocol = self
irc.IRCClient.connectionMade(self)
self.log('connected!!')
def connectionLost(self, reason):
self.log('disconnected... :( %s'%reason)
def signedOn(self):
chanName = self.factory.window.channelName.text().encode('ascii')
self.join(chanName)
def joined(self, channel):
self.log('joined %s'%channel)
def privmsg(self, user, channel, msg):
self.log('%s %s %s'%(user, channel, msg))
def action(self, user, channel, msg):
self.log('action: %s %s %s'%(user, channel, msg))
def log(self, str):
self.factory.window.view.addItem(str)
class IRCCoreFactory(protocol.ClientFactory):
protocol = IRCCore
def __init__(self, window):
self.window = window
def clientConnectionLost(self, connector, reason):
# reconnect to server if lose connection
connector.connect()
def clientConnectionFailed(self, connector, reason):
print('connection failed! :(', reason)
reactor.stop()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
connectLayout = QHBoxLayout()
connectLayout.addWidget(QLabel('Server:'))
self.serverName = QLineEdit('irc.freenode.org')
connectLayout.addWidget(self.serverName)
connectLayout.addWidget(QLabel('Channel:'))
self.channelName = QLineEdit('#pangaea')
connectLayout.addWidget(self.channelName)
connectLayout.addWidget(QLabel('Nick:'))
self.nickName = QLineEdit('ceruleanwave9832')
connectLayout.addWidget(self.nickName)
self.connectButton = QPushButton('Connect!')
connectLayout.addWidget(self.connectButton)
self.connectButton.clicked.connect(self.connectIRC)
self.view = QListWidget()
self.entry = QLineEdit()
self.entry.returnPressed.connect(self.sendMessage)
irc = QWidget(self)
vbox = QVBoxLayout()
vbox.addLayout(connectLayout)
vbox.addWidget(self.view)
vbox.addWidget(self.entry)
irc.setLayout(vbox)
self.setCentralWidget(irc)
self.setWindowTitle('IRC')
self.setUnifiedTitleAndToolBarOnMac(True)
self.showMaximized()
self.protocol = None
def connectIRC(self):
self.connectButton.setDisabled(True)
self.channelName.setDisabled(True)
self.nickName.setDisabled(True)
self.serverName.setDisabled(True)
ircCoreFactory = IRCCoreFactory(self)
serverName = self.serverName.text().encode('ascii')
reactor.connectTCP(serverName, 6667, ircCoreFactory)
#reactor.runReturn()
#app.exit()
#app.exit()
reactor.run()
def sendMessage(self):
if self.protocol:
chanName = self.channelName.text().encode('ascii')
message = self.entry.text().encode('ascii')
self.protocol.msg(chanName, message)
self.view.addItem('%s <%s> %s'%(chanName, self.protocol.nickname, message))
else:
self.view.addItem('Not connected.')
self.entry.setText('')
def closeEvent(self, event):
print('Attempting to close the main window!')
reactor.stop()
event.accept()
if __name__ == '__main__':
mainWin = MainWindow()
sys.exit(app.exec_())

View file

@ -0,0 +1,37 @@
import sys
from PySide import QtGui, QtScript
from PySide.QtCore import QTimer, SIGNAL
import qt4reactor
app = QtGui.QApplication(sys.argv)
qt4reactor.install()
from twisted.internet import reactor, task
from twisted.python import log
log.startLogging(sys.stdout)
def testReactor():
print 'tick...'
def buttonClick():
print 'click...'
reactor.iterate(5.0)
print 'click return'
engine = QtScript.QScriptEngine()
button = QtGui.QPushButton()
scriptButton = engine.newQObject(button)
engine.globalObject().setProperty("button", scriptButton)
app.connect(button, SIGNAL("clicked()"), buttonClick)
engine.evaluate("button.text = 'Hello World!'")
engine.evaluate("button.styleSheet = 'font-style: italic'")
engine.evaluate("button.show()")
task.LoopingCall(testReactor).start(1.0)
reactor.run()
log.msg('fell off the bottom?...')

View file

@ -0,0 +1,25 @@
import sys
from twisted.application import reactors
import qt4reactor
qt4reactor.install()
#reactors.installReactor('qt4')
from twisted.internet import reactor, task
from twisted.python import log
log.startLogging(sys.stdout)
def testReactor():
print 'tick...'
def doit():
task.LoopingCall(testReactor).start(1.0)
reactor.callLater(15.0,reactor.stop)
reactor.callWhenRunning(doit)
log.msg('calling reactor.run()')
reactor.run()
log.msg('fell off the bottom?...')
#sys.exit(app.exec_())

41
qt4reactor/gtrial.py Executable file
View file

@ -0,0 +1,41 @@
import sys
from PyQt4 import QtGui, QtScript
from PyQt4.QtCore import QTimer, SIGNAL, QEventLoop
import qt4reactor
app = QtGui.QApplication(sys.argv)
qt4reactor.install()
from twisted.internet import reactor, task
class doNothing(object):
def __init__(self):
self.count = 0
self.running=False
def buttonClick(self):
if not self.running:
from twisted.scripts import trial
trial.run()
def run():
t=doNothing()
engine = QtScript.QScriptEngine()
button = QtGui.QPushButton()
scriptButton = engine.newQObject(button)
engine.globalObject().setProperty("button", scriptButton)
app.connect(button, SIGNAL("clicked()"), t.buttonClick)
engine.evaluate("button.text = 'Do Twisted Gui Trial'")
engine.evaluate("button.styleSheet = 'font-style: italic'")
engine.evaluate("button.show()")
app.exec_()
print 'fell off the bottom?...'

359
qt4reactor/qt4reactor.py Normal file
View file

@ -0,0 +1,359 @@
# Copyright (c) 2001-2011 Twisted Matrix Laboratories.
# See LICENSE for details.
"""
This module provides support for Twisted to be driven by the Qt mainloop.
In order to use this support, simply do the following::
| app = QApplication(sys.argv) # your code to init Qt
| import qt4reactor
| qt4reactor.install()
alternatively:
| from twisted.application import reactors
| reactors.installReactor('qt4')
Then use twisted.internet APIs as usual. The other methods here are not
intended to be called directly.
If you don't instantiate a QApplication or QCoreApplication prior to
installing the reactor, a QCoreApplication will be constructed
by the reactor. QCoreApplication does not require a GUI so trial testing
can occur normally.
Twisted can be initialized after QApplication.exec_() with a call to
reactor.runReturn(). calling reactor.stop() will unhook twisted but
leave your Qt application running
API Stability: stable
Maintainer: U{Glenn H Tarbox, PhD<mailto:glenn@tarbox.org>}
Previous maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
Original port to QT4: U{Gabe Rudy<mailto:rudy@goldenhelix.com>}
Subsequent port by therve
"""
import sys
import time
from zope.interface import implements
from twisted.internet.interfaces import IReactorFDSet
from twisted.python import log, runtime
from twisted.internet import posixbase
from twisted.python.runtime import platformType, platform
try:
from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
from PyQt4.QtCore import QEventLoop
except ImportError:
from PySide.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
from PySide.QtCore import QEventLoop
class TwistedSocketNotifier(QObject):
"""
Connection between an fd event and reader/writer callbacks.
"""
def __init__(self, parent, reactor, watcher, socketType):
QObject.__init__(self, parent)
self.reactor = reactor
self.watcher = watcher
fd = watcher.fileno()
self.notifier = QSocketNotifier(fd, socketType, parent)
self.notifier.setEnabled(True)
if socketType == QSocketNotifier.Read:
self.fn = self.read
else:
self.fn = self.write
QObject.connect(self.notifier, SIGNAL("activated(int)"), self.fn)
def shutdown(self):
self.notifier.setEnabled(False)
self.disconnect(self.notifier, SIGNAL("activated(int)"), self.fn)
self.fn = self.watcher = None
self.notifier.deleteLater()
self.deleteLater()
def read(self, fd):
if not self.watcher:
return
w = self.watcher
# doRead can cause self.shutdown to be called so keep a reference to self.watcher
def _read():
#Don't call me again, until the data has been read
self.notifier.setEnabled(False)
why = None
try:
why = w.doRead()
inRead = True
except:
inRead = False
log.err()
why = sys.exc_info()[1]
if why:
self.reactor._disconnectSelectable(w, why, inRead)
elif self.watcher:
self.notifier.setEnabled(True) # Re enable notification following sucessfull read
self.reactor._iterate(fromqt=True)
log.callWithLogger(w, _read)
def write(self, sock):
if not self.watcher:
return
w = self.watcher
def _write():
why = None
self.notifier.setEnabled(False)
try:
why = w.doWrite()
except:
log.err()
why = sys.exc_info()[1]
if why:
self.reactor._disconnectSelectable(w, why, False)
elif self.watcher:
self.notifier.setEnabled(True)
self.reactor._iterate(fromqt=True)
log.callWithLogger(w, _write)
class QtReactor(posixbase.PosixReactorBase):
implements(IReactorFDSet)
def __init__(self):
self._reads = {}
self._writes = {}
self._notifiers = {}
self._timer = QTimer()
self._timer.setSingleShot(True)
QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate)
if QCoreApplication.startingUp():
# Application Object has not been started yet
self.qApp=QCoreApplication([])
self._ownApp=True
else:
self.qApp = QCoreApplication.instance()
self._ownApp=False
self._blockApp = None
posixbase.PosixReactorBase.__init__(self)
def _add(self, xer, primary, type):
"""
Private method for adding a descriptor from the event loop.
It takes care of adding it if new or modifying it if already added
for another state (read -> read/write for example).
"""
if xer not in primary:
primary[xer] = TwistedSocketNotifier(None, self, xer, type)
def addReader(self, reader):
"""
Add a FileDescriptor for notification of data available to read.
"""
self._add(reader, self._reads, QSocketNotifier.Read)
def addWriter(self, writer):
"""
Add a FileDescriptor for notification of data available to write.
"""
self._add(writer, self._writes, QSocketNotifier.Write)
def _remove(self, xer, primary):
"""
Private method for removing a descriptor from the event loop.
It does the inverse job of _add, and also add a check in case of the fd
has gone away.
"""
if xer in primary:
notifier = primary.pop(xer)
notifier.shutdown()
def removeReader(self, reader):
"""
Remove a Selectable for notification of data available to read.
"""
self._remove(reader, self._reads)
def removeWriter(self, writer):
"""
Remove a Selectable for notification of data available to write.
"""
self._remove(writer, self._writes)
def removeAll(self):
"""
Remove all selectables, and return a list of them.
"""
rv = self._removeAll(self._reads, self._writes)
return rv
def getReaders(self):
return self._reads.keys()
def getWriters(self):
return self._writes.keys()
def callLater(self,howlong, *args, **kargs):
rval = super(QtReactor,self).callLater(howlong, *args, **kargs)
self.reactorInvocation()
return rval
def reactorInvocation(self):
self._timer.stop()
self._timer.setInterval(0)
self._timer.start()
def _iterate(self, delay=None, fromqt=False):
"""See twisted.internet.interfaces.IReactorCore.iterate.
"""
self.runUntilCurrent()
self.doIteration(delay, fromqt)
iterate = _iterate
def doIteration(self, delay=None, fromqt=False):
'This method is called by a Qt timer or by network activity on a file descriptor'
if not self.running and self._blockApp:
self._blockApp.quit()
self._timer.stop()
delay = max(delay, 1)
if not fromqt:
self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
if self.timeout() is None:
timeout = 0.1
elif self.timeout() == 0:
timeout = 0
else:
timeout = self.timeout()
self._timer.setInterval(timeout * 1000)
self._timer.start()
def runReturn(self, installSignalHandlers=True):
self.startRunning(installSignalHandlers=installSignalHandlers)
self.reactorInvocation()
def run(self, installSignalHandlers=True):
if self._ownApp:
self._blockApp = self.qApp
else:
self._blockApp = QEventLoop()
self.runReturn()
self._blockApp.exec_()
class QtEventReactor(QtReactor):
def __init__(self, *args, **kwargs):
self._events = {}
super(QtEventReactor, self).__init__()
def addEvent(self, event, fd, action):
"""
Add a new win32 event to the event loop.
"""
self._events[event] = (fd, action)
def removeEvent(self, event):
"""
Remove an event.
"""
if event in self._events:
del self._events[event]
def doEvents(self):
handles = self._events.keys()
if len(handles) > 0:
val = None
while val != WAIT_TIMEOUT:
val = MsgWaitForMultipleObjects(handles, 0, 0, QS_ALLINPUT | QS_ALLEVENTS)
if val >= WAIT_OBJECT_0 and val < WAIT_OBJECT_0 + len(handles):
event_id = handles[val - WAIT_OBJECT_0]
if event_id in self._events:
fd, action = self._events[event_id]
log.callWithLogger(fd, self._runAction, action, fd)
elif val == WAIT_TIMEOUT:
pass
else:
#print 'Got an unexpected return of %r' % val
return
def _runAction(self, action, fd):
try:
closed = getattr(fd, action)()
except:
closed = sys.exc_info()[1]
log.deferr()
if closed:
self._disconnectSelectable(fd, closed, action == 'doRead')
def timeout(self):
t = super(QtEventReactor, self).timeout()
return min(t, 0.01)
def iterate(self, delay=None):
"""See twisted.internet.interfaces.IReactorCore.iterate.
"""
self.runUntilCurrent()
self.doEvents()
self.doIteration(delay)
def posixinstall():
"""
Install the Qt reactor.
"""
p = QtReactor()
from twisted.internet.main import installReactor
installReactor(p)
def win32install():
"""
Install the Qt reactor.
"""
p = QtEventReactor()
from twisted.internet.main import installReactor
installReactor(p)
if runtime.platform.getType() == 'win32':
from win32event import CreateEvent, MsgWaitForMultipleObjects
from win32event import WAIT_OBJECT_0, WAIT_TIMEOUT, QS_ALLINPUT, QS_ALLEVENTS
install = win32install
else:
install = posixinstall
__all__ = ["install"]

View file

@ -0,0 +1,9 @@
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
# see LICENSE for details
from twisted.application.reactors import Reactor
qt4 = Reactor('qt4', 'qt4reactor', 'Qt4 integration reactor')
qt4bad = Reactor('qt4bad', 'qt4reactor_bad', 'Qt4 broken reactor')