PDA

View Full Version : Python + PyQt IRC Client



crisp
25th January 2009, 05:42
Hi all, I've been pulling my hair out for a while now and I hope someone here can help me. I'm working with PyQt (4) and Python 2.5, in a Linux environment. My aim is to have a basic Qt based IRC client. I'm aware of the IRC RFC and the ins and outs of that etc, so I'm not to worried there.

I thought the best way to do it would be like: [IRC framework/API]--->[something that links the two]<---[Qt GUI code], although I'm starting to have my doubts now.

The GUI I've built with Qt Designer, and isn't much of a problem. The API type module for IRC I've started mainly works (infact as a starting point I've borrowed it from elsewhere on the net and tweaked it to my needs), as in I can have an IRC object connect to a server, send messages, etc via the command line. It's the linking of the two I'm worried about - I was going for total seperation between presentation and underlying mechanics as far as is possible. I have many versions of the above scenario from hours of trying, but I'll go ahead and paste the code for the 3 main files described so far.

This is the 'API':



import sys
import socket
import string
import threading
import time
import Queue

"""
class ReadFromServer():

def loop(self):
while 1:
try:
data = self.irc.recv(4096)
if data.find('PING') != -1:
self.irc.send('PONG ' + data.split() [1] + '\r\n')
if len(data) > 0:
print data
time.sleep(0.1)
except:
print 'Error: Possible interupt.'
break
"""

""" Set up buffer for incoming data """
incomingBuffer = Queue.Queue(5000)

class IRC:

recv_function = ''
irc = ''

def __init__(self):
self.irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.recv_function = self.recv

def recvLoop(self):
while 1:
try:
data = self.irc.recv(4096)
if data.find('PING') != -1:
self.irc.send('PONG ' + data.split() [1] + '\r\n')
#if data.find('hi') != -1:
#client.channelSend('#crasp', 'hai2u :)')
if len(data) > 0:
incomingBuffer.put(data)
print data
time.sleep(0.1)
except:
print 'Error: Possible interupt.'
break

def newConnection(self, server, port, nick, ident, realName):
self.server = server
self.port = port
self.nick = nick
self.ident = ident
self.realName = realName

self.irc.connect ( ( self.server, self.port ) )
self.rawSend("NICK %s\r\n" % self.nick)
self.rawSend("USER %s %s unused :%s\r\n" % (self.ident, self.server, self.realName))

def quit(self):
self.rawSend('QUIT :')

def part(self):
self.rawSend('PART :')

def join(self, channel):
self.rawSend('JOIN ' + channel)

def channelSend(self, channel, text):
self.rawSend('PRIVMSG ' + channel + ' :' + text)

def set_recv_function(self, f):
self.recv_function = f

def recv(self, s):
# default receive function
print s

def rawSend(self, s):
self.irc.send(s + '\r\n');

def my_receive(s):
print s
"""
client = IRC()
client.newConnection('irc.oftc.net', 6667, 'crispycream', 'crisp', 'crisp')
client.join('#crasp')
client.channelSend('#crasp', 'blah')
client.set_recv_function(my_receive)
client.recvLoop()

while 1:
if client.incomingBuffer.qsize() > 0:
item = client.incomingBuffer.get()
print item
client.incomingBuffer.task_done()
"""
This is the Qt Designed code:



# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'crispircmainwindow.ui'
#
# Created: Sun Jan 25 04:03:58 2009
# by: PyQt4 UI code generator 4.4.4
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

class Ui_CrispIRCMainWindow(object):
def setupUi(self, CrispIRCMainWindow):
CrispIRCMainWindow.setObjectName("CrispIRCMainWindow")
CrispIRCMainWindow.resize(531, 479)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(CrispIRCMainWindow.si zePolicy().hasHeightForWidth())
CrispIRCMainWindow.setSizePolicy(sizePolicy)
self.centralwidget = QtGui.QWidget(CrispIRCMainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = QtGui.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(0, 0, 531, 431))
self.widget.setObjectName("widget")
self.gridLayout = QtGui.QGridLayout(self.widget)
self.gridLayout.setObjectName("gridLayout")
self.displayBrowser = QtGui.QTextBrowser(self.widget)
self.displayBrowser.setObjectName("displayBrowser")
self.gridLayout.addWidget(self.displayBrowser, 0, 0, 1, 2)
self.connectButton = QtGui.QPushButton(self.widget)
self.connectButton.setObjectName("connectButton")
self.gridLayout.addWidget(self.connectButton, 1, 0, 1, 1)
self.disconnectButton = QtGui.QPushButton(self.widget)
self.disconnectButton.setObjectName("disconnectButton")
self.gridLayout.addWidget(self.disconnectButton, 1, 1, 1, 1)
CrispIRCMainWindow.setCentralWidget(self.centralwi dget)
self.statusbar = QtGui.QStatusBar(CrispIRCMainWindow)
self.statusbar.setObjectName("statusbar")
CrispIRCMainWindow.setStatusBar(self.statusbar)
self.menubar = QtGui.QMenuBar(CrispIRCMainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 531, 25))
self.menubar.setObjectName("menubar")
self.menuFile = QtGui.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menu_Edit = QtGui.QMenu(self.menubar)
self.menu_Edit.setObjectName("menu_Edit")
CrispIRCMainWindow.setMenuBar(self.menubar)
self.actionBlah = QtGui.QAction(CrispIRCMainWindow)
self.actionBlah.setObjectName("actionBlah")
self.actionBlaaah = QtGui.QAction(CrispIRCMainWindow)
self.actionBlaaah.setObjectName("actionBlaaah")
self.action_Preferences = QtGui.QAction(CrispIRCMainWindow)
self.action_Preferences.setObjectName("action_Preferences")
self.menuFile.addAction(self.actionBlah)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionBlaaah)
self.menu_Edit.addAction(self.action_Preferences)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menu_Edit.menuAction() )

self.retranslateUi(CrispIRCMainWindow)
QtCore.QMetaObject.connectSlotsByName(CrispIRCMain Window)

def retranslateUi(self, CrispIRCMainWindow):
CrispIRCMainWindow.setWindowTitle(QtGui.QApplicati on.translate("CrispIRCMainWindow", "Crisp IRC", None, QtGui.QApplication.UnicodeUTF8))
self.connectButton.setText(QtGui.QApplication.tran slate("CrispIRCMainWindow", "Connect", None, QtGui.QApplication.UnicodeUTF8))
self.disconnectButton.setText(QtGui.QApplication.t ranslate("CrispIRCMainWindow", "Disconnect", None, QtGui.QApplication.UnicodeUTF8))
self.menuFile.setTitle(QtGui.QApplication.translat e("CrispIRCMainWindow", "&File", None, QtGui.QApplication.UnicodeUTF8))
self.menu_Edit.setTitle(QtGui.QApplication.transla te("CrispIRCMainWindow", "&Edit", None, QtGui.QApplication.UnicodeUTF8))
self.actionBlah.setText(QtGui.QApplication.transla te("CrispIRCMainWindow", "Blah", None, QtGui.QApplication.UnicodeUTF8))
self.actionBlaaah.setText(QtGui.QApplication.trans late("CrispIRCMainWindow", "Blaaah", None, QtGui.QApplication.UnicodeUTF8))
self.action_Preferences.setText(QtGui.QApplication .translate("CrispIRCMainWindow", "&Preferences", None, QtGui.QApplication.UnicodeUTF8))
And finally, crucially, this is the code that attempts to link the two together.



from PyQt4.QtCore import *
from PyQt4.QtGui import *

import time

import ui_crispircmainwindow
import crisp_irc

MAC = "qt_mac_set_native_menubar" in dir()

class CrispIRCMainWindow(QMainWindow, ui_crispircmainwindow.Ui_CrispIRCMainWindow, crisp_irc.IRC):

def __init__(self, parent=None):
super(CrispIRCMainWindow, self).__init__(parent)
self.__index = 0
self.setupUi(self)

@pyqtSignature("")
def on_connectButton_clicked(self):
self.displayBrowser.append('Connecting...')
client = crisp_irc.IRC()
client.newConnection('irc.oftc.net', 6667, 'crispycream', 'crisp', 'crisp')
client.join('#crasp')
client.channelSend('#crasp', 'blah')
client.set_recv_function(crisp_irc.my_receive)
client.recvLoop()

if crisp_irc.incomingBuffer.qsize() > 0:
item = crisp_irc.incomingBuffer.get()
print item
crisp_irc.incomingBuffer.task_done()

#if irc._incomingBuffer.qsize() > 0:
# self.displayBrowser.append('BUFFER LARGER THAN ZERO')

@pyqtSignature("")
def on_disconnectButton_clicked(self):
self.displayBrowser.append('(Not really) Quitting...')

if __name__ == "__main__":
import sys

app = QApplication(sys.argv)
form = CrispIRCMainWindow()

form.show()
app.exec_()

crisp
25th January 2009, 05:43
Apologies for all that. Admitedly my understanding of OOP could be a little stronger, but the main problem I'm running into is that when I run that last lot of code, stuff connects in the background, but then the Connect button isn't 'released' and the GUI feeezes, forcing me to kill the process. I believe the root cause of this is the infinite loop I have intentionally created to keep checking the socket for incoming data. I'm not really sure of a better way to go about this.

What I'm really after is when I hit connect, all of the stuff that happens in the console in the background, happens in the main textBrowser in the GUI. Hitting disconnect to disconnect would be an added bonus :) If I can get over this barrier, I believe I'll be able to get a lot further with this. This is the first time I've done any meaningful GUI programming (I got my feet wet with a PyQt calculator app) so there may be something/s I've overlooked.

If anyone has a better suggestion as to how to do things then I'm all ears. I've tried using threads too. If it's not clear what I mean, then this image might help you visualise the problem: http://www.drivers-som.com/Qt/pyqt_irc.png Stuff in the console should go in the GUI :)

Apologies for the long post, but if you need any more information just ask and I'll provide. Thanks.