Tutorial zu Qt und Sockets

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

Hallo

kennt jemand ein gutes (wenn möglich deutsches - muss aber nicht) Tutorial, in dem man die Benutzung von Qt und sockets, sprich die Kombination aus GUI- und Netzwerk-Programmierung lernen kann. Ich möchte gerne einen Chat-Client schreiben, der allerdings über eine GUI verfügen soll. Den Server dazu habe ich bereits geschrieben, allerdings ohne GUI (was ich auch nicht vorhabe zu ändern). Mein Problem liegt nur darin, dass wenn der Server eine ankommende Nachricht verarbeitet und weiterschickt, ich nicht weiß wie ich gleichzeitig ein Fenster laufen lassen und das socket abhören soll. Dafür brauche ich doch QtNetwork oder nicht?

Kann mir da jemand helfen?
Grüße

P.S. kann man in diesem Forum seinen Namen ändern, meiner ist nicht mehr aktuell. ;-)
lunar

Lies doch einfach mal die Dokumentation zu den Klassen im QtNetwork-Modul. So schwierig ist das eigentlich nicht, dass man dafür ein Tutorial bräuchte. In der Qt-Dokumentation (nicht PyQt!) gibt es allerdings auch ein Beispiel, welches clientseitige Sockets erklärt.

Zur Namensänderung musst Du einfach nur einen der Moderatoren oder Administratoren anschreiben, und ihn um eine Änderung bitten.
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

Hab mir mal die Qt-Dokumentation angeguckt. In dem getFortune-client-Beispiel http://doc.qt.nokia.com/4.2/network-fortuneclient.html kommt nun der ">>" Operator von c++ vor.
Welche Entsprechung hat der in Python? Reicht da readBytes()?

Um mich zurecht zu finden versuche ich gerade einen einfachen Client zu schreiben, der nur einen String verschickt und diesen String in ein label schreibt und beim empfangen den empfangenen Text in ein zweites label_2 schreibt. Das verschicken geschieht über das eintragen in ein lineEdit und returnPressed. Kann mir jemand sagen, ob folgende Klasse dafür ausreicht:

Code: Alles auswählen

class tcpClient(QtGui.QDialog, Ui_Dialog):
	def __init__(self):
		QtGui.QDialog.__init__(self)
		self.setupUi(self)
		
		self.connect(self.lineEdit, QtCore.SIGNAL("returnPressed()"), self, QtCore.SLOT(self.submit()))
	
		self.socket = QtNetwork.QTcpSocket(self)
		self.socket.connectToHost("127.0.0.1", 6000)
		self.connect(socket, QtCore.SIGNAL("readyRead()"),self,QtCore.SLOT(self.incomingMsg()))

	def submit(self):
		msg = self.lineEdit.text()
		self.socket.send(msg)
		self.label.setText(msg)
		self.lineEdit.setText("")
	
	def incomingMsg(self):
		self.stream = QtCore.QDataStream(self.socket)
		self.incoming = self.stream.readBytes()
		self.label_2.setText(self.incoming)
lunar

Der Operator ist der Streaming-Operator, der eine Ausgabe oder eine Eingabe vornimmt (was Google Dir auch verraten hätte). Die Entsprechung in Python sind die "read*" und "write*" Methoden von QAbstractSocket und QIODevice.

Um zu wissen, ob der Quelltext "ausreicht", musst Du ihn eben testen :) Btw, es gibt eine neuere, schönere API für Signale und Slots, lies mal den Reference Guide.
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

lunar hat geschrieben:Die Entsprechung in Python sind die "read*" und "write*" Methoden von QAbstractSocket und QIODevice.
Also mache ich aus

Code: Alles auswählen

self.socket.send(msg)

Code: Alles auswählen

self.socket.write(msg)
Aber als Parameter erwartet der einen QByteArray. Wie mache ich aus einem String einen solchen?
lunar hat geschrieben: Btw, es gibt eine neuere, schönere API für Signale und Slots, lies mal den Reference Guide.
also so:

Code: Alles auswählen

self.connect(self.lineEdit, QtCore.SIGNAL("returnPressed()"), self.submit)
BlackJack

@usingKarmicKoala: Ich denke es war eher so etwas hier gemeint (ungetestet):

Code: Alles auswählen

self.lineEdit.returnPressed.connect(self.submit)
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

Ja hab die neue Schreibweise übernommen. funktioniert.

Allerdings ist bei mit self.incoming in incomingMsg() leer. warum?
BlackJack

@usingKarmicKoala: Weil `readBytes()` nicht das macht was Du anscheinend denkst. Wenn das funktionieren soll, wäre es vielleicht sinnvoll auch beim Senden über die Abstraktion eines `QDataStream` zu gehen.
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

So habs nun geschafft. Danke an meine beiden Helfer. Ich poste der Vollständigkeit halber mal den richtigen Quelltext:

Code: Alles auswählen

class tcpClient(QtGui.QDialog, Ui_Dialog):
	def __init__(self):
		QtGui.QDialog.__init__(self)
		self.setupUi(self)
		
		self.lineEdit.returnPressed.connect(self.submit)
	
		self.sock = QtNetwork.QTcpSocket(self)
		self.sock.connectToHost("127.0.0.1", 6000)
		self.sock.readyRead.connect(self.incomingMsg)

	def submit(self):
		msg = self.lineEdit.text()
		self.sock.write(msg.toAscii())
		self.label.setText(msg)
		self.lineEdit.setText("")
	
	def incomingMsg(self):
		self.incoming = str(self.sock.readAll())
		print self.incoming
		self.label_2.setText(self.incoming)
lunar

Falls es Dich interessiert, so habe ich auch ein Beispiel für die Verwendung von Sockets in Qt. Das Beispiel ist allerdings für Python 3 geschrieben, da die Qt-API bei dieser Version angenehmer ist (v.a. weil QString automatisch in str umgewandelt wird).
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

danke für das Beispiel aber dafür brauch ich ein wenig um das nachzuvollziehen.
aber ich habe noch eine frage:
wenn ich einen QTcpServer und einen client mit QTcpSocket schreibe und der server soll am anfang darauf warten, dass der client ihm seinen namen (also einen string) schickt. muss ich dann im server folgendes schreiben um darauf zu warten, dass der name geschickt wird:

Code: Alles auswählen

while not socket.waitForReadyRead():
       continue
?
lunar

Das kommt auf Deinen Serverquelltext an. Dieser Aufruf blockiert den gesamten Server, funktioniert also nur, wenn Du jede Verbindung in einem Thread laufen lässt.
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

ja gut das mache ich nicht.. wie kann ich denn ansonsten darauf warten eine nachricht zu bekommen? oder muss ich das dann in einen thread schreiben?

EDIT: aber im prinzip blockt das den server doch nur bis ich den namen bekomme. dann gehts zum slot, das für readyRead verantwortlich ist oder seh ich das falsch?
lunar

Wieso willst Du denn unbedingt blockierend warten? Du kannst doch den Namen auch einfach asynchron empfangen ...

Was die Blockade angeht, so dauert sie natürlich nur bis zum Empfang des Namens. Aber wie lange das dauert, ist eben von vorne herein nicht klar, und während dieser Zeit kann der Server keine weiteren Verbindungen annehmen.
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

alles klar ich halte mich jetzt an deinen Beispiel-Quelltext.
aber wenn ich versuche den auszuführen sagt die konsole mir:

Code: Alles auswählen

ImportError: No module named PyQt4.QtCore
was hab ich nicht installiert? mit python (ohne 3) war das kein problem
3ff
User
Beiträge: 191
Registriert: Dienstag 22. Dezember 2009, 12:54
Wohnort: Odenwald Sued-Hessen

@usingKarmicKoala
das passiert mir dauernd. Das liegt daran, da Du die falschen Libraries importiert hast bzw. die Qt4 Version sich irgendwo beißt.
--------------------------------------------------------
Dein Ausgangsproblem mit dem Tutorium in PyQt4 hatte ich auch. riverbankcomputing hilft nicht weiter.
Die sind gewissermassen der Taxifahrer. Der Fahrgast( und Bezahler) ist wohl Nokia.
Ich reg mich nicht mehr darüber auf, das die Beispiele zur PyQt.Library in Cpp sind.
Selbst gute Tutorien zu PyQt4 sind sehr selten zu finden. Das scheint Nokia alles nicht zu interessieren.
Ich bin in Qt4 und den Programmgenerator Pyuic4 hineingestossen worden.
Mit welchem Linux arbeitest Du? oder mit Windows?
Ich hab Ubuntu drauf und die jeweils neueste Version von Python ist immer auf dem Spiegelserver.
Mit Synaptic oder apt-get gibts immer die neueste Version, die wird soford eingebunden und fertig.
Der deutsche Spiegelserver steht an der FU Berlin, aber das ist unerheblich. Da kommt der code schon "vorverdaut"runter.
Manchmal kommen die Programme auch aus England aus Kent.
Den Tarball auszupacken ist sogar gefährlich, weil es dort zig Comileroptionen gibt.
Ubuntu gibts auch im Netz. ich bin damit sehr zufrieden.
Ich rate Dir: Datensicherheit und backups sind erste Bürgerpflicht täglich.
Guude
Fritz :D
ichisich
User
Beiträge: 134
Registriert: Freitag 1. Januar 2010, 11:52

Hi,

Du scheinst die Art von Import zu nutzen:

Code: Alles auswählen

import PyQt4
# und vor jede Instanziierung:
einString = QtCore.QString()
#oder
mainWindow = QtGui.Mainwindow()
#sowas schreiben !?
Herr Lunar macht aber:

Code: Alles auswählen

from PyQt4.QtCore import pyqtSignal, QObject, QTextStream, QTextCodec
from PyQt4.QtGui import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                         QLineEdit, QPlainTextEdit, QAction, QStyle)
from PyQt4.QtNetwork import QTcpSocket, QTcpServer, QHostAddress

#die instanziierung wäre dann:
mainWindow = QMainwindow()
.
.
.
Kann das sein das Du das gemischt hast ?
Ansonsten gib mal mehr Quelltext mit den Imports etc.
3ff
User
Beiträge: 191
Registriert: Dienstag 22. Dezember 2009, 12:54
Wohnort: Odenwald Sued-Hessen

@ichisich,
gut möglich.
Ich ahb mir das Programm von Lunar runtergezogen es läuft.
Ein generelle Problem sind die Programmfragmente. Das ist aber kein Qt Poblem. Das gibts auch in C oder Pascal. WEnn man die Randbedingungen nicht mit angibt, gibts probleme.
Bei Programmfragmenten muß man vorsichtig sein.
Guude
Fritz :shock:
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

also erstens:
ich benutze ubuntu lucid (deswegen ist mein username vll etwas veraltet) und habe ganz frisch python3 installiert

zweitens:
ich wollte nur den quelltext von lunar ausführen, hab da am import nichts geändert!
usingKarmicKoala
User
Beiträge: 27
Registriert: Samstag 28. November 2009, 15:58

aber allgemein was das thema des threads angeht:
am besten lernt man programmieren immernoch durch das angucken von gut kommentierten quelltexten!!!
Antworten