Integrieren und Iterieren eines QtDesigner Widgets in ein QMainWindow

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
JoeBlack
User
Beiträge: 4
Registriert: Dienstag 25. Mai 2021, 20:34

Hallo zusammen,
ich bin, wie so viele, ein kleiner Frischling sowohl auf dem Gebiet Python/Programmieren als auch im speziellen pyQt.

Mein Ziel ist es eine Lagertankverwaltung zu programmieren. Die Hauptansicht hierfür soll eine Übersicht mit Kacheln sein, welche entsprechende Tankinformationen enthalten um somit einen Überblick über die Tanks und ihren Zustand zu haben.

Das ganze GUI will ich überwiegend mit dem Qt Designer basteln.

Mein Problem ist jetzt, wie ich dynamisch die entsprechenden Kacheln (seperat ertstellte Widgets) in meinen Frame im MainWindow geladen bekomme.

Ich bin seit einigen Tagen auf der Suche nach sinnvollen Lösungen, aber bisher leider noch nicht fündig geworden.

Ich habe mein MainWindow über MainWindow_ui.py in mein main.py importiert und als Klasse Initialisiet.
Hier lassen sich ja auch rel. einfach Widget per Code einfügen etc.

Mein Gedanke war jetzt, über das Initialisieren einer zweiten Klasse "wid", welche ein Widget enthält, dieses entsprechend in mein MainWindow zu integrieren.
Somit hätte ich die möglichkeit meine Daten für jeden Tank individuell in das Widget zu laden und dieses entsprechend oft, je nach Tankanzahl, in mein MainWindow zu platzieren.

Leider musste ich feststellen, dass das so wohl nicht geht oder ich es einfach nicht hinbekomme. Bei einige Beiträgen bin ich darauf gestoßen, dass man Widgets im Designer Promoten kann/muss, leider würde sich damit aber noch nicht die Problematik lösen, dass die Anzahl der zu ladenden Elemente variieren kann.

Code: Alles auswählen


main.py

import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg

from MainWin import Ui_MainWindow
from wid import Ui_wid


class MainWindow(qtw.QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)


        self.ui.pushButton.clicked.connect(lambda: self.addwid())


    def addwid(self):

        self.ui.verticalLayout.addWidget(wid())



class wid(qtw.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.u2 = Ui_wid()
        self.u2.setupUi(self)
        #self.show()

if __name__ == '__main__':

    app=qtw.QApplication(sys.argv)
    w=MainWindow()
    w.show()

    sys.exit(app.exec_())

Code: Alles auswählen


wid.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_wid(object):
    def setupUi(self, wid):
        wid = QtWidgets.QWidget()
        wid.setObjectName("wid")
        wid.resize(400, 300)
        wid.setStyleSheet("background-color: rgb(0, 255, 0);")

        self.retranslateUi(wid)
        QtCore.QMetaObject.connectSlotsByName(wid)

    def retranslateUi(self, wid):
        _translate = QtCore.QCoreApplication.translate
        wid.setWindowTitle(_translate("wid", "Form"))

Code: Alles auswählen


MainWin.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setObjectName("widget")
        self.verticalLayout.addWidget(self.widget)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Add Widget"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Ich verstehe nicht ganz, warum ich eine Klasse nicht als Widget integrieren kann, wenn ich jedoch den Aufbau des Widgets direkt als variable übergebe funktioniert es ohne Probleme.

Hoffentlich ist es nicht zu wirr und ihr versteht was ich meine :P

Vielen lieben Dank schonmal für eure Hilfe und eure Ideen!


Die Idee ist sich das Widget für die Tanks später noch ohne Probleme mit dem Designer umgestalten zu können.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Ist das Python Code, den der Designer erstellt hat? Das sieht mir verdächtig danach aus.
Wenn dem so ist, wäre der erste Schritt von .py Dateien zu .ui Dateien zu wechseln.
Du schreibst die Klasse und in der __init__ lädst du dann mit uic (IIRC) die .ui.
JoeBlack
User
Beiträge: 4
Registriert: Dienstag 25. Mai 2021, 20:34

Moin,
Danke sparrow für deine Antwort und deine Hilfe.
Mein Ziel war eig. nicht uic.loadUi zu nutzen sondern den Designer Code voher auf Python Code zu Compilieren. Dazu hatte ich ganz seriöse YouTube Tut's gefunden gehabt. Ich dachte, es ist im großer und ganzen etwas korrekter bzw. stabiler hinterher.

Aber ich hab das ganze mal umgebaut zu deinem Vorschlag.

Leider weiß ich nicht für was das IIRC steht, und bei Googeln konnte auch nichts dazu finden auf die schnelle. Aber ich denke mal du meinst es wie folgt:

Code: Alles auswählen

import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import uic


class MainWindow(qtw.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        uic.loadUi('MainWin.ui', self)


        self.pushButton.clicked.connect(self.addwid)


    def addwid(self):

        self.verticalLayout.addWidget(Wid())



class Wid(qtw.QWidget):
    def __init__(self):
        super(Wid, self).__init__()

        uic.loadUi('wid.ui', self)

if __name__ == '__main__':

    app=qtw.QApplication(sys.argv)
    w=MainWindow()
    #w=Wid()
    w.show()
    
    sys.exit(app.exec_())

Die Fenster lassen sich beide schonmal anzeigen. Auch wenn ich auf den PushButton klicke wird kein Fehler geworfen, dass ist schonmal gut. Aber das Widget bisher leider nicht in das MainWindow geladen oder zumindest nicht angezeigt.


Btw: Für alle Newbies wie mich. Bin auf ein guten Link gestoßen über die unterschiedliche Initialisierungsmethoden für ein QtDesigner GUI: https://doc.bccnsoft.com/docs/PyQt5/designer.html
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@JoeBlack: Die Qt-Module unter solchen Abkürzungen zu importieren ist ungewöhnlich.

`super()` braucht keine Argumente.

Namen sollte man nicht kryptisch abkürzen. Wenn man `Widget` meint, sollte man nicht nur `Wid` schreiben. Einbuchstabige Namen sind selten gute Namen.

Worte innerhalb eines Namens (ausser bei Klassennamen) werden in Python per Konvention durch unterstriche getrennt. Qt verwendet an der Stelle `camelCase`. Das kann man für GUI-Code auch für Attribute verwenden, wenn man da ein einheitliches Bild zu den Namen haben möchte, die von Qt kommen.

Statt `QApplication.exec_()` sollte man `QApplication.exec()` in neuem Code verwenden. `exec_()` mit Unterstrich ist veraltet und gibt es in PyQt6 nicht mehr.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import sys

from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget


class Widget(QWidget):
    def __init__(self):
        super().__init__()
        uic.loadUi("wid.ui", self)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi("MainWin.ui", self)
        self.pushButton.clicked.connect(self.add_widget)

    def add_widget(self):
        self.verticalLayout.addWidget(Widget())


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Und mit folgenden *.ui-Dateien funktioniert das bei mir:

wid.ui:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>326</width>
    <height>52</height>
   </rect>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="label">
     <property name="font">
      <font>
       <pointsize>24</pointsize>
      </font>
     </property>
     <property name="frameShape">
      <enum>QFrame::Box</enum>
     </property>
     <property name="text">
      <string>Platzhalter für Inhalt</string>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>
MainWin.ui:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>102</width>
    <height>102</height>
   </rect>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>Add widget</string>
      </property>
     </widget>
    </item>
    <item>
     <layout class="QVBoxLayout" name="verticalLayout"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>102</width>
     <height>27</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
JoeBlack
User
Beiträge: 4
Registriert: Dienstag 25. Mai 2021, 20:34

Moin,

vielen lieben Dank __blackjack__ fü die große Hilfe!!! Tatsächlich, es funktioniert, cool!! 8)

Die Abkürzungen für die Qt-Module hatte ich von Alan D Moore aus seinen YouTube Videos abgeschaut.

Tatsächlich hätte es vermutlich auch früher schon geklappt, aber damit ein Widget angezeigt wird, muss es wohl mind. eine Sache beinhalten wie es scheint. Das Ausgangs-Widget im Designer zu erstellen und die Hintergrundfarbe zu ändern bzw. eine min. größe festzulegen reicht wohl nicht aus.

Danke euch beiden, jetzt komme ich endlich weiter :P


Beste grüße,

Joe
Antworten