Qt5-Designer und Buttonbox in Python verwenden

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
buffi
User
Beiträge: 2
Registriert: Donnerstag 5. Mai 2022, 06:14

Hallo

ich fange gerade an mit Python erste Erfahrungen zu machen. Ich komme von Delphi vor einigen Jahren.
Nun habe ich ein Testprogramm mit den Erfahrungen gemacht und komme an einen Punkt nicht weiter. Ich mache ein Unterformular auf und fange die Buttonbox (im QT-Designer erstellt) ab und schicke das in eine Class um dort die Reaktionen auszuwerten. Box besteht aus OK und Abbruch.

Funktioniert - aber die Ausgabe Print Ok oder Print Abbruch kommt immer sovielmal, wie ich das Unterformular wieder öffne. Also beim ersten Öffnen gar keine Ausgabe - bei zweiten mal ein OK oder Abbruch bei dritten mal zwei OK oder Abbruch usw. Mir kommt es vor als würde das Unterformular gar nicht geschlossen - und immer wieder eine Kopie geöffnet? Aber alles was nach unterformular.exec_() steht wird ausgeführt. Extra dafür die Slider reaktion und die LED-green dafür eingefügt, um das zu testen.

Ich danke erstmal für eure Mühe an meine blöde Frage.

Code: Alles auswählen

import sys

"""import der Paramenter von QT"""
from PyQt5 import QtWidgets, QtCore, QtGui, uic 


"""Klassen definieren"""
app = QtWidgets.QApplication(sys.argv)



"""Öffnen des Unterformular und die Ereignisse"""
class Unterform():
    unterformular = uic.loadUi("test2.ui")
    
class MM():
    
    def Print(self):
        print("OK")
        
class BB():
    
    def Print(self):
        print("Abbruch")    
    
 
     
     

"""Öffnen des Hauptformulars und die Ereignisse"""
class Hauptformular(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        
        """ 1. Laden des Formulars mit name led.ui und der Classe meinMenue übergeben
            2. Lade den toolButton1 und warete auf sein Klick und gehe dann in die Funktion onOK
            3. Frage den verticalSlider auf Änderung ab und gehe dann in die Funktion/Motul SL
            """      
        self.ui = uic.loadUi("led.ui", self)
        
        self.ui.toolButton1.clicked.connect(self.onOK)
        """Button mit blauer LED Starten """
        self.ui.toolButton1.setIcon(QtGui.QIcon('blue-led-on.png'))
        self.ui.verticalSlider_1.valueChanged.connect(self.SL)
        self.ui.verticalSlider_2.valueChanged.connect(self.SL)
        self.ui.verticalSlider_3.valueChanged.connect(self.SL)
        
        
        """ Dem vertikalSlider_1 den Wert 23 übergeben bei Buttondrücken -OK und Unterformular"""
    def onOK(self):
        """Button mit roter LED bei Button Click """
        self.ui.toolButton1.setIcon(QtGui.QIcon('led-red-on.png'))
        self.ui.verticalSlider_1.setValue(23)
        Unterform.unterformular.exec_()   # Kein self weil nicht zur class MeinMenue gehört sondern außerhalb der Klasse
        self.ui.verticalSlider_1.setValue(0)
        """Button mit grün LED bei beenden des Unterfensters """
        self.ui.toolButton1.setIcon(QtGui.QIcon('green-led-on.png'))
        """Buttonbox im Untermenue abfangen und auf OK oder Abbruch prüfen """
          
        Unterform.unterformular.buttonBox.accepted.connect(MM1.Print)
        Unterform.unterformular.buttonBox.rejected.connect(BB1.Print)
        
        
        """ vertikalSlider_1 bis _3 Wert lesen und den Wert an die Variable a bis c übergeben sowie den Wert in der Konsole ausgeben """        
    def SL(self):
        self.a = self.ui.verticalSlider_1.value()
        self.b = self.ui.verticalSlider_2.value()
        self.c = self.ui.verticalSlider_3.value()
        print(" Das ist die erste Variable - Werte des Sliders_1 = ",self.a)
        print(" Das ist die erste Variable - Werte des Sliders_2 = ",self.b)
        print(" Das ist die erste Variable - Werte des Sliders_3 = ",self.c)
        


MM1 = MM()    
BB1 = BB() 

dialog = Hauptformular()
dialog.show()

sys.exit(app.exec_())
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Literale Strings sind keine Kommentare! Kommentare werden mit # eingeleitet. Es gibt soetwas wie Doc-Strings, die eine Klasse oder Funktion dokumentieren, die stehen dann aber als erster Ausdruck in der jeweiligen Funktion.

Variablennamen, Klassen und Funktionen müssen aussagekräftige Namen haben, zwei Großbuchstaben sind das definitiv nicht. Variablen und Methoden werden per Konvention komplett kleingeschrieben, Klassen mit großem Anfangsbuchstaben. Nur Konstanten schreib man KOMPLETT_GROSS.
Benutze keine globalen Variablen, und mißbrauche vor allem nicht Klassen als globale Variablen.
Daher kommt auch Dein Problem.
Die Klassen MM und BB machen so gar keinen Sinn, weil sie keinen Zustand haben.
Die Klasse `Unterform` (wo ist das ular hin?) muß genauso aufgebaut sein, wie die Klasse Hauptformular. Das ui-Objekt darf keine globale Variable sein, sondern muß für jedes Fenster neu erzeugt werden.
Dein jetziger Code verbindet mit der globalen buttonBox jeweils zwei Signale, MM1.Print und BB1.Print und zwar immer NACHDEM der Dialog schon längst wieder geschlossen wurde, deshalb wird das Signal auch beim ersten mal 0mal verarbeitet, beim zweiten mal 1mal, beim dritten 2mal, usw.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@buffi: Zeichenkettenliterale sind keine Kommentare. Kommentare werden in Python mit # gekennzeichnet. Und nur das sind Kommentare.

Zeichenkettenliterale haben an bestimmten stellen im Code die Funktion von „Docstrings“, das heisst die werden vom Compiler und von Werkzeugen um Code zu dokumentieren an diesen Stellen erkannt und in den Objekten unter dem Attribut `__doc__` gespeichert, so dass man sie mit `help()` oder in IDEs als Tooltips, oder eben in einer generierten Dokumentation, lesen kann. An den Stellen wo literale Zeichenketten als Dokumentation erkannt werden, stehen sie *nach* einer Zeile, welche die Definition dessen was dokumentiert werden soll, einleitet. Nicht davor.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Klassen sind keine Container für Funktionen. Die ”Klassen” `Unterform`, `MM`, und `BB` machen so keinen Sinn weil die gar keine Objekte beschreiben, sondern entweder nur eine globale Variable enthalten (`Unterform`) oder eine Funktion (`MM`, `BB`). Wenn eine Klasse keine `__init__()`-Methode hat oder wie in allen drei Fällen hier, nicht mal ein Exemplar davon erstellt wird, sondern nur auf Klassenattribute zugegriffen wird, dann ist da was falsch, weil das nicht der Sinn von Klassen ist. (Von `MM` und `BB` werden zwar Exemplare erstellt, aber mit denen wird nichts gemacht.)

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Qt verwendet eine andere Namenskonvention, die kann man dann für den GUI-Teil auch nehmen, aber dann mit einer `Print()`-Methode oder einem `SL`-Attribut (das keine Konstante ist) auch noch eine dritte Konvention einzuführen ist vielleicht etwas viel. Beziehungsweise wenn man sich dann auch noch so etwas wie `verticalSlider_1` anschaut scheint bei Namen ja irgendwie alles erlaubt zu sein, also gar keine Namenskonvention zu existieren.

Namen sollten auch nicht durchummeriert werden oder kryptische Abkürzungen enthalten oder gar nur daraus bestehen.

Bei `loadUi()` ist es nicht sinnvoll das Ergebnis an ein Attribut von `self` zu binden *und* `self` als Argument beim Aufruf zu übergeben. Entweder das eine *oder* das andere. Da Du immer über das `ui`-Attribut zugreifst, sollte das `self` nicht übergeben werden. Dann muss das `Hauptformular` auch nicht von einem Widget erben.

`exec()` ist ein blockierender Aufruf der Dein Unterformular ausführt. Wenn Du erst *danach* festlegst was denn eigentlich beim Ausführen passieren soll wenn die Buttons gedrückt werden, hat das beim ersten mal natürlich keine Wirkung. Du musst *erst* angeben was beim Druck auf die Buttons passieren soll, und dann den Dialog ausführen.

Dass es immer mehr Ausgaben werden liegt daran das eben *keine* Kopie vom Unterformular verwendet wird, sondern das es das genau *einmal* gibt, und Du bei jedem Druck auf dem Button im Hauptformular eine *weitere* Verbindung von den immer gleichen Unterformular-Buttons zu den Print-Funktionen erstellst. Du müsstest das einmal am Anfang machen.

Attribute sollten nach Ablauf der `__init__()` existieren und nicht erst in anderen Methoden später eingefürht werden. Das ist verwirrend und fehleranfällig. Zumal `a`, `b`, und `c` hier auch gar nicht wirklich gebraucht werden. Das könnten einfach lokale Namen sein, oder auch gar keine Namen, wenn man die Zwischenergebnisse nicht an einen Namen bindet.

Da `exec` in Python 3 kein Schlüsselwort mehr ist, sollte man die `exec()`-Methode statt der `exec_()`-Methode verwenden. Letztere gibt es in PyQt6 nicht mehr.

Zwischenergebniss (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import sys
from functools import partial

from PyQt5 import QtGui, QtWidgets, uic


class Hauptformular:
    def __init__(self):
        self.ui = uic.loadUi("led.ui")

        self.ui.toolButton1.clicked.connect(self.onOK)

        self.ui.toolButton1.setIcon(QtGui.QIcon("blue-led-on.png"))
        self.sliders = [
            self.ui.verticalSlider_1,
            self.ui.verticalSlider_2,
            self.ui.verticalSlider_3,
        ]
        for slider in self.sliders:
            slider.valueChanged.connect(self.onSliderValueChanged)

        self.unterformular = uic.loadUi("test2.ui")
        self.unterformular.buttonBox.accepted.connect(partial(print, "OK"))
        self.unterformular.buttonBox.rejected.connect(
            partial(print, "Abbruch")
        )

    def onOK(self):
        self.ui.toolButton1.setIcon(QtGui.QIcon("led-red-on.png"))
        self.ui.verticalSlider_1.setValue(23)
        self.unterformular.exec()
        self.ui.verticalSlider_1.setValue(0)
        self.ui.toolButton1.setIcon(QtGui.QIcon("green-led-on.png"))

    def onSliderValueChanged(self):
        for number, slider in enumerate(self.sliders, 1):
            print(
                f" Das ist die erste Variable - Werte des Sliders_{number}"
                f" = {slider.value()}"
            )


def main():
    app = QtWidgets.QApplication(sys.argv)

    dialog = Hauptformular()
    dialog.ui.show()

    sys.exit(app.exec())


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
buffi
User
Beiträge: 2
Registriert: Donnerstag 5. Mai 2022, 06:14

DANKE euch beiden.

ich werde jetzt nochmal neu anfangen und mich genauer mit der Strukturierung beschäftigen. Also alles vergessen (großen teil der alten Lernstruktur) von Delphi und auf ein neues!

Erstmal danke für die Kopfwäsche - die wird mich etwas wieder in den Lernzyklus versetzen.

Sorry für eure Zeit und die Mühe.


Der Code funktioniert perfekt!!!!!!!!!!!


Gruß Uwe
Antworten