QPlainTextEdit hängt

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Hier mal der fertige Code Klick

Bin für Verbesserungsvorschläge offen ;) Danke an alle die geholfen haben =)
lunar

"ReadTXT.run" implementierst Du besser so:

Code: Alles auswählen

def run(self):
    with open("log.log", "r") as stream:
        for line in log:
            self.newLineRead.emit(line.strip())
Und da Du die neue Signal-Slot-API bereits zur Deklaration des Signals nutzt, spricht nichts dagegen, sie auch beim Verbinden zu nutzen, so dass Zeile 31ff. als "pushButton.clicked.connect(read.start)" formuliert werden kann.

Zum Schluss noch der obligatorische Hinweis auf PEP 8 (e.g. "MAINGUI" umbenennen), und darauf, dass "pushButton" und "textEdit" keine sonderlich aussagekräftigen Bezeichner sind. :)
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

mir gefällt die Abhängigkeit der 2 Klassen untereinander nicht!
Erzeuge das 'ReadTXT' in der 'main()' und übergib das Objekt dem 'MainGUI' bei der Erzeugung. Die umgekehrt Kopplung ist meiner Meinung nach nicht nötig. 'ReadTXT' muss nichts von einer GUI wissen. Die '__init__'-Methode der 'ReadTXT' Klassen kannst du dann komplett löschen.
Durch diese Trennung wären die 2 Klassen auch besser einzeln testbar.
lunar

"ReadTXT.__init__()" kann man auch so gleich löschen, der Konstruktor tut ja nichts sinnvolles. Aber ich denke, dass ist nur speziell in diesem Beispiel der Fall, tatsächlich wird da wohl irgendeine Art der Initialisierung stehen (e.g. Übergabe des Dateinamens der Logdatei oder so).

Auch sehe ich kein großes Problem in der „Abhängigkeit der Klassen“. Die gibt es gar nicht, denn "ReadTXT" ist auch jetzt schon vollkommen unabhängig von der GUI-Klasse. Allenfalls hängt die GUI-Klasse von ReadTXT ab, aber das ist mit einem einfachen Refactoring zu lösen, und daher meines Erachtens nicht so tragisch. Zumal das im tatsächlichen Quelltext möglicherweise auch so aussehen muss, nämlich wenn ReadTXT erst in Abhängigkeit von gewissen Eingaben in der GUI (e.g. Dateiname der Logdatei aus einem Dateidialog) erzeugt werden kann.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Bin mir nicht ganz sicher ob "startButton" und "logEdit" sinnvollere Namen sind ^^ aber ich habe die "__init__" Funktion der "ReadTXT" Klasse gelöscht und das "read" Objekt wird nun von der "main" Funktion vererbt.

Habe noch eine Frage bezüglich der Signale.
Wie stelle ich es am besten an das ich die gleichen Signal von mehreren Klassen aus nutzen kann? Wenn ich z.B das Signal "newLineRead" in einer anderen Klasse noch einmal definieren würde müsste ich auch den Slot in der GUI Klasse neu definieren. Würde aber lieber von Zwei Klassen auf einen Slot zugreifen.

Nun habe ich noch eine Frage die sich eher weniger auf diesen Thread richtet:
Wie bringe ich folgenden Beispiel Code auf 80 Zeichen pro Zeile?

Code: Alles auswählen

self.newLineRead.emit("Hallo, ich bin ein mehr als achtzig Zeichen langer Befehl und möchte ohne Zeilenumbruch ausgegeben werden")
Anbei noch der, wie oben beschrieben, veränderte Code.

Code: Alles auswählen

from PySide import QtCore, QtGui
import sys, time


class MainGui(QtGui.QMainWindow):
    def __init__(self, read):
        QtGui.QMainWindow.__init__(self)

        widget = QtGui.QWidget(self)
        widget.setLayout(QtGui.QVBoxLayout(widget))
        self.setCentralWidget(widget)

        logEdit = QtGui.QPlainTextEdit(widget)
        logEdit.setObjectName("textEdit")
        widget.layout().addWidget(logEdit)

        startButton = QtGui.QPushButton(widget)
        startButton.setObjectName("pushButton")
        widget.layout().addWidget(startButton)

        startButton.setText(
                   QtGui.QApplication.translate("Form",
                                                 "Start",
                                                  None,
                                            QtGui.QApplication.UnicodeUTF8
                                            )
                   )

        startButton.clicked.connect(read.start)
        read.newLineRead.connect(logEdit.appendPlainText)


class ReadTXT(QtCore.QThread):
    newLineRead = QtCore.Signal(unicode)

    def run(self):
        file = open("log.log", "r")
        lines = file.readlines()
        file.close()

        for line in lines:
            self.newLineRead.emit(line.strip())
            time.sleep(0.01)


def main():
    app = QtGui.QApplication(sys.argv)
    read = ReadTXT()
    widget = MainGui(read)
    widget.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()
lunar

Bitte gewöhne Dir die korrekte Terminologie an. In Deinem Beispiel wird das an "read" gebundene Objekt von "main()" an "MainGUI.__init__()" übergeben, mit Vererbung hat das nicht das Geringste zu tun. Vererbung ist eine Beziehung zwischen Klassen.

Deine Frage bezüglich der Signale verstehe ich nicht, oder anders gesagt, ich kann das beschriebene Problem nicht nachvollziehen. Du kannst doch ganz einfach mehrere, identische oder auch ganz verschiedene Funktionen oder Methoden mit einem Signal verbinden. Wenn Du Methoden zwischen Klassen "teilen" möchtest, in den Sinne, dass beide Klassen diese Methode in identischer Implementierung besitzen, dann hat das nichts mit Signalen und Slots, sondern nur mit den absoluten Grundlagen der Objektorientierung zu tun, die Lösung nämlich heißt Vererbung (und zwar im eigentlichen objektorientierten Sinne, und nicht in dem Sinne, welchem Du diesem Terminus in Deinem Beitrag verpasst hast).
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Sorry wegen der falschen Terminologie.. Werde in Zukunft besser nachdenken bevor ich schreibe ;)

Also ich mochte von Zwei Klassen auf das selbe Signal zugreifen. Ich habe in meinem echten Programm 8 Signale die ich in zwei klassen brauche, das heißt ich muss in der GUI Klasse 16 connect's machen. Ich habe mich nun mal an einem Beispiel mit Vererbung versucht... Das ganze läuft aber nicht wirklich... Klick
Ich habe also eine Talker Klasse erstellt welche das Signal beinhaltet und diese an drei Klassen Vererbt,(Dieses mal ist es sogar richtig :P) an meine GUI Klasse und an zwei Klassen die etwas in das Textfeld einfügen sollen. Nun mault der Interpreter aber rum:
Traceback (most recent call last):
File "C:\scripts\mainExample.py", line 61, in <module>
main()
File "C:\scripts\mainExample.py", line 56, in main
widget = MainGui(read)
File "C:\scripts\mainExample.py", line 34, in __init__
Talker.writeNewLine.connect(logEdit.appendPlainText)
AttributeError: 'PySide.QtCore.Signal' object has no attribute 'connect'
Denke mal die Talker Klasse wird nicht richtig initialisiert sodass das Signal nicht richtig erstellt wird... Wie geht es richtig?

Edit: Habe gerade herausgefunden dass ich das "Talker" in den Klassen durch self ersetzen muss :D Insgesamt wird aber trotzdem nichts in das Textfeld geschrieben. Denke mal dass ich wieder mehrere Signale erstelle und er deswegen nichts empfängt.
lunar

Wie in der Dokumentation beschrieben ist, müssen Klassen, die Signale verwenden, von "QObject" ableiten. Davon abgesehen ist Vererbung wirklich zu viel des Guten, wenn es wirklich nur darum geht, identische Signale in mehreren Klassen zu deklarieren. Da kannst Du die Deklaration auch einfach in jeder Klasse hinzufügen, Vererbung ist nur sinnvoll, wenn wirklich eine signifikante Menge an Quelltext zwischen Klassen zu teilen ist.

Davon abgesehen verstehe ich Dein Problem noch immer nicht. Im vorherigen Beitrag ging es um Slots, die in Python letztlich nichts weiter als normale Methoden sind, jetzt aber geht es Dir auf einmal um Signale, und die sind eben durchaus besondere Attribute.

Im Übrigen ist es eigentlich Grundwissen über Objektorientierung, dass man auf Exemplarattribute mittels "self" zugreift. Das muss Dir klar sein, bevor Du graphische Oberflächen entwickeln möchtest.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Hmm ich werde jetzt einfach in beiden "worker" Klassen die Signale definieren. Ist denke ich einfacher...
Und wie ich self benutze weiß ich natürlich :D Mir war nur nicht ganz das Ausmaß der Vererbung bewusst. Also was dort geschieht.

Denn mal Danke für die Hilfe =)
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Ich bin gerade auf noch ein Problem gestoßen. Ich brauche zum Beispiel in einer worker Klasse den Text aus einem QLineEdit. Wie stelle ich das ganze an?
lunar

@JonasR: Du musst eben den Text des Steuerelements irgendwie an das "Worker"-Exemplar übergeben.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Naja das war mir klar nur blieb ich auch an dem "irgendwie" hängen ^^

Soll ich in dem worker ein Signal senden welches der GUI Klasse sagen soll dass sie das QLineEdit ausliest und in eine var der worker Klasse schreiben soll?
Weiß halt nicht ob es dafür eine gute Lösung gibt...
lunar

Wieso übergibst Du den Text nicht einfach bei der Erzeugung des "Worker"-Exemplars?
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Weil sich das Ganze während der Laufzeit noch ändern kann.
lunar

@JonasR: Dann sende doch einfach ein Signal an den Thread, welchem Du den Text des Steuerelements als Parameter mitgibst.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Müsste mein worker dann aber nicht die GUI Klasse kennen um connecten zu können?
lunar

Nein, muss er nicht. Es ist gerade der Sinn des Signal-Systems, dass verschiedene Komponenten nichts voneinander wissen müssen, und trotzdem kommunizieren können. Du kannst beliebige Signale an beliebiger Stelle mit beliebigen Methoden oder Funktionen verbinden.

Im konkreten Fall verbindest Du einfach nach der Erzeugung des "Worker"-Exemplars das entsprechende Signal Deiner GUI-Klasse mit dem entsprechenden Slot des "Worker"-Exemplars. So schwer ist das doch nicht ...
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Sorry arbeite noch nicht so lange und intensiv mit Signalen und Slots :?

Kannst du mir das vielleicht ein Beispiel geben? Muss nichts großes sein.. Nur ein Denkanstoß ^^Stehe gerade ein bisschen auf dem Schlauch
lunar

Mit Verlaub, es finden sich bereits genügend Beispiele für die Verwendung von Signalen und Slots in der Dokumentation von Qt, und es wurde in diesem Thread auch bereits eines meiner PyQt-Beispiele verlinkt, in welchem naturgemäß ebenfalls ausgiebig Gebrauch von Signalen und Slots gemacht wird.
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

lunar mir sind die Grundlagen von Signalen und Slots, wie du an meinen Codefetzen siehst, schon läufig. Nur weiß ich nicht wie ich einen Slot im worker definieren soll der das GUI nicht kennt...
Vielleicht habe ich auch mal wieder was vollkommen falsches im Kopf aber ich erkläre mal was ich denke:

Signal, würde in der GUI Klasse stehen:

Code: Alles auswählen

signalVariable = QtCore.Signal(unicode)
Slot, würde im worker stehen:

Code: Alles auswählen

???.signalVariable.connect(func)
Also was soll anstelle der drei Fragezeichen hin wenn ich das GUI nicht kenne?
Antworten