diff --git a/TODO b/TODO index 38c46ad..be2733d 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ Bugs: -* memos need to test for max time * color swatch text doesnt disappear * X and _ buttons move around all crazy like diff --git a/irc.py b/irc.py index 1a87048..dc6ae08 100644 --- a/irc.py +++ b/irc.py @@ -1,9 +1,10 @@ from PyQt4 import QtGui, QtCore -from twisted.internet.protocol import ClientFactory -from twisted.words.protocols.irc import IRCClient -from twisted.internet import reactor +from oyoyo.client import IRCClient +from oyoyo.cmdhandler import DefaultCommandHandler +from oyoyo import helpers import logging import random +import socket from dataobjs import Mood, PesterProfile from generic import PesterList @@ -17,32 +18,31 @@ class PesterIRC(QtCore.QObject): self.config = config def IRCConnect(self): server = self.config.server() - 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() + 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 def closeConnection(self): - #logging.info("---> Logging on...") - # self.cli.close() - pass + self.cli.close() + def setConnectionBroken(self, broken=True): + self.brokenConnection = True @QtCore.pyqtSlot(PesterProfile) def getMood(self, *chums): - self.cli.getMood(*chums) + self.cli.command_handler.getMood(*chums) @QtCore.pyqtSlot(PesterList) def getMoods(self, chums): - self.cli.getMood(*chums) + self.cli.command_handler.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]) > CMD_LENGTH: - space = l[0].rfind(" ", 0,CMD_LENGTH) + if len(l[0]) > 400: + space = l[0].rfind(" ", 0,400) if space == -1: - space = CMD_LENGTH + space = 400 a = l[0][0:space] b = l[0][space:] if len(b) > 0: @@ -52,72 +52,117 @@ class PesterIRC(QtCore.QObject): else: return l textl = splittext(textl) - for t in textl: - self.cli.msg(h, t) + try: + for t in textl: + helpers.msg(self.cli, h, t) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString, bool) def startConvo(self, handle, initiated): h = unicode(handle) - if initiated: - self.cli.msg(h, "PESTERCHUM:BEGIN") - self.cli.msg(h, "COLOR >%s" % (self.mainwindow.profile().colorcmd())) + 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() @QtCore.pyqtSlot(QtCore.QString) def endConvo(self, handle): h = unicode(handle) - self.cli.msg(h, "PESTERCHUM:CEASE") + try: + helpers.msg(self.cli, h, "PESTERCHUM:CEASE") + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot() def updateProfile(self): me = self.mainwindow.profile() handle = me.handle - self.cli.setNick(handle) + try: + helpers.nick(self.cli, handle) + except socket.error: + self.setConnectionBroken() self.updateMood() @QtCore.pyqtSlot() def updateMood(self): me = self.mainwindow.profile() - self.cli.msg("#pesterchum", "MOOD >%d" % (me.mood.value())) + try: + helpers.msg(self.cli, "#pesterchum", "MOOD >%d" % (me.mood.value())) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot() def updateColor(self): me = self.mainwindow.profile() for h in self.mainwindow.convos.keys(): - self.cli.msg(h, "COLOR >%s" % (self.mainwindow.profile().colorcmd())) + try: + helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd())) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString) def blockedChum(self, handle): h = unicode(handle) - self.cli.msg(h, "PESTERCHUM:BLOCK") + try: + helpers.msg(self.cli, h, "PESTERCHUM:BLOCK") + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString) def unblockedChum(self, handle): h = unicode(handle) - self.cli.msg(h, "PESTERCHUM:UNBLOCK") + try: + helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK") + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString) def requestNames(self, channel): c = unicode(channel) - self.cli.sendMessage("NAMES", c) + try: + helpers.names(self.cli, c) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot() def requestChannelList(self): - self.cli.sendMessage("LIST") + try: + helpers.channel_list(self.cli) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString) def joinChannel(self, channel): c = unicode(channel) - self.cli.join(c) + try: + helpers.join(self.cli, c) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString) def leftChannel(self, channel): c = unicode(channel) - self.cli.leave(c) + try: + helpers.part(self.cli, c) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString, QtCore.QString) def kickUser(self, handle, channel): c = unicode(channel) h = unicode(handle) - self.cli.kick(h, c) + try: + helpers.kick(self.cli, h, c) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString) def setChannelMode(self, channel, mode, command): c = unicode(channel) - m = unicode(mode).replace("+", "") + m = unicode(mode) cmd = unicode(command) if cmd == "": cmd = None - self.cli.mode(c, True, m, cmd) + try: + helpers.mode(self.cli, c, m, cmd) + except socket.error: + self.setConnectionBroken() @QtCore.pyqtSlot() def reconnectIRC(self): - pass + self.setConnectionBroken() + + def updateIRC(self): + self.conn.next() moodUpdated = QtCore.pyqtSignal(QtCore.QString, Mood) colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor) @@ -129,48 +174,10 @@ class PesterIRC(QtCore.QObject): nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString) connected = QtCore.pyqtSignal() userPresentUpdate = QtCore.pyqtSignal(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) + QtCore.QString) +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 @@ -178,7 +185,7 @@ class PesterIRCClient(IRCClient): 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 >": @@ -191,7 +198,8 @@ class PesterIRCClient(IRCClient): mychumhandle = self.mainwindow.profile().handle mymood = self.mainwindow.profile().mood.value() if msg.find(mychumhandle, 8) != -1: - self.msg("#pesterchum", "MOOD >%d" % (mymood)) + helpers.msg(self.client, "#pesterchum", + "MOOD >%d" % (mymood)) elif chan[0] == '#': if msg[0:16] == "PESTERCHUM:TIME>": self.parent.timeCommand.emit(chan, handle, msg[16:]) @@ -212,88 +220,89 @@ class PesterIRCClient(IRCClient): self.parent.colorUpdated.emit(handle, color) else: self.parent.messageReceived.emit(handle, msg) - - def irc_ERR_NICKNAMEINUSE(self, prefix, params): - logging.info("---> recv NICKINUSE %s %s" % (prefix, params)) + + + 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): newnick = "pesterClient%d" % (random.randint(100,999)) - self.setNick(newnick) + helpers.nick(self.client, newnick) self.parent.nickCollision.emit(nick, newnick) - def userQuit(self, nick, reason): - logging.info("---> recv QUIT %s %s" % (nick, reason)) + def quit(self, nick, reason): handle = nick[0:nick.find("!")] self.parent.userPresentUpdate.emit(handle, "", "quit") self.parent.moodUpdated.emit(handle, Mood("offline")) - 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)) + def kick(self, opnick, channel, handle, op): + self.parent.userPresentUpdate.emit(handle, channel, "kick:%s" % (op)) # ok i shouldnt be overloading that but am lazy - def userLeft(self, nick, channel, reason="nanchos"): - logging.info("---> recv LEFT %s %s" % (nick, channel)) + def part(self, nick, channel, reason="nanchos"): handle = nick[0:nick.find("!")] self.parent.userPresentUpdate.emit(handle, channel, "left") if channel == "#pesterchum": self.parent.moodUpdated.emit(handle, Mood("offline")) - def userJoined(self, nick, channel): - logging.info("---> recv JOIN %s %s" % (nick, channel)) + def join(self, 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 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 + def mode(self, op, channel, mode, handle=""): self.parent.userPresentUpdate.emit(handle, channel, mode) - def userRenamed(self, oldnick, newnick): - logging.info("---> recv RENAME %s %s" % (oldnick, newnick)) + def nick(self, oldnick, newnick): + oldhandle = oldnick[0:oldnick.find("!")] newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb) - self.parent.moodUpdated.emit(oldnick, Mood("offline")) - self.parent.userPresentUpdate.emit("%s:%s" % (oldnick, newnick), "", "nick") + self.parent.moodUpdated.emit(oldhandle, Mood("offline")) + self.parent.userPresentUpdate.emit("%s:%s" % (oldhandle, newnick), "", "nick") if newnick in self.mainwindow.chumList.chums: self.getMood(newchum) - 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 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 = [] -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() + 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() + diff --git a/irc.pyc b/irc.pyc index a7a7116..301298b 100644 Binary files a/irc.pyc and b/irc.pyc differ diff --git a/menus.py b/menus.py index c6be3a1..e68aea9 100644 --- a/menus.py +++ b/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. 3.14 alpha 4") + self.setText("P3ST3RCHUM V. 3.1.4 alpha 4") self.setInformativeText("Programming by illuminatedwax (ghostDunk), art by Grimlive (aquaMarinist)") self.mainwindow = parent diff --git a/menus.pyc b/menus.pyc index 8a86c4c..23b319b 100644 Binary files a/menus.pyc and b/menus.pyc differ diff --git a/oyoyo/client.py b/oyoyo/client.py index fa60fa1..da5a716 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -29,8 +29,6 @@ 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): @@ -127,7 +125,18 @@ class IRCClient: msg = bytes(" ", "ascii").join(bargs) logging.info('---> send "%s"' % msg) - self.socket.send(msg + bytes("\r\n", "ascii")) + try: + self.socket.send(msg + bytes("\r\n", "ascii")) + except socket.error, se: + try: # a little dance of compatibility to get the errno + errno = e.errno + except AttributeError: + errno = e[0] + if not self.blocking and errno == 11: + print "O WELLS" + pass + else: + raise e def connect(self): """ initiates the connection to the server set in self.host:self.port @@ -157,16 +166,14 @@ class IRCClient: buffer = bytes() while not self._end: try: - #logfile.write("recv at %s\n" % datetime.now().strftime("%Y-%m-%d.%H.%M %S")) buffer += self.socket.recv(1024) - #logfile.write("recvd %s at %s\n" % (buffer, datetime.now().strftime("%Y-%m-%d.%H.%M %S"))) - #logfile.flush() except socket.error, e: try: # a little dance of compatibility to get the errno errno = e.errno except AttributeError: errno = e[0] if not self.blocking and errno == 11: + print "O WELLS" pass else: raise e diff --git a/oyoyo/client.pyc b/oyoyo/client.pyc index f289bd0..9cc2422 100644 Binary files a/oyoyo/client.pyc and b/oyoyo/client.pyc differ diff --git a/pesterchum.js b/pesterchum.js index 185587d..aaa058c 100644 --- a/pesterchum.js +++ b/pesterchum.js @@ -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": "testProfile", "block": []} \ No newline at end of file +{"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": []} \ No newline at end of file diff --git a/pesterchum.py b/pesterchum.py index c887918..f645fd5 100644 --- a/pesterchum.py +++ b/pesterchum.py @@ -21,6 +21,7 @@ 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): @@ -1656,6 +1657,26 @@ 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) @@ -1675,9 +1696,6 @@ 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: @@ -1726,7 +1744,9 @@ class MainProgram(QtCore.QObject): self.irc = PesterIRC(self.widget.config, self.widget) self.connectWidgets(self.irc, self.widget) - self.irc.IRCConnect() + self.ircapp = IRCThread(self.irc) + self.connect(self.ircapp, QtCore.SIGNAL('restartIRC()'), + self, QtCore.SLOT('restartIRC()')) def connectWidgets(self, irc, widget): irc.connect(widget, QtCore.SIGNAL('sendMessage(QString, QString)'), @@ -1846,6 +1866,7 @@ 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()')) diff --git a/qt4reactor/.gitignore b/qt4reactor/.gitignore deleted file mode 100644 index e1fb410..0000000 --- a/qt4reactor/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.[oa] -*~ -*.pyc -*.pyo -*.class -.project -.pydevproject -*.cache -_trial_temp diff --git a/qt4reactor/LICENSE b/qt4reactor/LICENSE deleted file mode 100644 index 5fc7ac7..0000000 --- a/qt4reactor/LICENSE +++ /dev/null @@ -1,57 +0,0 @@ -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. diff --git a/qt4reactor/README b/qt4reactor/README deleted file mode 100644 index e6b5fec..0000000 --- a/qt4reactor/README +++ /dev/null @@ -1,55 +0,0 @@ -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 diff --git a/qt4reactor/__init__.py b/qt4reactor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/qt4reactor/bin/gtrial b/qt4reactor/bin/gtrial deleted file mode 100755 index e0d899e..0000000 --- a/qt4reactor/bin/gtrial +++ /dev/null @@ -1,24 +0,0 @@ -#!/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() diff --git a/qt4reactor/ghtTests/buttonStress.py b/qt4reactor/ghtTests/buttonStress.py deleted file mode 100755 index 7b4ec54..0000000 --- a/qt4reactor/ghtTests/buttonStress.py +++ /dev/null @@ -1,57 +0,0 @@ -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?...') - - diff --git a/qt4reactor/ghtTests/fakeAppButtonStress.py b/qt4reactor/ghtTests/fakeAppButtonStress.py deleted file mode 100755 index d5a2977..0000000 --- a/qt4reactor/ghtTests/fakeAppButtonStress.py +++ /dev/null @@ -1,51 +0,0 @@ -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?...' - - diff --git a/qt4reactor/ghtTests/ircClient.py b/qt4reactor/ghtTests/ircClient.py deleted file mode 100644 index 9e6fdfb..0000000 --- a/qt4reactor/ghtTests/ircClient.py +++ /dev/null @@ -1,106 +0,0 @@ -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_()) diff --git a/qt4reactor/ghtTests/testIterate.py b/qt4reactor/ghtTests/testIterate.py deleted file mode 100755 index 261155b..0000000 --- a/qt4reactor/ghtTests/testIterate.py +++ /dev/null @@ -1,37 +0,0 @@ -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?...') - - diff --git a/qt4reactor/ghtTests/trivialscript.py b/qt4reactor/ghtTests/trivialscript.py deleted file mode 100755 index 2c015cc..0000000 --- a/qt4reactor/ghtTests/trivialscript.py +++ /dev/null @@ -1,25 +0,0 @@ -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_()) - diff --git a/qt4reactor/gtrial.py b/qt4reactor/gtrial.py deleted file mode 100755 index 704a2f4..0000000 --- a/qt4reactor/gtrial.py +++ /dev/null @@ -1,41 +0,0 @@ -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?...' - - diff --git a/qt4reactor/qt4reactor.py b/qt4reactor/qt4reactor.py deleted file mode 100644 index eff7e2e..0000000 --- a/qt4reactor/qt4reactor.py +++ /dev/null @@ -1,359 +0,0 @@ -# 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"] - diff --git a/qt4reactor/twisted/plugins/qt4.py b/qt4reactor/twisted/plugins/qt4.py deleted file mode 100644 index 0f380b5..0000000 --- a/qt4reactor/twisted/plugins/qt4.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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') -