QString und socket

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Klip
User
Beiträge: 98
Registriert: Donnerstag 10. August 2006, 20:39

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.
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.
Benutzeravatar
Klip
User
Beiträge: 98
Registriert: Donnerstag 10. August 2006, 20:39

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
lunar

Und woher kommen die Nullbytes in dieser Zeichenkette?
Benutzeravatar
Klip
User
Beiträge: 98
Registriert: Donnerstag 10. August 2006, 20:39

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 =)
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 ...
Benutzeravatar
Klip
User
Beiträge: 98
Registriert: Donnerstag 10. August 2006, 20:39

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!
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.
Benutzeravatar
Klip
User
Beiträge: 98
Registriert: Donnerstag 10. August 2006, 20:39

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.
Antworten