Seite 1 von 1

QString und socket

Verfasst: Dienstag 14. Juli 2009, 19:19
von Klip
Hallo zusammen,

ich arbeite im Moment ein wenig mit PyQt4 und bin auf ein Problem gestoßen. Ich erhalte über das socket-Modul einen Python-String und möchte diesen in einen QString konvertieren und in einem QTextEdit anzeigen lassen.

Leider gibt es dabei Probleme.

Code: Alles auswählen

message = self.socket.recv(2048)
print "Recv:", message # !!!
message = QtCore.QString(message)
print message
self.gui.messageField.append(message)

Code: Alles auswählen

Sent: asdfasdfa
Recv: asdfasdfa
a   # --> nach Konvertierung
message kommt an und enthält den Text, der abgeschickt wurde. Sobald ich ihn in einen QString konvertiere (hier als Zwischenschritt um das Ergebnis anzeigen zu können), enthält dieser QString nur das erste Zeichen von message, der Rest existiert nicht.

Code: Alles auswählen

message = QtCore.QString(unicode(message))
So wird der String zwar scheinbar korrekt konvertiert. Im Interpreter ist der String vollständig lesbar, aber bei der Anzeige im QTextEdit sind hinter jedem Buchstaben kleine Kästchen zu sehen, die da nicht hingehören.

Was läuft hier schief? Wäre sehr dankbar für Hinweise.

Verfasst: Dienstag 14. Juli 2009, 21:50
von lunar
Irgendwo liegt ein Kodierungsfehler vor. Wo genau, lässt sich angesichts der bruchstückhaften Informationen kaum sagen. Es lässt sich allenfalls der Tipp geben, "repr()" zur Ausgabe zu verwenden, diese Funktion zeigt dir die tatsächlichen Werte in der Zeichenkette an, und nicht das, was dein Terminal macht.

Verfasst: Mittwoch 15. Juli 2009, 15:12
von Klip
Hi lunar,

Code: Alles auswählen

message = self.socket.recv()
print "Recv:", repr(message)
repr() spuckt mir folgendes aus:

Code: Alles auswählen

Verschickter String: fwefweqf
Recv: 'f\x00w\x00e\x00f\x00w\x00e\x00q\x00f\x00'
Beste Grüße

klip

Verfasst: Mittwoch 15. Juli 2009, 17:34
von lunar
Und woher kommen die Nullbytes in dieser Zeichenkette?

Verfasst: Mittwoch 15. Juli 2009, 18:11
von Klip
Sooo, ich bin hinter die Ursache des Problems gekommen.

Offenbar hat das socket-Modul Probleme, QStrings über send() zu verschicken. Wandelt man den QString explizit in einen Python-String um, funktioniert alles reibungslos.
Nach dem Empfangen lässt man den Python-String von PyQt wieder in einen QString casten und kann ihn in der GUI verwenden.

Code: Alles auswählen

self.socket.send(str(self.lineEdt.text()))  # QTextEdit
Daher kamen auch die Nullbytes. Danke lunar =)

Verfasst: Mittwoch 15. Juli 2009, 18:33
von lunar
Klip hat geschrieben:Offenbar hat das socket-Modul Probleme, QStrings über send() zu verschicken.
Das "socket"-Modul arbeitet mit Bytes. Wenn du versuchst, Zeichendatentypen wie QString oder unicode direkt an die Funktionen dieses Moduls zu übergeben, ist das vor allem dein Fehler. Undefinierte Resultate sind da zu erwarten.

Deine jetzige Lösung ist nur wenig besser, weil sie implizit "ascii" zur Kodierung der Zeichen verwendet. Versuch doch mal, Umlaute mit diesem Code zu verschicken ...

Verfasst: Mittwoch 15. Juli 2009, 19:06
von Klip
Ja, stimmt =/

Code: Alles auswählen

Traceback (most recent call last):
  File "./scales.py", line 75, in sendMessage
    self.client.send(str(self.lineEdt.text()))
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
Tja, das ist ein Problem... was kann ich dagegen tun?

Hier ein wenig mehr Code...

Client (Qt-GUI), verschickt einen String aus einem QLineEdit.
self.client ist das socket der GUI.

Code: Alles auswählen

    def sendMessage(self):
        self.client.send(str(self.lineEdt.text()))
        print "Sent:", repr(str(self.lineEdt.text()))
        self.lineEdt.clear()
Client-Thread (lauscht im Hintergrund, ob etwas empfangen wurde und schreibt empfangene Daten ins QTextEdit):

Code: Alles auswählen

class ChatclientThread(Thread):
    def __init__(self, gui, socket):
        Thread.__init__(self)
        self.gui = gui
        self.socket = socket
        
    def run(self):
        while True:
            message = self.socket.recv(1024)
            print "Recv:", repr(message)
            self.gui.messageField.append(message)
Server-Thread: Der String wird an alle Clients geschickt.

Code: Alles auswählen

class ConnThread(Thread):
    def __init__(self, connection):
        Thread.__init__(self)
        self.connection = connection
        
    def run(self):
        self.handler()
        
    def handler(self):
        while True:
            data = self.connection.recv(1024)
            if not data: 
                break
                
            print "Recv:", repr(data)
                
            # Send to all clients
            for i in Server.allClients:
                i.send(str(data))
Soweit funktioniert das Ganze nun. Tippe ich einen String in den Client (QLineEdit) und schicke diesen ab, erhalte ich denselben String zurück. Leider klappt das wie lunar angesprochen hat nicht mit Umlauten.

Experimente mit decode('utf-8') schlugen fehl. Ich komme ehrlich gesagt nicht weiter.
Muss ich den String vorher von Hand in Bytes umwandeln und daraus nach Empfangen wieder einen String basteln?

Vielen Dank soweit schonmal!

Verfasst: Mittwoch 15. Juli 2009, 20:16
von lunar
Klip hat geschrieben:Muss ich den String vorher von Hand in Bytes umwandeln und daraus nach Empfangen wieder einen String basteln?
Ja, natürlich. Wenn du das "socket"-Modul verwendet willst, mittels "unicode(qstring).encode(…)", alternativ kannst du auch "QTcpSocket" nutzen und über einen "QTextStream" ansprechen.

Verfasst: Mittwoch 15. Juli 2009, 21:19
von Klip
Das war's, jetzt klappt es einwandfrei!

Vielen Dank lunar und sorry für die schwere Geburt.

angepasst:

Code: Alles auswählen

class ChatclientThread(Thread):
    def __init__(self, gui, socket):
        Thread.__init__(self)
        self.gui = gui
        self.socket = socket
        
    def run(self):
        while True:
            message = self.socket.recv()
            self.gui.messageField.append(message.decode('utf-8'))

Code: Alles auswählen

    def sendMessage(self):
        self.client.send(unicode(self.lineEdt.text()).encode('utf-8'))
        self.lineEdt.clear()
Ich hatte mich seinerzeit für das socket-Modul entschieden weil ich zu QNetwork keine Tutorials gefunden habe und das mein erster Kontakt mit sockets ist.

Vielleicht schreibe ich den Kram nun auf QNetwork um, wäre für die Streams ziemlich praktisch. Muss mich da erstmal weiter reinlesen.