SelectionBox überwachen und Wert wiederverwenden

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
doerflia
User
Beiträge: 20
Registriert: Freitag 3. Mai 2019, 14:49

Dienstag 11. Juni 2019, 16:14

Hi,

ich baue über eine Schleife über ein Array/eine Liste und mit mehreren if-elif-Anweisungen ein Design auf.

Abhängig vom Wert einer SelectionBox (MapFeature),
sollen sich die Werte einer anderen SelectionBox (Status) ergeben.

Dabei arbeite ich mit dem .connect()-Befehl und
möchte das Signal mit .emit() abfangen.

Code: Alles auswählen

            if group_name == "Map Feature":
                # creates all choices for selection
                for idx, item in enumerate(items[1]):
                    self.combo_box_map_feature.addItem(item)

                # set listener for event handling to selection box
                self.combo_box_map_feature.activated.connect(self.feature_type_listener)

                # add complete Widget to Map Feature element
                layout_vbox_map_feature.addWidget(self.combo_box_map_feature)

                # create GroupBox for Map Feature Selection
                map_feature_group_box = QGroupBox(group_name)
                map_feature_group_box.setLayout(layout_vbox_map_feature)
                
                # add GroupBox to outer layout
                layout_grid_radio_groups.addWidget(map_feature_group_box)
            elif group_name == "Validation Status":
                for status_type in validation_lst:
                   if status_type[0] == self.combo_box_map_feature.activated.emit():
                      self.combo_box_status.addItem(status_type[1])

                # add complete Widget to Status element
                layout_vbox_status.addWidget(self.combo_box_status)

                # create GroupBox for Status selection
                status_group_box = QGroupBox(group_name)
                status_group_box.setLayout(layout_vbox_status)
                
                # add GroupBox to outer layout
                layout_grid_radio_groups.addWidget(status_group_box)
Außerdem verwende ich die Überwachung für andere zwecke, weshalb sich die Funktion "feature_type_listener" wie folgt aufgebaut hat:

Code: Alles auswählen

    def feature_type_listener(self):
        feature_type = self.sender().currentText()
         
        # hide all group_boxes
        self.group_box_sign.hide()
        self.group_box_pole.hide()
        self.group_box_rb.hide()
        self.group_box_sl.hide()
        self.group_box_ee.hide()
        self.group_box_bridge.hide()

        # resize window
        self.widget.resize(self.widget.minimumSizeHint())
        self.resize(self.minimumSizeHint())
        
        # set combo_boxes and value line to default
        self.combo_box_lane_sign_type.setCurrentIndex(0)
        self.combo_box_warning_sign_type.setCurrentIndex(0)
        self.sign_val_line_edit.text() == None
            
        # show selected group box/feat_type
        if feature_type == "TRAFFIC SIGN":
            self.group_box_sign.show()
        elif feature_type == "POLE":
            self.group_box_pole.show()
        elif feature_type == "ROADSIDE BARRIER":
            self.group_box_rb.show()
        elif feature_type == "SPECIAL LANE TYPE":
            self.group_box_sl.show()
        elif feature_type == "EXIT/ENTRY":
            self.group_box_ee.show()
        elif feature_type == "BRIDGE":
            self.group_box_bridge.show()
Bevor ich den Emit-Befehl eingefügt hatte, hatte ich ein lauffähiges Programm,
allerdings keine Auswahlmöglichkeiten in der Status-SelectionBox.

Mit anderen Wegen bin ich auch nicht voran gekommen,
daher dann die Idee mit dem .emit()-Befehl.

Das Problem jetzt: das Prorgamm stürzt beim Öffnen ab.
Fehlermeldung:
File "C:/Users/MATTDOE/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\video_uav_tracker\issue_dialog.py", line 251, in __init__
if status_type[0] == self.combo_box_map_feature.activated.emit():
TypeError: activated(self, int) signal has 1 argument(s) but 0 provided
Ehrlicherweise ist mir das .activated von der Funktionsweise bzw. vom Sinn meiner Verwendung nicht klar.
Ohne .activated, kann ich aber irgendwie auch schon den Listener/die Überwachung starten.
Daher habe ich nach Online-Recherche das einfach hinzugefügt.
Damit lief dann der Code, bis ich das mit .emit() nun einfügen wollte.

Auch mit der Fehlermeldung konnte ich dann nichts mehr anfangen.
Ist .activated() nicht einfach nur der Funktionsaufruf/die Aktion, die den Listener startet?
Brauche ich das beim Wert abfragen überhaupt?
Kann ich den überhaupt von einer SelectionBox den aktuellen Wert ermitteln und verwenden (außerhalb der Methode "feature_type_listener".


Vielen Dank für euer Hilfe :)
Matze
Benutzeravatar
__blackjack__
User
Beiträge: 4048
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Dienstag 11. Juni 2019, 16:34

@doerflia: Ich denke Du hast `emit()` falsch verstanden. Damit kann man nichts ”abfangen” und es macht auch keinen Sinn den nicht vorhandenen Rückgabewert mit irgend etwas zu vergleichen. `emit()` ist zum *senden* von Signalen. Mit dem was Du da geschrieben hast, könntest Du das `activated`-Signal *senden*, wenn Du denn das Argument vom Typ `int` angeben würdest, denn dieses Signal sendet als Datum ja den Index von dem aktivierten Element mit.

Und ja, man kann von einer `QComboBox` den aktuellen Index und den aktuellen Text abfragen.
“Programmieren ist ein Hobby, bei dem es einen riesigen Baumarkt mit quasi jedem Bauteil und Werkzeug und fast immer kostenlos gibt. Ob man deswegen in der Lage ist einen Kölner Dom zu bauen ist eine andere Frage. Arbeit steckt auf jeden Fall drin ;).” — Greebo, forum.ubuntuusers.de
__deets__
User
Beiträge: 6217
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 11. Juni 2019, 16:36

Ich weiss nicht so genau was du da gebastelt hast, aber das du selbst ein emit absendest sieht falsch aus. Zuerstmal "faengt man nichts ab" mit emit. Im Gegenteil. Man loest ein Signal aus, und alle damit verbundenen Slots werden aufgerufen. Der Fehler, den du dabei siehst, ist dabei recht klar: das signal erwartet einen Parameter. Den du aber nicht mitgibst. Soll Python raten, welche Zahl gemeint ist? Das geht ja nun nicht.

Du musst es aber auch nicht selbst ausloesen. Das wird fuer dich ausgeloest eben genau dann, wenn der Benutzer ein Element ausgewaehlt hat. Besser als activated ist aber currentIndexChanged. Und auch das musst du nicht mit emit selbst ausloesen. Sondern das wird fuer dich ausgeloest, wenn eine Aenderung erfolgt ist. Und im feature_type_listener musst du nun natuerlich ein weiteres Argument "selected_index" angeben, denn genau darum geht es doch: du willst mitbekommen, welcher Index und damit welches Element ausgewaehlt wurde.
doerflia
User
Beiträge: 20
Registriert: Freitag 3. Mai 2019, 14:49

Mittwoch 12. Juni 2019, 08:17

Vielen Dank für eure Antwort.
Ja, da habe ich das mit dem emit() falsch verstanden. Macht natürlich auch vom Begriff so viel mehr Sinn :lol: :lol:

Das Problem habe ich aber leider immer noch:
Wie bekomme ich den aktuellen Wert der Selction Box in meinem weiteren Programm genutzt.
Also ein Rückgabewert der Funktion "feature_type_listener" ist ja technisch nicht möglich, oder?

Oder kann ich so etwas in der Art basteln:

Code: Alles auswählen

current_feature = self.combo_box_map_feature.activated.connect(self.feature_type_listener)
und:

Code: Alles auswählen

    def feature_type_listener(self):
        feature_type = self.sender().currentText()
        
        ....
        
        return feature_type
um dann im Code wieder wie folgt weiter zu machen:

Code: Alles auswählen

            elif group_name == "Validation Status":
                for status_type in validation_lst:
                   if status_type[0] == current_type
                      self.combo_box_status.addItem(status_type[1])
Also ich suche quasi eine Möglichkeit, um den aktuellen Text einer Selection Box für eine Abfrage zu verwenden,
z. B. "TRAFFIC SIGN" == "TRAFFIC SIGN"

Das liegt daran, dass sich status_type wie folgt aufbaut:

Code: Alles auswählen

status_type = (
   ("TRAFFIC SIGN", ("OK", "FALSE POSITIVE", "FALSE NEGATIVE", "WRONG POSITION", "WRONG CLASSIFICATION", "WRONG SHAPE", "WRONG VALUE", "WRONG DIRECTION", "OTHER MISTAKE")),
   ("POLES", ("OK", "FALSE POSITIVE", "FALSE NEGATIVE", "WRONG POSITION", "WRONG CLASSIFIACTION")),
   ...,
  ("TRAFFIC LIGHTS", ("OK", "FALSE POSITIVE", "FALSE NEGATIVE", "WRONG POSITION"))
)
Wie gesagt, jedes Feature hat eigene Möglichkeiten für einen Status.

Ein "Integer"-Wert der Selection Box bringt mir da relativ wenig... Jemand eine Idee?
Benutzeravatar
__blackjack__
User
Beiträge: 4048
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mittwoch 12. Juni 2019, 09:37

@doerflia: Ich schrob ja das man von einer `QComboBox` den aktuellen Text abfragen kann. Und Du machst das ja sogar bereits in Deinem `feature_type_listener()` in der allerersten Zeile.

Wobei Du das eigentlich nicht müsstest, beziehungsweise so nicht machen solltest, denn das Signal liefert diese Information schon mit – da solltest Du nicht auf `sender()` zurückgreifen müssen, was IMHO sowieso grundsätzlich ein „code smell“ ist, und in der Qt-Dokumentation steht dazu auch eine Warnung.

Und selbst wenn Du den Index als Ganzzahl hättest, dann hat `QComboBox` eine Methode um den Text des Elements an diesem Index abzufragen. Wäre ja sonst ziemlich nutzlos.

Der `connect()`-Aufruf sollte so aussehen:

Code: Alles auswählen

        self.combo_box_map_feature.activated[str].connect(
            self.feature_type_listener
        )

# ...

    def feature_type_listener(self, feature_type):
        self.group_box_sign.hide()
        self.group_box_pole.hide()
        self.group_box_rb.hide()
        self.group_box_sl.hide()
        self.group_box_ee.hide()
        self.group_box_bridge.hide()
Edit: Ich sehe diesen `hide()` und dann wieder eines davon anzeigen-Kram erst jetzt so richtig bewusst. Du möchtest Dir vielleicht mal `QStackedWidget` anschauen bevor Du dieses Rad selbst noch mal neu erfindest.
“Programmieren ist ein Hobby, bei dem es einen riesigen Baumarkt mit quasi jedem Bauteil und Werkzeug und fast immer kostenlos gibt. Ob man deswegen in der Lage ist einen Kölner Dom zu bauen ist eine andere Frage. Arbeit steckt auf jeden Fall drin ;).” — Greebo, forum.ubuntuusers.de
doerflia
User
Beiträge: 20
Registriert: Freitag 3. Mai 2019, 14:49

Mittwoch 12. Juni 2019, 09:55

Vielen Dank __blackjack__, du hast mir sehr weitergeholfen.
Problem gelöst. Danke dir!

Ja, QStackedWidget wäre eine Option, ist aber der Fall, wenn ich "blättern" würde, bzw. mehrere Ansichten erstelle.
Eigentlich habe ich eine Ansicht, gewisse Teile sind immer zu sehen, andere verändern sich je nach Auswahl der Checkbox oben.
Könnte das natürlich auch wie eine Blätter-Ansicht schreiben und dann die gemeinsamen Teile "kopieren".
Wäre vielleicht sogar der sauberere Weg gewesen.

Problem, wenn Tools der Kollegen weiterentwickelt werden :)
Da wird auf bestehendes aufgebaut :D
Da das kein Tool ist, dass an den Kunden raus geht und nur bedingt wichtig für unsere eigentliche Arbeit ist,
wird das wohl oder übel schlecht programmiert bleiben...
Benutzeravatar
__blackjack__
User
Beiträge: 4048
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mittwoch 12. Juni 2019, 10:40

@doerflia: Warum die gemeinsamen Teile kopieren? Man muss ja nur den Teil der sich ändert in einem `QStackedWidget` organisieren.
“Programmieren ist ein Hobby, bei dem es einen riesigen Baumarkt mit quasi jedem Bauteil und Werkzeug und fast immer kostenlos gibt. Ob man deswegen in der Lage ist einen Kölner Dom zu bauen ist eine andere Frage. Arbeit steckt auf jeden Fall drin ;).” — Greebo, forum.ubuntuusers.de
Antworten