AttributeError: 'Ui_MainWindow' object has no attribute 'karte_textfeld'

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Hallo Zusammen,

folgendes Problem macht mich wahnsinnig. Ich kriege es nicht gelöst:

Ich möchte die letzte Funktion

Code: Alles auswählen

    def changeElements(self, kartentext):
        self.karte_textfeld.setText(kartentext)
der Klasse Ui_Mainwindow von einer anderen Klasse aufrufen. Der Aufruf in dieser anderen Klasse sieht so aus:

Code: Alles auswählen

    def green(self):
        """ Random choice from Card Database """
        
        # Random Inhalt 
        self.connection = sqlite3.connect("karten.db")
        self.cursor_show = self.connection.cursor()
        self.cursor_show.execute("SELECT * FROM greencard")
        self.row = self.cursor_show.fetchall()

        self.zufallsliste = [element for element in self.row ]
        print("Zufallsliste", self.zufallsliste)
        self.zufall = choice(self.zufallsliste)
        print("Zufalls Element", self.zufall)
        print(self.zufall[1])
        self.cursor_show.close()

        self.kartentext = self.zufall[1]

        instanceonMainframe = Ui_MainWindow()
        instanceonMainframe.changeElements(self.kartentext)
Selbst wenn ich von der Klasse Ui_Mainwindow erben lasse, erhalte ich den AttributeError. Gäbe es in dieser PyQT5 Klasse einen Konstruktor, dann könnte ich diesen aufrufen und dann sollte die Logik auch gehen. Mit dieser setupUi(self, MainWindow) Funktion komme ich nicht klar. Wie kann ich Widgets dieser Klasse von außen verändern?

Code: Alles auswählen

 
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 634)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.red = QtWidgets.QPushButton(self.centralwidget)
        self.red.setGeometry(QtCore.QRect(520, 30, 171, 271))
        self.red.setText("")
        self.red.setObjectName("red")
        self.red.setStyleSheet("background-color: red")
        self.green = QtWidgets.QPushButton(self.centralwidget)
        self.green.setGeometry(QtCore.QRect(120, 30, 171, 271))
        self.green.setText("")
        self.green.setStyleSheet("background-color: green")
        self.green.setObjectName("green")
        self.yellow = QtWidgets.QPushButton(self.centralwidget)
        self.yellow.setGeometry(QtCore.QRect(320, 30, 171, 271))
        self.yellow.setText("")
        self.yellow.setObjectName("yellow")
        self.yellow.setStyleSheet("background-color: yellow")
        self.karte_textfeld = QtWidgets.QTextBrowser(self.centralwidget)
        self.karte_textfeld.setGeometry(QtCore.QRect(120, 360, 571, 151))
        self.karte_textfeld.setObjectName("karte_textfeld")
        self.readme = QtWidgets.QPushButton(self.centralwidget)
        self.readme.setGeometry(QtCore.QRect(350, 530, 89, 25))
        self.readme.setObjectName("readme")
        self.line = QtWidgets.QFrame(self.centralwidget)
        self.line.setGeometry(QtCore.QRect(120, 330, 571, 16))
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(120, 310, 571, 20))
        self.label.setObjectName("label")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        self.menuNeue_Karte_erstellen = QtWidgets.QMenu(self.menubar)
        self.menuNeue_Karte_erstellen.setObjectName("menuNeue_Karte_erstellen")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.action = QtWidgets.QAction(MainWindow)
        self.action.setObjectName("action")
        self.menuNeue_Karte_erstellen.addAction(self.action)
        self.menubar.addAction(self.menuNeue_Karte_erstellen.menuAction())

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Mein Kartenspiel"))
        self.readme.setText(_translate("MainWindow", "Vorlesen?"))
        self.menuNeue_Karte_erstellen.setTitle(_translate("MainWindow", "Neue Karte erstellen"))
        self.action.setText(_translate("MainWindow", "Anlegen"))

    def changeElements(self, kartentext):
        self.karte_textfeld.setText(kartentext)
        
        
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 würde mich riesig freuen, wenn ihr mir weiterhelfen könnt.

Gruß,
m.g.o.d
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Achso, noch eine wichtige Anmerkung: Ich instanziere die Klasse natürlich vorher schon:

Code: Alles auswählen

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    dialog = GameplayGui() 
    error_dialog = QtWidgets.QErrorMessage() 
    dialog.show()
    sys.exit(app.exec_())
Und innerhalb dieser instanzierten Klasse bilde ich eine Instanz auf die Klasse Ui_MainWindow()

Code: Alles auswählen

    def start(self):
        self.window = QtWidgets.QMainWindow()
        self.ui = Ui_MainWindow()       # Instance on mainframe
        self.ui.setupUi(self.window)
        self.window.show()
        self.Game = Gameplay()          # Instance on Gameplay
        self.Game.createdatabase()
        self.carddialog = Ui_Dialog()   # Instance on frame 2
Sirius3
User
Beiträge: 17825
Registriert: Sonntag 21. Oktober 2012, 17:20

Erzeuge keinen Python-Code aus ui-Dateien, und vor allem, ändere diese Datei nicht. Fenster werden direkt aus ui-Dateien erzeugt und mit loadui geladen.
Wenn Du etwas aus einer anderen Klasse heraus aufrufen willst, brauchst Du eine Referenz auf diese Instanz.
Da Du den Code zu dieser Klasse nicht zeigst, kann man dazu aber auch nichts sagen.

Nicht alles muß an self gebunden werden.
cursor_show ist garantiert nur innerhalb der Funktion gültig, hat also nichts an self zu suchen. connection auch nicht, wenn innerhalb der Funktion die Verbindung erzeugt wird. row wohl auch nicht; row ist schon eine Liste, zufallsliste also unnötig (die würde man auch per list(row) erzeugen).
zufall gehört auch nicht an self gebunden, zufall ist auch ein schlechter Name, denn das ist ja ein Element, oder besser gesagt eine greencard.
Ich denke, kartentext gehört auch nicht an self gebunden.
Wie oben schon geschrieben, ein neues UI-Objekt zu erzeugen ist falsch, Du mußt dieses Objekt übergeben.
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Hallo Sirius,

okay ich habe nun mein Problem in ein leichteres Programm umgesetzt:

Code: Alles auswählen

from PyQt5 import QtWidgets, QtCore, uic 

import sys


class Gui(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi("trash.ui", self)
        self.ui.setWindowTitle("Name")
        self.ui.pushButton.clicked.connect(self.init)
   
        
    def init(self):
        print("Erzeugung einer Instanz auf Operations:")
        instanz1 = Operations()
        instanz1.action()


class Operations(Gui):
    def __init__(self):
        super().__init__()


    def action(self):
        print("Test Variante 1 über Instanz")
        instanz2 = Gui()
        instanz2.ui.textEdit.setText("geschafft!")
        print("Test Variante 2 über self")
        self.ui.textEdit.setText("geschafft!")





if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    dialog = Gui() 
    error_dialog = QtWidgets.QErrorMessage() 
    dialog.show()
    sys.exit(app.exec_())

Ich bekomme jetzt zwar keine Fehlermeldung, allerdings wird das textEdit-Objekt dennoch nicht mit dem text "geschafft" versehen. Warum funktioniert das nicht? Ich kann diese Änderung nur im Konstruktor von class Gui selbst machen, aber anscheinend nirgendwo anders.

update: okay der grundsätzliche Datenaustausch geht, was NICHT geht, ist den String auf dem Widget auszugeben. Das ist mir bisher nur im Konstruktor gelungen :-/ i dont understand it.
Zuletzt geändert von m.g.o.d am Montag 14. September 2020, 18:54, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17825
Registriert: Sonntag 21. Oktober 2012, 17:20

Du machst hier folgendes: Erzeuge das Fenster dialog als Instanz der Klasse Gui und zeige sie an.
Soweit ok.
Beim Knopfdruck wird ein neues Fenster erzeugt, diesmal als Instanz von Operations und action aufgerufen; hier erzeugst Du noch ein Fenster, wieder als Instanz von Gui. Und in diesem dritten Fenster setzt Du den Text. Dann wird das dritte Fenster und dann das zweite Fenster wieder zerstört, ohne dass sie jemals eingezeigt wurden.

Warum willst Du überhaupt eine weitere Klasse habe? Operations macht im jetzigen Zustand keinen Sinn.

Das ist einfacher:

Code: Alles auswählen

class Gui(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi("trash.ui")
        self.ui.setWindowTitle("Name")
        self.ui.pushButton.clicked.connect(self.action)
   
    def action(self):
        self.ui.textEdit.setText("geschafft!")
Bei loadUI weist man entweder den Rückgabewert einem Attribut zu, oder übergibt self. Aber niemals beides.
Benutzeravatar
__blackjack__
User
Beiträge: 13241
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@m.g.o.d: Das `Gui`-Objekt das Du in `Operations.action()` erstellst ist ein anderes als das was Du angezeigt hast. Du darfst da kein *neues* Objekt erstellen sondern musst das vorhandene, angezeigte verwenden. Also entweder der `action()`-Methode als Argument mitgeben, oder dem `Operations`-Objekt beim erstellen und dann an ein Attribut binden.

Eine `__init__()` die einzig die `__init__()` der Basisklasse aufruft kann man sich sparen. Das passiert automatisch wenn es keine `__init__()` gibt.

Und um da gleich mal den nächsten Fehler zu vermeiden: Der Code in dem ``if __name__ == ...``-Zweig muss in einer Funktion verschwinden, damit Du nicht anfängst auf globale Variablen zuzugreifen.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Sirius3 hat geschrieben: Montag 14. September 2020, 18:42 Du machst hier folgendes: Erzeuge das Fenster dialog als Instanz der Klasse Gui und zeige sie an.
Soweit ok.
Beim Knopfdruck wird ein neues Fenster erzeugt, diesmal als Instanz von Operations und action aufgerufen; hier erzeugst Du noch ein Fenster, wieder als Instanz von Gui. Und in diesem dritten Fenster setzt Du den Text. Dann wird das dritte Fenster und dann das zweite Fenster wieder zerstört, ohne dass sie jemals eingezeigt wurden.

Warum willst Du überhaupt eine weitere Klasse habe? Operations macht im jetzigen Zustand keinen Sinn.

Das ist einfacher:

Code: Alles auswählen

class Gui(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi("trash.ui")
        self.ui.setWindowTitle("Name")
        self.ui.pushButton.clicked.connect(self.action)
   
    def action(self):
        self.ui.textEdit.setText("geschafft!")
Bei loadUI weist man entweder den Rückgabewert einem Attribut zu, oder übergibt self. Aber niemals beides.
Wenn ich den code so ausführe, dann wird mir nur ein kleines, leeres Frame ohne jeglichen Inhalt ausgegeben. Nach meinem Verständnis wäre der Parameter "self" in der loadUI Funktion eben genau die ganzen Gui-Elemente, die ich angezeigt haben möchte. Füge ich wieder self hinzu, ist wieder alles so, wie im QTDesigner angelegt...
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

__blackjack__ hat geschrieben: Montag 14. September 2020, 18:46 @m.g.o.d: Das `Gui`-Objekt das Du in `Operations.action()` erstellst ist ein anderes als das was Du angezeigt hast. Du darfst da kein *neues* Objekt erstellen sondern musst das vorhandene, angezeigte verwenden. Also entweder der `action()`-Methode als Argument mitgeben, oder dem `Operations`-Objekt beim erstellen und dann an ein Attribut binden.

Eine `__init__()` die einzig die `__init__()` der Basisklasse aufruft kann man sich sparen. Das passiert automatisch wenn es keine `__init__()` gibt.

Und um da gleich mal den nächsten Fehler zu vermeiden: Der Code in dem ``if __name__ == ...``-Zweig muss in einer Funktion verschwinden, damit Du nicht anfängst auf globale Variablen zuzugreifen.
Danke, das war die Lösung. Tausend Dank :-) !!! Die doppelt erstellte Instanz auf Gui() war mein Fehler. So funktioniert es:

Code: Alles auswählen

class Gui(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi("trash.ui", self)          # self.ui IST DIE INSTANZ, die mir angezeigt wird
        self.ui.setWindowTitle("Name")
        self.ui.pushButton.clicked.connect(self.init)
      
    def init(self):
        instanzOperations = Operations() 
        instanzOperations.action(self.ui)
       

class Operations:
    def action(self, ui):
        print("Nun verändere den TextEdit")
        ui.textEdit.setText("Hallo")
Benutzeravatar
__blackjack__
User
Beiträge: 13241
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@m.g.o.d: Da solltest Du dann aber nicht `self.ui` übergenen sondern nur `self` und das `ui`-Attribut sollte es nicht geben. Und wie Sirius3 schon schrob ist die Klasse `Operations` keine sinnvolle Klasse. Da gehört eine `__init__()` hin die etwas sinnvolles macht und wenn `action()` das `self`-Argument nicht verwendet, müsstest Du mal erklären warum das nicht einfach eine Funktion ist.

Code: Alles auswählen

class Gui(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        uic.loadUi("trash.ui", self)
        self.setWindowTitle("Name")
        self.pushButton.clicked.connect(self.init)

    def init(self):
        print("Nun verändere den TextEdit")
        self.textEdit.setText("Hallo")
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Antworten