PyQt - Fenster wechseln

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
23z86
User
Beiträge: 4
Registriert: Montag 22. März 2021, 19:03

Hallo allerseits,

nach bash und Java (ja, leider) habe ich es mir zur Aufgabe gemacht, Python zu lernen.
Nun bin ich bei der GUI-Programmierung gelandet und komme nicht weiter.

Ich möchte von einem Fenster über einen Pushbutton zum zweiten Fenster wechseln - funktioniert einwandfrei.
Im zweiten Fenster gibt es einen "Zurück"-Button, mit dem man zum ersten Fenster gelangen kann - erfolglos.

Anbei mein Ansatz. Ich hoffe, ihr könnt mir etwas auf die Sprünge helfen.

Code: Alles auswählen

import sys
from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
from PyQt5 import QtWidgets

class StartWindow(QMainWindow):
    def __init__(self):
        super(StartWindow, self).__init__()
        loadUi("ui/startwindow.ui",self)
        self.main_show.clicked.connect(self.showNextWindow)
        
    def showNextWindow(self):
        loadUi("ui/nextwindow.ui",self)     
        
class NextWindow(QMainWindow):
    def __init__(self):
        super(NextWindow, self).__init__()
        loadUi("ui/nextwindow.ui",self)
        self.next_back.clicked.connect(self.showMainWindow)
        
    def showMainWindow(self):
        loadUi("ui/startwindow.ui",self) 

app = QApplication(sys.argv)
window1 = StartWindow()
window1.show()
app.exec_()
Ich bin für Ratschläge und sonstige Hilfestellungen recht dankbar :)

Mit freundlichen Grüßen
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@23z86: Das zum zweiten Fenster wechseln funktioniert *nicht* einwandfrei, weil es nur ein Fenster gibt. Du erstellst ein Exemplar von `StartWindow` und wenn der Pushbutton gedrückt wird, dann wird der Inhalt von diesem Fenster durch "ui/nextwindow.ui" *ersetzt*. Und das war's. Von da an ist kein Pushbutton mehr mit irgendwas verbunden, denn der Pushbutton der mit `showNextWindow()` verbunden war, ist ja nun weg.

Du musst am Anfang beide Fenster erstellen und die dann jeweils wechselseitig bekannt machen, so dass in der Rückrufmethode das eine Fenster versteckt und das andere angezeigt werden kann.

Dann hast Du aber wirklich *zwei* Fenster, und die müssen auch nicht an der gleichen Stelle angezeigt werden. Falls das wie bisher im gleichen Fenster passieren soll, würde sich *ein* Fenster anbieten, mit einem `QTabWidget` um zwischen den beiden Seiten wechseln zu können. Oder ein `QStackedWidget` wenn Du die Pushbuttons zum Wechseln unbedingt innerhalb des Widgets haben willst, statt einer Leiste mit Reitern.

Sonstige Anmerkungen zum Quelltext: Das Hauptprogramm gehört in eine Funktion, damit man die Variablen dort nicht aus versehen irgendwo in Funktionen oder Methoden verwenden kann, und damit man das Modul importieren kann, ohne dass das Programm los läuft.

`QtWidgets` und `QDialog` werden importiert aber nicht verwendet.

`super()` braucht keine Argumente.

Namen sollte nicht nummeriert werden, schon gar nicht komplett sinnfrei.

Der Unterstrich bei `exec_()` sollte weg. `exec` ist in Python 3 kein Schlüsselwort mehr und in PyQt6 wird es die Methode mit Unterstrich nicht mehr geben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
23z86
User
Beiträge: 4
Registriert: Montag 22. März 2021, 19:03

Vielen Dank, __blackjack__

Nach deinem Beitrag, etwas Literatur und Probieren hat es super funktioniert. Wie folgt sieht es nun aus:

Code: Alles auswählen

import sys
from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
from PyQt5 import QtWidgets

class StartWindow(QMainWindow):
    def __init__(self):
        super(StartWindow, self).__init__()
        loadUi("start.ui",self)
        self.fz1_button.clicked.connect(self.showAddObjectWindow)
        
    def showAddObjectWindow(self):
        self.mwindow = AddObjectWindow()
        self.mwindow.show()
        
class AddObjectWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(AddObjectWindow, self).__init__()
        loadUi("addobject.ui",self)
        self.abortObject.clicked.connect(self.CloseAddObjectWindow)
        self.saveObject.clicked.connect(self.SaveObject)
        
    def CloseAddObjectWindow(self):
        self.addowindow = AddObjectWindow
        self.close()
    
    def SaveObject(self):
        pass  

def main():
    app = QApplication(sys.argv)
    window1 = StartWindow()
    window1.show()
    app.exec_()
    
if __name__ == '__main__':
    main()
Aber nur weil es funktioniert, heißt es nicht, dass es hundertprozentig richtig ist.
Deine anderen Anmerkungen habe ich leider nicht umgesetzt, da die mich begleitende Fachliteratur den gleichen Stil bevorzugt. Für den Anfang sollte es jedoch reichen, nehme ich an.

Mit freundlichen Grüßen
Benutzeravatar
Dennis89
User
Beiträge: 1123
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

für die Gestaltung deiner Codezeilen sollte folgender Link deine Fachliteratur sein:
https://www.python.org/dev/peps/pep-0008/

Wenn du einen deutschen Aufsatz schreibst, schlägst du die Rechtschreibung auch nicht irgendwo nach, sondern im Duden, der ist dafür gemacht. So ist das hier auch.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@23z86: Bei der Anmerkung dass das importierte `QtWidgets` nirgends verwendet wird, war als Konsequenz angedacht den unnötigen Import zu entfernen, nicht verwirrenderweise *eine* Verwendung des importierten `QMainWindow` unnötigerweise über diesen vorher unbenutzten Import zu machen. Was sollte denn der Sinn davon sein und nach welchem Kriterium wurde entschieden welches `QMainWindow` ersetzt wird? Warum nicht das andere? Warum nicht beide? Oder eben: warum überhaupt‽

`QDialog` wird immer noch importiert und nirgends verwendet. Warum? Könnte es denn verwendet werden? Also benutzt `AddObjectWindow` überhaupt irgend eine Eigenschaft eines `QMainWindow`? Oder könnte das ein Dialog sein/werden? Denn jetzt entspricht das Programm ja nicht mehr der ursprünglichen Beschreibung, dass immer nur eines der beiden Fenster sichtbar sein soll. Wäre bei einem modalen Dialog auch nicht ganz der Fall — da wären dann beide Fenster sichtbar — aber wenn der Dialog aktiv wäre, dann könnte man das andere Fenster wenigstens nicht benutzen. Im Moment kann man man da ja weiterhin auf den Button klicken und neue `AddObjectWindow`-Exemplare erstellen. Wobei die alten dann nicht mehr vom Programm direkt referenzierbar werden, was das Attribut unsinnig macht.

Wenn Deine Literatur noch Python 2 Code zeigt, solltest Du die wirklich austauschen. Noch mal: `super()` braucht keine Argumente. Das war in Python 2 so. Python 2 ist seit über einem Jahr EOL.

`showAddObjectWindow()` ist als Name ein bisschen irreführend. Das erstellt ein komplettes Fensterobjekt und zeigt nicht nur ein bestehendes an, wie das die `show()`-Methode macht.

Attribute sollten nach Ablauf der `__init__()`-Methode alle bestehen und nicht in späteren Methodenaufrufen neu hinzukommen.

Bei Namen kann man ja noch argumentieren das man bei GUI-Code den Qt-Stil verwendet, aber ”PascalCase” verwendet auch Qt nicht für Methodennamen. Das ist bei beiden Stilen Klassen vorbehalten. Weshalb `saveObject` ein sehr ungünstiger Name für den Button ist der die `saveObjekt()`-Methode aufrufen soll.

Für die `CloseAddObjectWindow()` gilt wieder das für Einführen von neuen Attributen in Methoden gesagte. Aber es macht auch überhaupt gar keinen Sinn die Klasse als Attribut auf einem Exemplar dieser Klasse zu setzen. Und das unmittelbar vor dem Schliessen des Fensters‽ Damit ist die komplette Methode überflüssig, denn man könnte da einfach die `close()`-Methode mit dem Button verbinden.

Warum man unsinnige Nummernanhängsel und überflüssige Unterstriche die in neuen Bibliotheksversionen *falsch* werden, nicht einfach weglassen kann, verstehe ich nicht.

Code: Alles auswählen

#!/usr/bin/env python3
import sys
from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog


class StartWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        loadUi("start.ui", self)
        self.fz1_button.clicked.connect(self.createAddObjectWindow)
        self.add_object_window = None

    def createAddObjectWindow(self):
        #
        # FIXME There is nothing to prevent this method being called several
        #   times.  Either this is a programming error or the attribute is
        #   unnecessary.
        #
        #   Also the attribute keeps being bound to the `AddObjectWindow`
        #   instance after that window is destroyed.  Does that make any sense?
        #
        self.add_object_window = AddObjectWindow()
        self.add_object_window.show()


class AddObjectWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        loadUi("addobject.ui", self)
        self.abortButton.clicked.connect(self.close)
        self.saveButton.clicked.connect(self.saveObject)

    def saveObject(self):
        ...


def main():
    app = QApplication(sys.argv)
    window = StartWindow()
    window.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten