Seite 1 von 1

AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Samstag 10. Oktober 2020, 10:37
von PyNeo
Hallo Mitglieder,
ich versuche dem Nr.1 oder Nr.2 Tutorial zu folgen und mit Hilfe von Qt Designer nachzustellen.
Mein vorgehen:
1: Erstellen im Qt Designer > mytest.ui
2: Mit dem Befehl (Linux) pyuic5 -x mytest.ui -o mytest.py ins Python "übersetzen"
3: In der erstellten Python mytest.py Datei setze ich für jeweils ein radioButton die methode > self.radiobtn1.toggled.connect(self.onRadioBtn) ein.
4: Sobald ich an den Punkt komme >:

Code: Alles auswählen

def onRadioBtn(self):
        radioBtn = self.sender()
        if radioBtn.isChecked():
            self.label.setText("You Have Selected " + radioBtn.text())
            
erhalte ich diese Fehlermeldung >:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/.../.../../mytest.py", line 59, in onRadioBtn
    radioBtn = self.sender()
AttributeError: 'Ui_Dialog' object has no attribute 'sender'
Es funktioniert nur wenn ich für jeden einzelnen radioButton eine eigene methode setze und einzeln anspräche. :oops:

Code: Alles auswählen

#    def onRadioBtn(self):
#        if self.radiobtn1.isChecked():
#           self.label.setText("You Have Selected " + self.radiobtn1.text())

#    def onRadioBtn1(self):
#        if self.radiobtn2.isChecked():
#            self.label.setText("You Have Selected " + self.radiobtn2.text())

USW
Wo ist mein Fehler, was mache ich falsch.
Bitte Hilfe! Viel Dank im Voraus!

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Samstag 10. Oktober 2020, 14:42
von __blackjack__
@PyNeo: Beide Lösungen sind nicht gut. `sender()` weil es wissen über Objekte auf der anderen Seite der Verbindung erfordert was nicht sein sollte, und es funktioniert auch nicht unter allen Umständen. Zu beiden Punkten hat die Qt-Dokumentation eine deutliche Warnung bei dieser Methode. Und alle Radiobuttons mit einer eigenen Methode verbinden die aber alle die gleiche Struktur haben ist unnötige Schreibarbeit/kopierter Code, mit den Konsequenzen/Problemen die sich daraus ergeben.

Man würde hier eine Methode schreiben, die den entsprechenden Wert, den man benötigt und den Zustand als Argumente erhält. Den Wert bindet man dann per `functools.partial()` und den Rückgabewert davon verbindet man mit dem `stateChanged`-Signal.

Du änderst da in generiertem Quelltext bei dem oben eine Warnung steht, dass man genau das nicht macht. Ich würde auch generell keinen Quelltext aus der *.ui-Datei generieren, sondern die zur Laufzeit im Programm laden. Siehe das `PyQt5.uic`-Modul.

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Montag 12. Oktober 2020, 09:16
von PyNeo
__blackjack__ hat geschrieben: Samstag 10. Oktober 2020, 14:42 Man würde hier eine Methode schreiben, die den entsprechenden Wert, den man benötigt und den Zustand als Argumente erhält. Den Wert bindet man dann per `functools.partial()` und den Rückgabewert davon verbindet man mit dem `stateChanged`-Signal.

Du änderst da in generiertem Quelltext bei dem oben eine Warnung steht, dass man genau das nicht macht. Ich würde auch generell keinen Quelltext aus der *.ui-Datei generieren, sondern die zur Laufzeit im Programm laden. Siehe das `PyQt5.uic`-Modul.
Danke für die Erläuterung.

Wenn ich richtig verstanden habe soll man die *.ui Datei nicht in *.py generieren und direkt Laden > w = uic.loadUi("*.ui") ?

Ist es das was du gemeint hast? (aber mit radioButton)

Code: Alles auswählen

  from functools import partial

def checkBoxStateChanged(self, i):
    checkBox = self.findChild(QCheckBox, 'checkBox' + str(i))
    print i, checkBox.isChecked()
  

for i in range(1, 11):
    checkBox = self.findChild(QCheckBox, 'checkBox' + str(i))
    checkBox.stateChanged.connect(partial(self.checkBoxStateChanged, i))
  
  
In der Theorie hab ich das verstanden. Leider finde ich nicht die richtige Umsetzung für mein vorhaben.
Hast du oder kannst du eventuell, die richtige Reihenfolge darstellen?

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 09:10
von PyNeo
__blackjack__ hat geschrieben: Samstag 10. Oktober 2020, 14:42......Den Wert bindet man dann per `functools.partial()` und den Rückgabewert davon verbindet man mit dem `stateChanged`-Signal.
................ Ich würde auch generell keinen Quelltext aus der *.ui-Datei generieren, sondern die zur Laufzeit im Programm laden. Siehe das `PyQt5.uic`-Modul.
Ich habe versucht deine Tipps zu befolgen:
1: Die *.ui Datei wird durch> from PyQt5.uic import loadUi / loadUi('test.ui', self) in eine *.py Datei geladen/importiert.
2: `functools.partial()` und `stateChanged`-Signal wird wie folgt angewendet >

Code: Alles auswählen

from functools import partial

        self.radioButton1.clicked.connect(partial(self.checkRadioButtonState))

        for i in range(1, 5):
            radioButton = self.findChild(QRadioButton, 'radioButton' + str(i))
            radioButton.clicked.connect(partial(self.checkRadioButtonState, i))
    def checkRadioButtonState(self, i):
        radioButton = self.findChild(QRadioButton, 'radioButton' + str(i))
Das ganze sieht so aus>

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QRadioButton
from PyQt5.uic import loadUi
from functools import partial


class CodeCreator(QMainWindow):
    def __init__(self):
        super(CodeCreator, self).__init__()
        loadUi('test.ui', self)
        self.radioButton1.clicked.connect(partial(self.checkRadioButtonState))

        for i in range(1, 5):
            radioButton = self.findChild(QRadioButton, 'radioButton' + str(i))
            radioButton.clicked.connect(partial(self.checkRadioButtonState, i))
    def checkRadioButtonState(self, i):
        radioButton = self.findChild(QRadioButton, 'radioButton' + str(i))
        if self.radioButton1.isChecked():
            file = open("output.txt", "a")
            file.write("Code für: " + self.radioButton1.text() + "\n")
            file.close()
            self.groupBoxCode.setTitle("Code für: " + self.radioButton1.text())
        if self.radioButton2.isChecked():
            file = open("output.txt", "a")
            file.write("Code für: " + self.radioButton2.text() + "\n")
            file.close()
            self.groupBoxCode.setTitle("Code für: " + self.radioButton2.text())
        if self.radioButton3.isChecked():
            file = open("output.txt", "a")
            file.write("Code für: " + self.radioButton3.text() + "\n")
            file.close()
            self.groupBoxCode.setTitle("Code für: " + self.radioButton3.text())
        if self.radioButton4.isChecked():
            file = open("output.txt", "a")
            file.write("Code für: " + self.radioButton4.text() + "\n")
            file.close()
            self.groupBoxCode.setTitle("Code für: " + self.radioButton4.text())


FILENAME = "output.txt"

if __name__ == '__main__':
    app = QApplication(sys.argv)
    cc = CodeCreator()
    cc.show()
    app.exec_()
Jetzt meine Frage:
1: Ist es so richtig umgesetzt?
2: Kann man es noch weiter verkleinern/ kombinieren? Es sind ja doch fast immer die selben befehle die ausgeführt werden, wenn man den entsprechenden radioButton drückt.
3: Eventuell eine alternative dazu?

Bitte Hilfe! Viel Dank im Voraus!

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 09:29
von __deets__
Statt dem Index kannst du auch gleich den Button mit Partial anbinden. Womit die if-Kaskade entfällt. Und Open/close macht man stattdessen mit dem with-statement.

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 11:45
von PyNeo
__deets__ hat geschrieben: Sonntag 18. Oktober 2020, 09:29 Statt dem Index kannst du auch gleich den Button mit Partial anbinden. Womit die if-Kaskade entfällt. Und Open/close macht man stattdessen mit dem with-statement.
Ein konkretes Beispiel zur Umsetzung?
Wehre echt super!
Danke!

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 11:46
von __deets__
radioButton.clicked.connect(partial(self.checkRadioButtonState, radioButton))

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 12:01
von Sirius3
In `__init__` wird noch ein Signal an radioButton1 gebunden, was gar nicht funktioniert.
`radioButton` wird in `checkRadioButtonState` doch gar nicht benutzt.
Du hast also zwei Konzepte gemischt und nutzt keines richtig.
Also entweder man benutzt das Argument:

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QRadioButton
from PyQt5.uic import loadUi
from functools import partial

OUTPUT_FILENAME = "output.txt"

class CodeCreator(QMainWindow):
    def __init__(self):
        super(CodeCreator, self).__init__()
        loadUi('test.ui', self)
        self.radiobuttons = [
            self.radioButton1, self.radioButton2, self.radioButton3, self.radioButton4
        ]
        for radiobutton in self.radiobuttons:
            radioButton.clicked.connect(partial(self.checkRadioButtonState, radiobutton))

    def checkRadioButtonState(self, radiobutton):
        if radiobutton.isChecked():
            with open(OUTPUT_FILENAME, "a") as output:
                text = radiobutton.text()
                output.write(f"Code für: {text}\n")

def main():
    app = QApplication(sys.argv)
    cc = CodeCreator()
    cc.show()
    app.exec_()

if __name__ == '__main__':
    main()
Oder Du braucht gar kein partial, weil Du immer alle Radiobuttons checkst:

Code: Alles auswählen

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

OUTPUT_FILENAME = "output.txt"

class CodeCreator(QMainWindow):
    def __init__(self):
        super(CodeCreator, self).__init__()
        loadUi('test.ui', self)
        self.radiobuttons = [
            self.radioButton1, self.radioButton2, self.radioButton3, self.radioButton4
        ]
        for radiobutton in self.radiobuttons:
            radioButton.clicked.connect(self.checkRadioButtonState)

    def checkRadioButtonState(self):
        for radiobutton in self.radiobuttons:
            if radiobutton.isChecked():
                with open(OUTPUT_FILENAME, "a") as output:
                    text = radiobutton.text()
                    output.write(f"Code für: {text}\n")

def main():
    app = QApplication(sys.argv)
    cc = CodeCreator()
    cc.show()
    app.exec_()

if __name__ == '__main__':
    main()

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 12:30
von PyNeo
Sirius3 hat geschrieben: Sonntag 18. Oktober 2020, 12:01 Also entweder man benutzt das Argument:
..................
Oder Du braucht gar kein partial, weil Du immer alle Radiobuttons checkst:
Vielen Dank für die Aufklärung, hab mir schon gedacht das was spanisch ist.
Leider krieg ich eine fehler meldug egal ob mit Argument

Code: Alles auswählen

line 16, in __init__
    radioButton.clicked.connect(partial(self.checkRadioButtonState, radiobutton))
NameError: name 'radioButton' is not defined
oder ohne partial...

Code: Alles auswählen

line 15, in __init__
    radioButton.clicked.connect(self.checkRadioButtonState)
NameError: name 'radioButton' is not defined
wenn ich self. und / oder radioButton1 hinzufüge funktioniert auch nur der erste... und als output nur ein vierfaches: Code für: Button1
Code für: Button1
Code für: Button1
Code für: Button1

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 12:39
von Sirius3
Ich traue Dir zu den Schreibfehler radioButton statt radiobutton selbst zu entdecken.
Wenn Du nur stumpf ein Programm kopierst, statt es durchzulesen, wirst Du nichts lernen.

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 13:01
von PyNeo
Sirius3 hat geschrieben: Sonntag 18. Oktober 2020, 12:39 Ich traue Dir zu den Schreibfehler radioButton statt radiobutton selbst zu entdecken.
Wenn Du nur stumpf ein Programm kopierst, statt es durchzulesen, wirst Du nichts lernen.
Vielen Dank!
Ich tue mein bestes und versuche das meiste zu verstehen und umsetzen. Ich habe versucht es mit self. und radioButton1 zu korrigieren (obwohl es total falsch war). In der Aufregung ist mir der Schreibfehler nicht aufgefallen.
Aber ohne gezielte Hilfe werde ich auch nicht schlauer. Bei google suche, viel lesen, verstehen und selbst zu schreiben bin ich NICHT abgeneigt! Ob es am ende richtig ist kann ich nicht wissen. Jetzt weis ich für solche Sachen beschied und kann es für die Zukunft es selbst anwenden.
Bin mir sicher das von mir noch 1000 dumme Fragen in der Zukunft kommen werden! :shock:

Ich bin sehr dankbar für jede Hilfe! Vielen Dank!

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 17:28
von PyNeo
Sirius3 hat geschrieben: Sonntag 18. Oktober 2020, 12:39 Wenn Du nur stumpf ein Programm kopierst, statt es durchzulesen, wirst Du nichts lernen.
Wahrscheinlich wieder eine nicht besonders schlaue Frage ohne nach zu forschen aber, in den letzten zielen steht jetzt:

Code: Alles auswählen

def main():
    app = QApplication(sys.argv)
    cc = CodeCreator()
    cc.show()
    app.exec_()

if __name__ == '__main__':
    main()
der unterschied zu vorherigen:

Code: Alles auswählen

if __name__ == '__main__':
    app = QApplication(sys.argv)
    cc = CodeCreator()
    cc.show()
    app.exec_()
es funktioniert zwar beides aber was ist das richtige oder bessere? Im Vorfeld aus vielen andere Tutorials kenne ich nur "meine Variante"

Re: AttributeError: 'Ui_Dialog' object has no attribute 'sender'

Verfasst: Sonntag 18. Oktober 2020, 18:04
von Sirius3
Die obere Variante ist die richtige, weil unten trotzdem noch globale Variablen erzeugt werden.