Zweites Fenster aus Gui-Anwendung aufrufen mit uic.LoadUi

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
johnsilver
User
Beiträge: 3
Registriert: Samstag 17. Februar 2018, 09:33

Samstag 17. Februar 2018, 12:39

Hallo liebe Forumsmitglieder,

ich möchte gerne ein Programm schreiben, dessen Hauptfenster mit dem Qt-Designer erstellt wurde und mittels uic.loadUi eingebunden wird, ebenso ein zweites Fenster, das aufgerufen wird, wenn ein Button oder Menü im Hauptfenster geclickt wird.

Mein Problem ist jetzt, dass wenn ich das Hauptfenster mittels loadUi einbinde (s. Code unten), das Dialogfenster aufgerufen, aber danach die Anwendung beendet wird und auch noch zwei Instanzen bestehen bleiben, die abgebrochen werden müssen.
Wenn ich statt den Befehlen in Zeile 13 und 14 den auskommentierten Code darüber nehme, funktioniert alles wie gewünscht.
Wo liegt mein Fehler?
Vielen Dank schon mal im Voraus für die Unterstützung!

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import *
from PyQt5.uic import *

class First(QMainWindow):
    def __init__(self, parent=None):
        super(First, self).__init__(parent)
        
#        self.pushButton = QPushButton("click me")
#        self.setCentralWidget(self.pushButton)
#        self.pushButton.clicked.connect(self.on_pushButton_clicked)

        self.ui = loadUi("gui/hauptfenster.ui",  self)
        self.ui.pushButton.clicked.connect(self.on_pushButton_clicked)

    def on_pushButton_clicked(self):
        from gui.dialogfenster import Dialogfenster
        self.dialog = Dialogfenster(self)
        self.dialog.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = First()
    main.show()
    sys.exit(app.exec_())
__deets__
User
Beiträge: 3299
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 17. Februar 2018, 13:09

Gewoehn dir bitte sofort ab, importe lokal zu machen. Dazu gibt es nur in sehr speziellen Ausnahmefaellen Gruende zu. Die Zeile

Code: Alles auswählen

 from gui.dialogfenster import Dialogfenster
gehoert an den Anfang, genauso wie alle anderen importe auch.

Dein eigentliches Problem ist simpel: loadUI erwartet als zweites Argument das QWidget (im allgemeinen, du brauchst wohl QDialog), in dem die per designer erstellten GUI-Elemente angelegt werden. Wenn du einen Dialog machen moechtest, dann musst du auch einen erzeugen, und den als Argument uebergeben. So wie du das jetzt machst, braetst du einfach alle widgets in dein bereits bestehendes Hauptfenster - mit unwaegbkaren Konsequenzen.

EDIT: und noch ein Nachtrag: du benutzt lokale Pfade fuer die ui-Dateien. Das wird dir auf die Fuesse fallen, wenn du mal versuchst, das ganze als fertiges Programm auszuliefern. Stattdessen solltest du etwas wie

Code: Alles auswählen

ui_path = os.path.join(os.path.dirname(__file__), "pfad/zur/ui/datei.ui")
machen. Dann funktioniert das auch wenn das current workingdirectory im Wald steht.
johnsilver
User
Beiträge: 3
Registriert: Samstag 17. Februar 2018, 09:33

Samstag 17. Februar 2018, 14:07

Danke für die Hinweise!
Das mit dem Import weiß ich, dass das schlechter Stil ist, habe es nur zu Demonstrationszwecken an dieser Stelle mit eingefügt.

Das Dialog-Widget ist in der Klasse "Dialogfenster", was ja auch funktioniert.

Code: Alles auswählen

from PyQt5.uic import *
from PyQt5.QtWidgets import QDialog

class Dialogfenster(QDialog):
    
    def __init__(self,  parent = None):
        super(Dialogfenster,  self).__init__(parent)
        #QDialog.__init__(self, parent)
        self.dialogui = loadUi("gui/dialog.ui",  self)
loadUI erwartet als zweites Argument das QWidget (im allgemeinen, du brauchst wohl QDialog), in dem die per designer erstellten GUI-Elemente angelegt werden.
Ich gebe doch aber an zweiter Stelle immer self mit, an das gebunden werden soll. Wenn ich das weglasse, wird's ganz chaotisch.
Und wenn ich

Code: Alles auswählen

self.ui = loadUi("gui/hauptfenster.ui",  QDialog)
schreibe, dann kommt die Meldung
unhandled TypeError
"('Wrong base class of toplevel widget', (<class 'PyQt5.QtCore.pyqtWrapperType'>, 'QMainWindow'))"


Ich fürchte, ich habe die grundsätzliche Logik noch nicht ganz verstanden. :(
__deets__
User
Beiträge: 3299
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 17. Februar 2018, 16:06

Mein Fehler, es muss wohl ein QMainWindow sein statt QDialog.

Aber das Problem bleibt: self ist das bereits existierende MainWindow. Was soll denn dabei gutes rum kommen, dem eine zweite UI reinzudruecken? Dann hast du all die Widgets im gleichen Fenster.

Du musst eine *Instanz* (nicht nur die Klasse, so wie du das gemacht hast) von QMainWindow reingeben. Schau dir mal die pyqt-Doku an, da findest du bestimmt Beispiele fuer Dialoge, die du adaptieren kannst.
__deets__
User
Beiträge: 3299
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 17. Februar 2018, 18:19

Weil ich durch Zufall genau das gerade mache, hier der Code den ich vor 10 Minuten schrub:

Code: Alles auswählen

    main_window = QtWidgets.QMainWindow()
    loadUi(os.path.join(os.path.dirname(__file__), "mainwindow.ui"), main_window)

    main_window.show()
johnsilver
User
Beiträge: 3
Registriert: Samstag 17. Februar 2018, 09:33

Samstag 17. Februar 2018, 21:10

__deets__ hat geschrieben:Weil ich durch Zufall genau das gerade mache, hier der Code den ich vor 10 Minuten schrub:

Code: Alles auswählen

    main_window = QtWidgets.QMainWindow()
    loadUi(os.path.join(os.path.dirname(__file__), "mainwindow.ui"), main_window)

    main_window.show()
Kommt das dann in die "Main"-Funktion rein bzw. muss außerhalb einer Klasse sein?
Ich wollte das halt in einer Klasse haben, damit ich dort auch noch zusätzliche Funktionen ergänzen kann. In der Main sollte nur der Start, die Erzeugung des Hauptfensters und der Anzeige-Aufruf sein.

Code: Alles auswählen

    app = QApplication(sys.argv)
    main = First()
    main.show()
    sys.exit(app.exec_())
Die First-Klasse (vom Typ QMainWindow) wird mit der Ui-Datei initialisiert und kann um Funktionen und Signale erweitert werden, wie z.B.:

Code: Alles auswählen

        self.pushButton.clicked.connect(self.on_pushButton_clicked)

    def on_pushButton_clicked(self):
Was ich nicht verstehe: Warum funktioniert bei meinem Code das mit dem Dialog, nur mit dem Hauptfenster nicht?

__deets__ hat geschrieben:Schau dir mal die pyqt-Doku an, da findest du bestimmt Beispiele fuer Dialoge, die du adaptieren kannst.
Wollte ich schon machen, aber die Website ist zur Zeit irgendwie down bzw. zieht um. Kann das sein?
Antworten