QString im Unicode?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich weiß, dass dieses leidige Thema schon mehrfach behandelt wurde, jedoch steige ich einfach nicht dahinter, wie man mit dem unicode - decode und encode - hantieren soll. Ich habe schon einiges gelesen, aber dennoch komme ich einfach nicht dahinter. In meinem Fall wird eine File Dialog geöffnet. Es gibt ja auch Pfade mit Umlaute oder Zeichen, die außerhalb von ASCII-Zeichen sind. Ich ging davon aus, dass das Rahmenwerk QT (QFileDialog) sich um solche Belange kümmert.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PyQt4.QtGui import *
from PyQt4.QtCore import *
from os.path import expanduser
import sys

def get_new_path():

    QMessageBox.about(None, 'No folder', "Nur ein Text")
    folder = QFileDialog.getExistingDirectory(None, "Open folder",
                                              expanduser("~"),
    QFileDialog.ShowDirsOnly)
    return folder
    
def show_path():
    folder_path = get_new_path()
    print folder_path
    
if __name__ == "__main__":
    app = QApplication(sys.argv)
    show_path()
    sys.exit(app.exec_())

Führe ich dieses ausführbare Programm aus, dann erhalte ich folgende Fehlermeldung:
AttributeError: 'QString' object has no attribute 'decode'
Ich bewege also mit meinem Pfad außerhalb des ASCII-Zeichen. Nun eine Frage an dieser Stelle. Was genau muss ich machen? Ich habe schon versucht die Variable folder_path mit der encode()-Methode und mit der decode()-Methode zu behandeln. QStrings haben solche Methoden nicht. Oder brauche ich diese Kodierung gar nicht, wenn ich die Variable folder_path zum Inhalt von lineEdit mache? Also, den Pfad über lineEdit ausgeben.

Ich freue mich über konkrete Vorschläge. Vielleicht kann es mir mal jemand an Hand meines Beispiels vernünftig erklären, denn ich möchte es auch verstehen. Danke euch.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich bins nochmal:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PyQt4.QtGui import *
from PyQt4.QtCore import *
from os.path import expanduser
import sys

def get_new_path():

    QMessageBox.about(None, 'No folder', "Nur ein Text")
    folder = QFileDialog.getExistingDirectory(None, "Open folder",
                                              expanduser("~"),
    QFileDialog.ShowDirsOnly)
    return folder
    
def show_path():
    folder_path = get_new_path()
    print unicode(folder_path, 'latin-1')
    
if __name__ == "__main__":
    app = QApplication(sys.argv)
    show_path()
    sys.exit(app.exec_())
Nach vielem Probieren hat es auf einmal scheinbar geklappt. Ich habe die Variable folder_path unicode(folder_path, 'latin-1') wie folgt enkodiert. Nun wollte ich wissen, ob das nur ein "Zufall" ist, dass es bei mir klappt und es eine andere, bzw. bessere Lösung gibt?

Gruß
Sophus
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du verwendest nirgends `decode` also kann der Fehler so nicht auftreten. QString sind QT-Unicode-Strings. Das Encoding bei "unicode" ist also absoluter Blödsinn. Der wird bei Python Unicode-Strings sogar mit einer Fehlermeldung belohnt.
Das mit Unicode ist doch eigentlich ganz einfach. Intern wird nur mit Unicode gearbeitet, wenn Du aber irgendetwas aus einem Byte-orientierten Gerät liest oder schreibst, mußt Du de- bzw. encodieren.

Python2 encodigert dabei Unicode-Strings an manchen Stellen automatisch, wenn er z.B. das Encoding des Terminals kennt. Da QStrings aber kein encode kennen, gibts in diesen Fällen Probleme.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Von einem absoluten Blödsinn zu reden ist ein wenig zu weit gegriffen. Erst dadurch, dass ich Unicode auf die Variable folder_path mit latin-1 angewendet habe, taucht keinerlei Fehlermeldung auf, und der Pfad, auch die mit den Umlauten werden über die print-Anweisung ordentlich ausgegeben. Also augenscheinlich hat dieser absolute Blödsinn also etwas bewirkt. Und ich kann auf die Variable folder_path keine decode()-Methode verwenden, da diese Variable vom Typ ein QString ist. Wo also sollte ich in meinem Code die decode()-Methode verwenden?

Ohne diesen unicode() bekomme ich die Fehlermeldung:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xdc' in position 9: ordinal not in range(128)
Mit diesem unicode und dazu 'latin-1' bekomme ich über die print-Anweisung:
C:\Users\Übergang\Contacts
Also hat der absolute Blödsinn also was gebracht? Oder aber ich bilde mir das ein, dass die print-Anweisung mir das ausgibt, und in Wirklichkeit wäre das eine Fehlermeldung. Aber dann wären wir bei der Philosophie von Plato angekommen. Soweit müssen wir nicht gehen.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: richtig, Du hast nirgends `decode` verwendet, also woher kommt dann die Fehlermeldung in Deinem ersten Beitrag?

QtStrings sind Unicode-Strings, deshalb können sie mit "unicode" in Python-Unicode-Strings konvertiert werden. Da aber beides Unicode ist, braucht man kein Encoding anzugeben. Daher ist die Angabe eines Encodings Blödsinn, und wie ich gerade getestet habe, sogar gefährlich, weil intern Qt alles erst nach Latin1 konvertiert, alles was nicht mit Latin1 darstellbar ist durch "?" ersetzt, um es dann in Python wieder nach Unicode zu decodieren.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Ich verwende genau den gleichen Quelltext wie hier. Kein zusätzlicher Quelltext. Ich verstehe ja selbst nicht, wieso ich eine Fehlermeldung ohne diesen unicode() bekomme. Hast du vielleicht eine Ahnung? Ich benutze Python 2.7.6, Windows 7. Ich habe deshalb keinen decode() angewendet, weil ich davon ausging, dass das QT-Rahmenwerk das für mich erledigt. Und QFileDialog gehört ja dazu, richtig?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: da Du nicht weiter auf das überflüssige Encoding bei "unicode(...)" eingehst, gehe ich mal davon aus, dass Du meine Antwort jetzt verstanden hast.

Alle Argumente, die print bekommt und kein String oder Unicode-String sind, werden mit str(...) in einen String umgewandelt. Das geht bei QStrings mit Umlauten schief mit der Fehlermeldung

Code: Alles auswählen

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
QStrings kann man aber mit unicode in Python-Unicode-Strings umwandeln, die dann von print auf magische Weise ins Encoding des Terminals encodiert werden.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3:

Das Umwandeln von QStrings mit unicode in Python-Unicode-Strings sieht dann wie folgt aus?

Etwa so?

Code: Alles auswählen

print unicode(folder_path)
Ich glaube kaum, dass du das so meinst. Sicherlich liege ich gänzlich falsch.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: genau so meine ich das.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Vielen Dank. Es klappt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Irgendwie verfolgt mich das Unicode-Problem. Ich habe hier eine Funktion, der bestimmte Argumente von einer anderen Funktion übergeben werden. Aus der log_in()-Funktion werden Daten aus einem QlineEdit entnommen, und diese dann als Parameter an die connect_to_mysql()-Funktion übergeben. Sobald aber zum Beispiel im Passwort ein Umlaut vorhanden ist, bekomme ich eine Fehlermeldung, selbst das Argument in Python-Unicode umgewandelt wird.

Code: Alles auswählen

    def log_in(self):
        dbms='pymysql'
        server_host = str(self.ui_login.lineEdit_sevrer_host.text())
        username = str(self.ui_login.lineEdit_username.text())
        password = unicode(self.ui_login.lineEdit_password.text())
        database_name = str(self.ui_login.lineEdit_database_name.text())
        port = str(self.ui_login.lineEdit_port.text())
        mysql_connection_result = manage_mysql.connect_to_mysql(dbm_system,
                                                                    dbms_driver,
                                                                    username,
                                                                    password,
                                                                    server_host,
                                                                    port,                                                                   
                                                                    database_name)

def connect_to_mysql(dbm_system, dbm_driver, db_user, db_passwd, db_host, db_port, db_name):
    engine = create_engine('{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'.format(
                                                                     db_sys=unicode(dbm_system),
                                                                     db_driver=unicode(dbm_driver),
                                                                     user=unicode(db_user), 
                                                                     password=unicode(db_passwd),
                                                                     host=unicode(db_host),
                                                                     port=unicode(db_port),
                                                                     db=unicode(db_name),
                                                                     connect_args = {'time_zone': '+00:00'},
                                                                     encoding='utf8',
                                                                     echo=True))
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-7: ordinal not in range(128)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du versuchst da Unicode-Zeichen in einen Byte-String hineinzuformatieren. Das will Python nicht. Du mußt den Format-String auch als Unicode-String angeben. Außerdem handelt es sich um eine URL, das heißt, Du mußt Sonderzeichen (die ja v.a. in Passwörtern vorkommen können) kodieren.
Was soll eigentlich die ständige Konvertiererei von QString nach str nach unicode? Alles überflüssig.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Sophus: Ich unterstreiche jetzt nochmal, was Sirius3 mehrfach erläutert hat: Wandle Qt-Strings unter Python 2 stets mittels ``unicode(dein_qt_string)`` direkt in einen Unicode-String um. Die Verwendung von `str()` beim Konvertieren von Qt-Strings in Python-Strings ist nur unter Python 3 sinnvoll. Du befolgst das nach wie vor nicht konsequent und wirst dir damit nach wie vor Fehler einhandeln. Sicherlich auch, weil du (mutmaßlich) das Konzept von Unicode noch nicht verstanden hast.

Übrigens empfehle ich dir, die Zeile ``from __future__ import unicode_literals`` an den Anfang deines Programms zu setzen. Damit ist garantiert, dass alle Vorkommen von ``'dein_text'`` zu Unicode umgewandelt werden. Hierdurch wird in deinem Fall auch der Aufruf von `create_engine()` funktionieren. Oder zumindest wird dann kein `UnicodeEncodeError` mehr für diese Zeile geworfen. Was du möglicherweise sonst noch an Fehlern in deinem Quelltext hast, weiß ich natürlich nicht.

Übrigens würde es enorm helfen, wenn du bei Fehlermeldungen den kompletten Traceback reinkopiert, oder mindestens den Teil, woraus hervorgeht, in welcher Zeile der Fehler aufgetreten ist. Dann muss man die Zeile nämlich nicht raten.

Hier übrigens nochmal ganz deutlich:

Code: Alles auswählen

inhalt = str(dein_widget.text())
print unicode(inhalt)

# ... wird zu:
print unicode(dein_widget.text())
Der `str()`-Schritt fällt also weg.

Wahlweise könnte man auch eine kleine Hilfsfunktion definieren:

Code: Alles auswählen

def print_content(text_widget):
    print unicode(text_widget.text())
BlackJack

@snafu: `unicode_literals` würde ich nicht importieren, da gibt es wohl einige Stellen die dann kaputt gehen können oder nur scheinbar funktionieren bis dann mal irgendwo etwas ausserhalb von ASCII in die Daten kommt. Unicode ist ja ”ansteckend”, das heisst bei Operationen die eigentlich mit Bytestrings arbeiten sorgt ein Unicode-Operand dafür dass das Ergebnis zu Unicode wird. Und damit rechnen dann vielleicht nicht alle Aufrufer.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@BlackJack: Du beziehst dich vermutlich auf betriebssystemnahe Anwendungen und auf Interaktionen in Netzwerken bzw ganz generell die Kommunikation mit externen Systemen? Ich würde erwarten, dass der Anwender dafür selbst unter Python 2 explizit angibt, dass er mit Bytestrings (``b'spam'``) arbeiten möchte.

Grundsätzlich kann für Programmierer, die nicht so recht wissen, was sie tun, die Verwendung von `unicode_literals` aber eine Fehlerquelle sein - das stimmt schon. Andererseits ist Code, der sich *nicht* auf Python-2-Verhalten verlässt, schon mal in Bezug auf Strings in aller Regel leichter auf Python 3 portierbar.

Ich persönlich verwende den Import unter Python 2 immer dann, wenn ich in einer Situation bin, wo ich explizit angeben muss, dass ich einen Unicode-String mittels String-Literalen erzeugen möchte. Dann nutze ich statt ``u'spam'`` eben gleich den Import. Ich gebe zu, dass ich da eigentlich nicht ganz konsequent bin. Aber der Import für jedes einzelne Modul nervt halt irgendwie und die Verwendung von Unicode-Strings wird in Python 2 ja oftmals auch nicht zwingend benötigt.

EDIT: Zu der obigen Aufzählungen gehört als wichtiger Punkt noch die Verwendung von Bibliotheken, die schlichtweg nicht auf Unicode eingestellt sind. Da muss man natürlich auch aufpassen, dass man denen nicht versehentlich Unicode übergibt.
BlackJack

@snafu: Fehlerquelle über die man sehr leicht stolpern kann sind Dateinamen. Ist das betriebssystemnah? Ich denke auch es ist einfach sauberer wenn man beim/vorm Portieren nach Python 3 jede Zeichenkette explizit als b'' oder u'' markiert, damit kann man dann sicher sein dass man sich alles mal angeschaut hat und nicht einfach nur hofft das die Umstellung schon funktionieren wird.

Zu den Bibliotheken die nicht auf Unicode eingestellt sind gehören auch Teile der Standardbibliothek. `pack()` und `unpack()` aus dem `struct`-Modul mögen als erstes Argument keine Unicodezeichenkette. Was ich auf den ersten Blick etwas überraschend fand, aber dann dachte ich es gibt sicher noch mehr Module die in C geschrieben sind und kein Unicode erwarten bei Argumenten die für den Programmierer auf den ersten Blick aber nach Zeichenkette und nicht nach Bytes aussehen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute, ich habe jetzt konseqenterweise alle QStrings in Unicode() umgewandelt, jedoch ergibt sich weiterhin das Problem:

Code: Alles auswählen

# function from frm_log_in.py
    def log_in(self):
        dbms='pymysql'
        server_host = unicode(self.ui_login.lineEdit_sevrer_host.text())
        username = unicode(self.ui_login.lineEdit_username.text())
        password = unicode(self.ui_login.lineEdit_password.text())
        database_name = unicode(self.ui_login.lineEdit_database_name.text())
        port = unicode(self.ui_login.lineEdit_port.text())

        dbms_driver = 'pymysql'
        dbm_system = 'mysql'
            
        mysql_connection_result = manage_mysql.connect_to_mysql(dbm_system,
                                                                    dbms_driver,
                                                                    username,
                                                                    password,
                                                                    server_host,
                                                                    port,                                                                   
                                                                    database_name)

# function from a core-module
def connect_to_mysql(dbm_system, dbm_driver, db_user, db_passwd, db_host, db_port, db_name):

    try:
        engine = create_engine('{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'.format(
                                                                     db_sys=dbm_system,
                                                                     db_driver=dbm_driver,
                                                                     user=db_user,
                                                                     password=db_passwd,
                                                                     host=db_host,
                                                                     port=db_port,
                                                                     db=db_name,
                                                                     connect_args = {'time_zone': '+00:00'},
                                                                     encoding='utf8',
                                                                     echo=True))
self.log_in()
File "D:\Dan\Python\xarphus\xarphus\frm_db_login.py", line 203, in log_in
database_name)
File "D:\Dan\Python\xarphus\xarphus\core\manage_mysql.py", line 152, in connect_to_mysql
echo=True))
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
Er zeigt mir zu letzt auf echo=True.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du hast ja auch nichts von dem, was ich geschrieben habe, umgesetzt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Du wolltest, dass die Strings im Format, also in der connect_to_mysql()-Funktion in unicode() umgewandelt werden. Nicht dass ich deine Anmerkung überlese. Ich dachte nur, wenn ich die Strings schon weit vorher in unicode() umgewandelt habe, dann brauche ich die hinterher nicht nochmal in unicode() umzuwandeln. Vielleicht sollte ich nicht erst versuchen selbstständig zu denken.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die Aussage war:
Sirius3 hat geschrieben:@Sophus: Du versuchst da Unicode-Zeichen in einen Byte-String hineinzuformatieren. Das will Python nicht. Du mußt den Format-String auch als Unicode-String angeben.
Und der Format-String ist nach wie vor ein Bytestring. Setze entweder ein "u" vor den String oder verwende den von mir empfohlenen Import. Wie schon angesprochen wurde, kann es sein, dass du bei Nutzung der Import-Variante noch andere Veränderungen im Quelltext vornehmen musst.

Dies ist der Format-String, falls das noch nicht klargeworden ist:

Code: Alles auswählen

'{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'
Antworten