failed twisted integration....just stick w/oyoyo for now
This commit is contained in:
parent
666c2b88c0
commit
5de02c65b5
33 changed files with 984 additions and 181 deletions
1
TODO
1
TODO
|
@ -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
BIN
convo.pyc
Binary file not shown.
BIN
dataobjs.pyc
BIN
dataobjs.pyc
Binary file not shown.
BIN
generic.pyc
BIN
generic.pyc
Binary file not shown.
289
irc.py
289
irc.py
|
@ -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()
|
||||
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()
|
||||
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)
|
||||
|
@ -176,8 +131,46 @@ class PesterIRC(QtCore.QObject):
|
|||
userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString,
|
||||
QtCore.QString)
|
||||
|
||||
class PesterHandler(DefaultCommandHandler):
|
||||
|
||||
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)
|
||||
|
||||
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:])
|
||||
|
@ -221,88 +213,87 @@ class PesterHandler(DefaultCommandHandler):
|
|||
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 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 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 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
BIN
irc.pyc
Binary file not shown.
BIN
memos.pyc
BIN
memos.pyc
Binary file not shown.
2
menus.py
2
menus.py
|
@ -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
BIN
menus.pyc
Binary file not shown.
BIN
mispeller.pyc
BIN
mispeller.pyc
Binary file not shown.
Binary file not shown.
|
@ -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):
|
||||
|
|
BIN
oyoyo/client.pyc
BIN
oyoyo/client.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
oyoyo/parse.pyc
BIN
oyoyo/parse.pyc
Binary file not shown.
BIN
parsetools.pyc
BIN
parsetools.pyc
Binary file not shown.
|
@ -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": []}
|
|
@ -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
9
qt4reactor/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
*.[oa]
|
||||
*~
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.class
|
||||
.project
|
||||
.pydevproject
|
||||
*.cache
|
||||
_trial_temp
|
57
qt4reactor/LICENSE
Normal file
57
qt4reactor/LICENSE
Normal 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
55
qt4reactor/README
Normal 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
0
qt4reactor/__init__.py
Normal file
24
qt4reactor/bin/gtrial
Executable file
24
qt4reactor/bin/gtrial
Executable 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()
|
57
qt4reactor/ghtTests/buttonStress.py
Executable file
57
qt4reactor/ghtTests/buttonStress.py
Executable 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?...')
|
||||
|
||||
|
51
qt4reactor/ghtTests/fakeAppButtonStress.py
Executable file
51
qt4reactor/ghtTests/fakeAppButtonStress.py
Executable 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?...'
|
||||
|
||||
|
106
qt4reactor/ghtTests/ircClient.py
Normal file
106
qt4reactor/ghtTests/ircClient.py
Normal 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_())
|
37
qt4reactor/ghtTests/testIterate.py
Executable file
37
qt4reactor/ghtTests/testIterate.py
Executable 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?...')
|
||||
|
||||
|
25
qt4reactor/ghtTests/trivialscript.py
Executable file
25
qt4reactor/ghtTests/trivialscript.py
Executable 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
41
qt4reactor/gtrial.py
Executable 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
359
qt4reactor/qt4reactor.py
Normal 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"]
|
||||
|
9
qt4reactor/twisted/plugins/qt4.py
Normal file
9
qt4reactor/twisted/plugins/qt4.py
Normal 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')
|
||||
|
Loading…
Reference in a new issue