QT: Wert aus Combobox als Parameter übergeben

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Nachbar
User
Beiträge: 24
Registriert: Sonntag 10. Juli 2016, 08:12

Hallo Zusammen,
zunächst: ich bin Programmieranfänger und tue mich mit der Objektorientierung noch sehr schwer. Damit alles mehr Spaß macht, möchte ich meine kleinen Progrämchen gerne mit GUIs verknüpfen, dafür habe ich QT bzw. den QT Designer entdeckt. Ich habe schon viel gegoogled aber leider komme ich nicht auf die Lösung meines trivialen Problems, nämlich den ausgewählten Wert (Variable "x") einer Combobox an eine Methode ("auswahl") zu übergeben, um dann innerhalb der Methode damit arbeiten zu können. Vielleicht könnt ihr mir helfen :?:

Code: Alles auswählen

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'trying.ui'
#
# Created by: PyQt4 UI code generator 4.12.1
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui


def auswahl(x):
    if x == "Ja":
        return "Ja"
    elif x == "Nein":
        return "Nein"

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(800, 600)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.comboBox = QtGui.QComboBox(self.centralwidget)
        self.comboBox.setGeometry(QtCore.QRect(320, 130, 85, 27))
        self.comboBox.setObjectName(_fromUtf8("comboBox"))
        self.comboBox.addItem(_fromUtf8(""))
        self.comboBox.addItem(_fromUtf8(""))
        
        # wie kann ich diesen Wert übergeben?
        x = self.comboBox.connect()
        
        self.pushButton = QtGui.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(320, 210, 96, 26))
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.lineEdit = QtGui.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(310, 80, 113, 27))
        self.lineEdit.setObjectName(_fromUtf8("lineEdit"))
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

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

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du solltest ein paar Dinge anders machen:

- benutz Qt5. Qt4 ist schon lange nicht mehr fuer Neuentwicklungen vorgesehen, und wird nicht mehr aktiv entwickelt. Du handelst dir also Fehler und auch altbackenen Code ein.
- ganz oben in deinem Programm steht ein ganz dicker Kommentar, und der sagt, das man die Datei die du benutzt, NICHT veraendern soll. Denn sie ist aus dem UI-File des designers entstanden. Wenn du also den Designer wieder anwirfst, um deine GUI weiter zu entwickeln, aendert sich der gesamte Code - und du stehst vor dem nichts. Stattdessen benutzt man die
generierte Datei via import und schreibt seinen Code in ein extra Modul! Diesem Muster zu folgen ist also sehr anzuraten.
- es ist empfehlenswert sich an die Namenskonventionen fuer Python zu halten. EtwasSoGeschriebenes ist ein Name fuer eine Klasse, NICHT fuer ein Objekt (auch gerne dieser Klasse). Und diverse andere Dinge, die alle in PEP8 beschrieben sind.

Was dein eigentliches Problem angeht: GUI-Programmierung ist ein sehr fortgeschrittenes Thema. Der ganz entscheidende Teil des Denkens, der sich dafuer aendern muss: Programme laufen nicht mehr linear ab. Stattdessen reagieren sie auf Ereignisse. Deine gezeigte Funktion "auswahl" ist darum falsch gedacht: per return kann da nichts passieren, denn der Rueckgabewert der Funktion hat keinen Ort, wo er sinnvoll verwandt werden kann.

Und last but not least: Qt hat ein Konzept von Signalen, und Slots, also im weitesten Sinne Endpunkten, an die diese Signale gesandt werden. Fuer dein Problem musst du dir aus der Dokumentation von Qt das entsprechende Signal raussuchen, und mit einem Slot, also einer Funktion oder Methode, verbinden. Zum Beispiel currentTextChanged, oder currentIndexChanged.

Auf Qt5 umgearbeitet, und mit ein paar Aenderungen versehen, die es so machen, wie es richtig ist, funktioniert das hier fuer mich:

Code: Alles auswählen

import sys

from PyQt5 import QtCore, QtGui, QtWidgets


def auswahl(x):
    print("auswahl war:", x)


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.comboBox = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox.setGeometry(QtCore.QRect(320, 130, 85, 27))
        self.comboBox.setObjectName("comboBox")
        self.comboBox.addItem("A")
        self.comboBox.addItem("B")

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(320, 210, 96, 26))
        self.pushButton.setObjectName("pushButton")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(310, 80, 113, 27))
        self.lineEdit.setObjectName("lineEdit")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        QtCore.QMetaObject.connectSlotsByName(MainWindow)



def main():
    app = QtWidgets.QApplication(sys.argv)
    main_window = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(main_window)
    combo_box = main_window.findChild(QtWidgets.QComboBox, "comboBox")
    combo_box.currentTextChanged.connect(auswahl)
    main_window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()
Nachbar
User
Beiträge: 24
Registriert: Sonntag 10. Juli 2016, 08:12

Vielen Dank für deine Antwort! Habe jetzt erstmal Qt5 installiert. Das mit dem coden in einem Modul und dann importieren muss ich mir in Ruhe mal angucken.
__deets__ hat geschrieben: Sonntag 26. August 2018, 20:00 - es ist empfehlenswert sich an die Namenskonventionen fuer Python zu halten. EtwasSoGeschriebenes ist ein Name fuer eine Klasse, NICHT fuer ein Objekt
Meinst du damit "auswahl"?

__deets__ hat geschrieben: Sonntag 26. August 2018, 20:00 Der ganz entscheidende Teil des Denkens, der sich dafuer aendern muss: Programme laufen nicht mehr linear ab. Stattdessen reagieren sie auf Ereignisse.
Das gilt jetzt aber ganz allgemein für objektorientierte Programmierung - oder eher speziell für GUI Programmierung?
__deets__ hat geschrieben: Sonntag 26. August 2018, 20:00Deine gezeigte Funktion "auswahl" ist darum falsch gedacht: per return kann da nichts passieren, denn der Rueckgabewert der Funktion hat keinen Ort, wo er sinnvoll verwandt werden kann.

Und last but not least: Qt hat ein Konzept von Signalen, und Slots, also im weitesten Sinne Endpunkten, an die diese Signale gesandt werden. Fuer dein Problem musst du dir aus der Dokumentation von Qt das entsprechende Signal raussuchen, und mit einem Slot, also einer Funktion oder Methode, verbinden. Zum Beispiel currentTextChanged, oder currentIndexChanged.

Auf Qt5 umgearbeitet, und mit ein paar Aenderungen versehen, die es so machen, wie es richtig ist, funktioniert das hier fuer mich:
Okay hm, das muss sich noch setzen. Danke erstmal für den Codevorschlag, ich muss mal sehen wie weit ich damit komme, um mein Program umzusetzen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das gilt für GUI Programmierung. Mit OO hat das erstmal nichts zu tun, auch wenn GUI ohne OO ungewöhnlich ist. Und “auswahl” ist doch nicht CamelCase. Sondern MainWindow. Das ist falsch benannt. Und darum von mir umbenannt worden.
Nachbar
User
Beiträge: 24
Registriert: Sonntag 10. Juli 2016, 08:12

__deets__ hat geschrieben: Donnerstag 30. August 2018, 08:18 Das gilt für GUI Programmierung. Mit OO hat das erstmal nichts zu tun, auch wenn GUI ohne OO ungewöhnlich ist.
Ach so, okay.
Und “auswahl” ist doch nicht CamelCase. Sondern MainWindow. Das ist falsch benannt. Und darum von mir umbenannt worden.
Ja hatte mich halt gewundert, weil ich nicht glaubte, dass der Qt Designer sich nicht an die Namenskonventionen hält. Also bezog ich deinen Hinweis auf irgendeine von mir benannte Sache, da blieb nicht viel außer "auswahl" :)

Was ich an deinem Code (der auch bei mir funktioniert) nicht verstehe, ist wie bei Aufruf von "auswahl" der Parameter x mit einem Wert gefüllt wird. Wo übergebe ich etwas? Kommt der Wert von currentTextChanged und landet in Variable x :oops: ??

Code: Alles auswählen

combo_box.currentTextChanged.connect(auswahl)
Und wie bekommt man die Ausgabe ins Label anstatt in die Konsole?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Nachbar: Qt ruft `auswahl()` mit dem Text auf jedes mal wenn sich der Text ändert. Das es das machen soll, hast Du mit dem `currentTextChanged.connect` gesagt.

Wenn die Ausgabe in einem Label erfolgen soll, dann braucht `auswahl()` Zugriff auf dieses Label um darauf die `setText()`-Methode mit dem Text als Argument aufrufen zu können. Da sind wir dann wieder bei Objektorientierung, denn dazu müsste `auswahl()` eine Methode auf dem Objekt sein das auch das Label als Attribut besitzt.

Sorry das ich da jetzt noch eine Baustelle aufmache, aber die ganzen `setGeometry()`-Aufrufe sehen nicht gut aus. Du solltest Layouts verwenden statt alles absolut zu positionieren. Sonst kann eine GUI auf anderen Rechnern, und/oder mit anderen Einstellungen für Aufflösung, Schriftgrössen, usw. im Extremfall sogar unbrauchbar werden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Nachbar
User
Beiträge: 24
Registriert: Sonntag 10. Juli 2016, 08:12

Ich wollte mal rückmelden, dass ich weitergekommen bin. So richtig auf die Sprünge geholfen hat mir dieses Video:

https://www.youtube.com/watch?v=XBurh2nBR6c

Demnach habe ich den ganzen QT Designer Code von meinem eigentlichen Programm getrennt (in die Datei "meinprogramm.ui") und im eigentlichen Programm per

Code: Alles auswählen

loadUi
eingebunden, also:

Code: Alles auswählen

app = QApplication(sys.argv)
fenster = loadUi("meinprogramm.ui")
fenster.show()
Damit konnte ich dann in meinem eigentlichen Programmcode per

Code: Alles auswählen

fenster.irgendeinwidget
auf die Werte der jeweiligen Widgets (z. B. von der Combobox) zugreifen. Das war wahrscheinlich so ungefähr das was ihr meintet.
Antworten