PyQt Tutorialübersicht

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.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

Hallo liebe Forumsmitglieder!

Ich bin neu in diesem Forum, da ich mich nun ernsthafter mit python auseinander setze. Ich habe bereits einen Onlinekurs und das Python-Buch von Galileo-Computing durchgearbeitet. Somit sind schon ein paar Grundkenntnisse vorhanden. Bevor ich mit Python begann habe ich viel in PHP gearbeitet und dort überwiegen Laravel angewendet.

Nun wieso dieser Post:

Ich habe mit Python und PyQT angefangen und soweit einfache "applets" geschrieben. Nun möchte ich ein größeres Projekt angehen, bei dem ich eine Anwendung schreiben möchte, die mehrere Unterfenster besitzt. Mit Konfigurationen arbeitet ein Layout hat und so weiter. Also im Grunde eine Anwendung, mit der es Spaß macht zu arbeiten.

Nun ich suche noch eine gute Tutorial-Übersicht zu:

- Layouten mit von Anwendungen mit PyQt, ich habe bisher mit den Styles gearbeitet im Creator, jedoch war das meist eher schlecht als recht.
- Multi-Window Anwendungen also wie gehe ich am schlausten mit mehreren Fenstern um, wie blockiere ich zum Beispiel ein Hintergrundfenster, oder wie schließe ich ein altes Fenster, wenn ich ein neues Aufrufe
- Laufzeitparalele Darstellung, wenn größere Datenmengen verarbeitet werden und mehrere Animationen/Berechnungen parallel laufen sollen

Ich danke für eure Hilfe und hoffe euch bald im Showroom die Anwendung zu präsentieren :-)
xXSkyWalkerXx1
User
Beiträge: 379
Registriert: Mittwoch 27. Juni 2018, 17:39

Krie9er hat geschrieben: Montag 30. März 2020, 20:21 - Layouten mit von Anwendungen mit PyQt, ich habe bisher mit den Styles gearbeitet im Creator, jedoch war das meist eher schlecht als recht.
- Multi-Window Anwendungen also wie gehe ich am schlausten mit mehreren Fenstern um, wie blockiere ich zum Beispiel ein Hintergrundfenster, oder wie schließe ich ein altes Fenster, wenn ich ein neues Aufrufe
- Laufzeitparalele Darstellung, wenn größere Datenmengen verarbeitet werden und mehrere Animationen/Berechnungen parallel laufen sollen
Layouts:
  • QSplitter: Widgets werden nacheinander Vertikal/Horizontal (abhängig von QBoxLayout, also VBox oder HBox) platziert
  • QHBoxLayout: *H*orizontal verlaufendes Layout
  • QVBoxLayout: *V* verlaufendes Layout
  • QGridLayout: Gitternetzlayout, als Bsp. Taschenrechnerinputs (1-7)
Multiwindow:
  • für jedes Fenster erstellst du eine Klasse. Soll ein Fenster von einem anderen Fenster aufgerufen werden, lässt du das aktuelle Fenster verstecken ( ".hide()" ) und das andere anzeigen ( ".show()" )
    warum auch immer, kommt es bei mir aber widerrum dazu, dass sich *manchmal* das Fenster leicht durch ".hide()" und dann mit ".show()" am Style verändert, weshalb ich auch schon einmal einfach die Transparenz dafür verwendet habe
Laufzeutparallele:
  • wenn du Threads meinst, dann gibt es '_thread' (für Funktionen bestimmt) und 'Thread' (für Klassen bestimmt)

Anhand der Begriffe lässt sich auch schnell was bei Google finden...
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@xXSkyWalkerXx1: nein, `_thread` gibt es nicht, jedenfalls ist es nicht für die Benutzung gedacht, wegen des führenden Unterstrichs. Wenn man Qt benutzt, möchte man auch QThread benutzen.
xXSkyWalkerXx1
User
Beiträge: 379
Registriert: Mittwoch 27. Juni 2018, 17:39

Sirius3 hat geschrieben: Dienstag 31. März 2020, 09:08 @xXSkyWalkerXx1: nein, `_thread` gibt es nicht, jedenfalls ist es nicht für die Benutzung gedacht, wegen des führenden Unterstrichs. Wenn man Qt benutzt, möchte man auch QThread benutzen.
Warum ist es nicht für die Benutzung gedacht? Denn ich finde es eigentlich ziemlich hilfreich.
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

Das ist eine Design-Entscheidung der Python-Entwickler, die als die Art und Weise, wie man Threads benutzt threading.Threads vorgesehen haben, und nicht _threads. Die API von _threads ist sehr low-level und man muß viel implizites Wissen haben, um sie korrekt zu benutzen.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

xXSkyWalkerXx1 hat geschrieben: Dienstag 31. März 2020, 08:24
Krie9er hat geschrieben: Montag 30. März 2020, 20:21 - Layouten mit von Anwendungen mit PyQt, ich habe bisher mit den Styles gearbeitet im Creator, jedoch war das meist eher schlecht als recht.
- Multi-Window Anwendungen also wie gehe ich am schlausten mit mehreren Fenstern um, wie blockiere ich zum Beispiel ein Hintergrundfenster, oder wie schließe ich ein altes Fenster, wenn ich ein neues Aufrufe
- Laufzeitparalele Darstellung, wenn größere Datenmengen verarbeitet werden und mehrere Animationen/Berechnungen parallel laufen sollen
Layouts:
  • QSplitter: Widgets werden nacheinander Vertikal/Horizontal (abhängig von QBoxLayout, also VBox oder HBox) platziert
  • QHBoxLayout: *H*orizontal verlaufendes Layout
  • QVBoxLayout: *V* verlaufendes Layout
  • QGridLayout: Gitternetzlayout, als Bsp. Taschenrechnerinputs (1-7)
Multiwindow:
  • für jedes Fenster erstellst du eine Klasse. Soll ein Fenster von einem anderen Fenster aufgerufen werden, lässt du das aktuelle Fenster verstecken ( ".hide()" ) und das andere anzeigen ( ".show()" )
    warum auch immer, kommt es bei mir aber widerrum dazu, dass sich *manchmal* das Fenster leicht durch ".hide()" und dann mit ".show()" am Style verändert, weshalb ich auch schon einmal einfach die Transparenz dafür verwendet habe
Laufzeutparallele:
  • wenn du Threads meinst, dann gibt es '_thread' (für Funktionen bestimmt) und 'Thread' (für Klassen bestimmt)

Anhand der Begriffe lässt sich auch schnell was bei Google finden...
Wow vielen Dank für die schnell und freundliche Antwort!

Ich habe mir den Thread direkt als Bookmark gespeichert!

Wie löst ihr die Organisation des Programms? Meine idee ist, die views in eigene Klassen zu schreiben, genauso wie die Prozesse. Also im Prinzip versuche ich MVC anzuwenden. Ist das Sinnvoll, leider geben meine Tutorialunterlagen hier keine Info zu best-practises oder cummon-standards, was das angeht.

Mit Layouten meinte ich eigentlich das optische Design des Programms. Ich hatte mich hier etwas zu ungenau ausgedrückt :-)
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

@xXSkyWalkerXx1 ich habe noch eine tiefere Frage zu dem Laden der Fenster.

Ich habe folgenden Code in meiner StartApp stehen:

Code: Alles auswählen

import sys, os
from PyQt5 import QtWidgets, uic, QtGui
from myApp import EffectOnVariationExample
from PyQt5.QtGui import QIcon, QPixmap

class  StartProgram(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi(myApp/gui/navigatorwindow.ui', self)
        self.show()
        self.routing()

    def routing(self):
        self.ui.btnEffectOfVariationExample.clicked.connect(self.playEffectOfVariationExample)

    def playEffectOfVariationExample(self):
        widget =EffectOfVariationExample()
        print("Test")
        self.hide()


app = QtWidgets.QApplication(sys.argv)
lp = StartProgram()

sys.exit(app.exec_())
In meiner Klasse EffectOfVariationExample steht:

Code: Alles auswählen

from PyQt5 import QtWidgets, uic, QtGui

class  EffectOnVariantionExample(QtWidgets.QWidget):
    def __init__(self):
        self.ui = uic.loadUi('gui/EffectOfVariationExample.ui', self)
        self.show()
jetzt erhalte ich jedoch die folgende Fehlermeldung, wenn ich auf den Button zum Öffnen des neuen Fensters clicke:

Code: Alles auswählen

    widget = EffectOnVariationExample()
TypeError: 'module' object is not callable
Ehrlich gesagt verstehe ich nicht recht was dieses Fehlermeldung bedeuten soll. Wie realisierst du/ihr das öffnen (und schließen) von mehreren Fenstern in pyQt5?

Gruß
Zuletzt geändert von Krie9er am Dienstag 14. April 2020, 10:06, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das hat mit Qt nichts zu tun. Hast du den Java-Style (und Python-Fehler) gemacht, deine Klasse in eine Datei gleichen Namens zu stecken? Das macht man nicht. Module sind in Python klein geschrieben, und sollten auch mehrere Klassen und Funktionen enthalten. Und dann importiert man *aus* diesen Modulen eine Klasse.

Code: Alles auswählen

from myapp.effectonvariationmodule import EOnM
Dazu dann auch noch die Anmerkung, dass myApp nicht korrekt benamt ist, und mit my und App auch so ziemlich das nichtssagende, was man sich vorstellen kann. Da waere 'einhorn' oder so wirklich eine bessere Wahl.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

@►__deets__ Danke für die Anmerkungen, ja da schlägt meine PHP-Vergangenheit durch.

myApp war eigentlich nur ein Platzhalter xD

Ich habe den code jetzt auch verändert, sodass die Klasse geladen wird. Und das Widget auch angezeigt wird:
programm.py:

Code: Alles auswählen

import sys, os
from PyQt5 import QtWidgets, uic, QtGui
from LeanPlayground import EffectOnVariationExample, Routing
from PyQt5.QtGui import QIcon, QPixmap

class  StartProgram(QtWidgets.QMainWindow):
    def __init__(self, parent=None):

        self.rootDir = os.path.dirname(__file__)
        print(os.path.join(self.rootDir, 'LeanPlayground/gui/navigatorwindow.ui'))
        super().__init__(parent)
        self.ui = uic.loadUi('LeanPlayground/gui/navigatorwindow.ui', self)
        self.ui.licenseOwnerLogo.setPixmap(QPixmap('LeanPlayground/assets/apple-icon-120x120.png'))
        self.show()
        self.routing()

    def routing(self):
        self.ui.btnEffectOfVariationExample.clicked.connect(self.playEffectOfVariationExample)

    def playEffectOfVariationExample(self):
        widget = EffectOnVariationExample.playEOVE()
        print("Test")
        self.hide()


app = QtWidgets.QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon('LeanPlayground/assets/favicon_2.ico'))
lp = StartProgram()

sys.exit(app.exec_())
leanplayground/EffectOnVariation.py:

Code: Alles auswählen

from PyQt5 import QtWidgets, uic, QtGui
from PyQt5.QtGui import QIcon, QPixmap

class  playEOVE(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi('LeanPlayground/gui/EffectOfVariationExample.ui', self)
        self.show()
        
Aktuell suche ich noch eine Möglichkeit, wie ich beim schließen des neuen Fensters das NavigatorFenster wieder anzeige (mit .show())
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du solltest wirklich die Python Namenskonventionen adoptieren. EtwasMitGrossBuchstabenDarin ist in den Augen jedes erfahrenen Pythoneers eine Klasse. Damit verwirrst du uns und ggf. dich selbst.

Und wenn ein Fenster geschlossen wird, dann sollte es da doch ein Signal geben, an das man sich haengen kann.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

Ich hatte eigentlich gedacht, dass ich mich an die Namenskonvention halte?

Welcher Part ist nicht korrekt?

Für Dateien und Klasse benutze ich PascalCases
für Funktionen und Variablen camelCases

Documentation

spricht jedoch von sneak cases für funktionen und variablen, jedoch hält sich selbst python nicht an diese Konvention oder? Deshalb dachte ich camelCases wären hier in Ordnung.
Besonders, da pyQt es auch so verwendet.

Ich habe das mit dem Hide and show nun so gelöst:

#program.py

Code: Alles auswählen

    def playEffectOfVariationExample(self):
        widget = EffectOnVariationExample.PlayEOVE(self)
        print("Test")
        self.hide()
#EffectOnVariationExample.py

Code: Alles auswählen

class  PlayEOVE(QtWidgets.QWidget):
    def __init__(self, mainWindow, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi('LeanPlayground/gui/EffectOfVariationExample.ui', self)
        self.show()
        self.mainWindow = mainWindow

    def closeEvent(self, event):
        self.mainWindow.show()
Wie kann ich es eigentlich realisieren, dass ich nicht in jeder Klasse immer wieder die selben Sachen importieren muss? z.B. habe ich im Hauptprogramm ja schon PyQt5 importiert, wieso muss ich das in der Klasse erneut machen? Gibt es da eine Möglichkeit, dass die Imports vom Hauptprogramm vererbt werden?

Danke für die tolle Hilfe :-)!
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Python haelt sich da durchaus dran. Aber Qt hat eben eine C++ bzw. eigene Namenskonvention, und um da keine Abweichung mit der nur in C++ verfuegbaren Dokumentation zu haben, folgt PyQt eben der. Fuer eigenen Code und Module mache ich das aber nicht.

Und nein. Jedes Modul muss seine importe selbst machen. Das du dich da aber auf Klassen beziehst, ist wie schon erwaehnt ein Missverstaendnis: in Python ist es durchaus ueblich, mehrere Klassen in einem Modul zu haben. Nicht ein Modul / Klasse. Womit dieses Problem auch etwas weniger schwer wiegt.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

__deets__ hat geschrieben: Dienstag 14. April 2020, 11:34 ...

Und nein. Jedes Modul muss seine importe selbst machen. Das du dich da aber auf Klassen beziehst, ist wie schon erwaehnt ein Missverstaendnis: in Python ist es durchaus ueblich, mehrere Klassen in einem Modul zu haben. Nicht ein Modul / Klasse. Womit dieses Problem auch etwas weniger schwer wiegt.
Das ist wohl war :-) ich persönlich finde es viel übersichtlicher pro klasse eine Datei zu haben. Aber das ist nur mein eigener Geschmack.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

Eine kleine Frage noch zum Laufzeitparalellen arbeiten in einem Fenster.

In der Anwendung soll eine Anwendung oben etwas Berechnen, wobei es für die Geschwindigkeit der Berechnung einen Slider gibt und einen Start und Stop Button.

Die "Bremse" habe ich über ein Time.sleep() implementiert. Nun möchte ich jedoch während der Berechnung die Geschwindigkeit umstellen oder die Berechnung Pausieren , bzw. auch gerne mit dem Fenster weiter interagieren.
Allerdings ist das so aktuell nicht möglich :-/

# dummyfunktion zum test ob es funktioniert

Code: Alles auswählen

    def simulate(self):
        while self.simulation_active:
            self.iteration = self.iteration + 1
            self.machine_one_no_variation_status = random.randint(0,100)
            self.write_to_frontend()
            if self.iteration > 100:
                self.simulation_active = False
            if self.simulation_speed > 0:
                time.sleep(self.simulation_speed/100)
Sie wird gecalled durch

Code: Alles auswählen

def play_simulation(self):
        self.simulation_active = True
        self.simulate()
was wiederum durch

Code: Alles auswählen

self.ui.btnPlaySimulation.clicked.connect(self.play_simulation)
aufgerufen wird.

Im kern dreht sich nun meine Frage darum, wie ich weiter mit den Buttons interagieren kann, während eine Berechnung im Programm läuft.

Wahrscheinlich hab ich aktuell total Tomaten auf den Augen xD
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es gibt zwei prinzipielle Moeglichkeiten:

- die Berechnung wird in lauter kleine Teilstuecke zerlegt, und du erledigst nur einige wenige davon, bevor die GUI wieder dran kommt. Fuer sowas benutzt man QTimer. Um das umzusetzen kann man in Python zB auf asyncio setzen.
- die Berechnung wird in einen extra Thread oder gar Prozess ausgelagert. Hier liegt die Herausforderung darin, dass man Threads und GUIs nicht einfach mischen kann, sonst kommt es zu Abstuerzen. Mit Qt gibt es Rezept, wie man Worker-Threads baut, die sicher mit signal/slot Verbindungen kommunizieren.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

Oh danke für die Information.

Hast du eventuell etwas mehr Informationen zum 2. erwähnten Punkt? Wo ist dieses "Rezept" dokumentiert?
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

In der offiziellen Qt Dokumentation, und ich habe hier gelegentlich auch schon mal was dazu geschrieben, und das immer wieder mal rausgesucht. Aber gerade nicht die Zeit.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

Danke für den Hinweis, ich habe mir die offizielle Doku angesehen und das soweit, denke ich auch implementiert. Nun habe ich jedoch einen ganz merkwürdigen Bug.
Mein Fenster freezed immernoch jedoch glaube ich zu wissen wieso, nur weis ich nicht was der Grund ist wieso dies passiert.

Der Playbutton führt jetzt folgende Funktion aus:

Code: Alles auswählen

    def play_simulation(self):
        self.thread = ProductionProcess()
        print('Aufruf')
        self.thread.start()
Mein Thread ist noch einfach gehalten, da ich einfach nur testen möchte ob das Fenster dann nicht mehr freezed.

Code: Alles auswählen

class ProductionProcess(QThread):

    def __init__(self):
        QThread.__init__(self)
        self.iteration = 0

    def __del__(self):
        self.wait()

    def run(self):
        while self.iteration < 5:
            print(self.iteration)
            self.iteration = self.iteration + 1
            self.sleep(2)
Nach meinem Verständnis sollte doch der thread nun einmal ausgeführt werden, oder?

Naja der Output in der Console zeigt, dass der Thread 2x ausgeführt wird o.O

Code: Alles auswählen

Aufruf
0
1
2
3
4
Aufruf
0
1
2
3
4
Woran kann das liegen? Das merkwürdige ist, dass ich beim 2. Aufruf (der automatisch aus irgendeinem Grund passiert) dann die Thread Funktion habe.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du hast nicht das implementiert, was ich im Kopf hatte, und meines Kenntnisstandes auch nicht die Empfehlung von Qt. Man ueberlaedt run nicht mehr selbst, und was man NIEMALS in Python macht ist, __del__ zu benutzen. Wo hast du das her?

Hier ist das Beispiel, das ich mal geschrieben habe. Es macht ein paar Dinge mehr, die kannst du rauswerfen.

viewtopic.php?f=24&t=44250&start=15#p335559

Zu deinem Problem kann man mit dem gezeigten Code nichts sagen, daraus ist das nicht erklaerbar.
Krie9er
User
Beiträge: 22
Registriert: Montag 30. März 2020, 20:06

Danke für die Info, mein Fehler war, dass ich in der actions 2x den Button mit der Funktion verbinde.

Danke für den Link!
Antworten