PyQt4 ComboBox-item mit einer Funktion verbinden

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
smile-1969
User
Beiträge: 20
Registriert: Sonntag 22. März 2015, 16:46

Hallo,
versuche leider erfolglos meine "items" der Combobox in mein Programm einzubinden.
Beschreibung des Programms: Mit den Pushbuttons "EIN" und "AUS" schalte ich eine definierte LED1 ein bzw. aus.
Über eine Combobox möchte ich nun eine Auswahl zur Verfügung stellen, welche Led geschaltet werden soll (LED1, LED2 oder...).
Wie verbinde ich nun die jeweils ausgewählte LED der Combobox mit dem entsprechenden Programmablauf?
Müsste doch auf diese Art funktionieren: if...Inhalt Combobox=LED1...schalte LED1...etc.
Habe mich zwar durch etwaige Dokumentationen gekämpft, finde aber leider nicht das passende oder verstehe es einfach nicht.
Vielleicht kann mir jemand mit einem verständlichem Beispiel helfen?
Danke Uwe
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Du kannst eine ComboBox auf verschiedene Art und Weise verwenden. Lies Dir doch mal die Doku dazu durch.

Eine einfache (und für Dich vermutlich hinreichende) Möglichkeit wäre es, über den Namen zu gehen. Also wählt jemand den Eintrag "LED3" aus, so kannst Du in einem Dict nachgucken, welcher Port o.ä. zu dieser LED gehört.

Dazu müssten wir aber erst wissen, wie Deine Datenstrukturen so aussehen.

Sind die LEDs durchnummeriert, wäre das Arbeiten über den Index auch denkbar.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Eine Möglichkeit ist, die LEDs in einem Dictionary vorrätig zu halten, als Keys verwendest du fortlaufende Ganzzahlen.
Dann kannst du den currentIndex der QComboBox als Index des Dictionary benutzen.

Auch interessant dazu: currentIndexChanged
smile-1969
User
Beiträge: 20
Registriert: Sonntag 22. März 2015, 16:46

Wenn ich das richtig verstehe, dann ist diese Zuordnung als im Dict hinterlegt, also wie genau erstelle ich den?
Habe die Combobox über QtDesigner mit den den aufeinanderfolgenden Zeilen LED1, LED2...gefüllt, lade die die ui., kann in der Box auswählen, habe aber keine Verbindung zu einer Aktion. Hoffe das beantwortet die Frage zu meiner Datenstruktur...
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Madmartigan hat geschrieben:Eine Möglichkeit ist, die LEDs in einem Dictionary vorrätig zu halten, als Keys verwendest du fortlaufende Ganzzahlen.
Bei *fortlaufenden* Ganzzahlen ist ein Dictionary nicht unbedingt das beste Mittel. Da reicht dann ja eine Liste vollkommen aus, ggf. auch eine Liste von Tupeln mit dem Aufbau (name, led_port) o.ä.

@smile-1996: Öh... das ist jetzt nicht unbedingt der beste Weg. Somit hast Du ja Daten und GUI ziemlich stark verzahnt. Und von eine explizite Struktur hast Du jetzt ja gar nicht, sondern die Daten sind in der UI-Datei fix hinterlegt.

Fangen wir mal anders an: Soll die Bezeichnung denn so simpel bleiben? Und Was passiert denn konkret, wenn man die LED1 auswählt? Und was bei LED2 und LED3 usw.? Was sind die speziellen Daten, die dann zum Zuge kommen?

Wenn wir das wissen, kann man Dir auch raten, wie Du am besten vorgehst!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Hyperion hat geschrieben:
Madmartigan hat geschrieben:Eine Möglichkeit ist, die LEDs in einem Dictionary vorrätig zu halten, als Keys verwendest du fortlaufende Ganzzahlen.
Bei *fortlaufenden* Ganzzahlen ist ein Dictionary nicht unbedingt das beste Mittel. Da reicht dann ja eine Liste vollkommen aus, ggf. auch eine Liste von Tupeln mit dem Aufbau (name, led_port) o.ä.
Das Dictionary ermöglich ihm aber die Auswahl der LEDs per Namen. Die Liste ist da nicht die erste Wahl, sie hätte keinen Vorteil.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Madmartigan: Du schreibst selbst von einem *Index*. Auf Listen wird per Index zugegriffen, auf Dictionaries per Key. Also wo ist hier der Vorteil eines Wörterbuchs? Nein, die offensichtliche Datenstruktur ist eine Liste.
smile-1969
User
Beiträge: 20
Registriert: Sonntag 22. März 2015, 16:46

Über die die beiden PushButtons der Gui kann ich eine Led ein-bzw. ausschalten. Wird also der Button "EIN" betätigt, wird die Routine buttonEIN aufgerufen und GPIO-Port 4 auf High gesetzt bzw. über die Routine buttonAUS auf LOW, wenn der Button "Aus" geklickt wird. Das funktioniert auch. Nun möchte ich das Programm wie folgt erweitern:
Über die Combobox möchte ich einstellen, welche Led bzw. welcher GPIO-Port angesprochen werden soll. Ist also in der Combobox LED1 ausgewählt, soll entsprechend der GPIO-Port 4 geschaltet werden. Ist LED2 gewählt, soll der GPIO-Port 18 geschaltet werden usw. Entsprechend der Auswahl der Combobox soll also eine zugeordnete Routine aufgerufen werden.
Nachtrag: Habe das offensichtliche vergessen, steuer über die GUI die Ports eines RPi.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dann hast Du mehrere Möglichkeiten:

1.) Du speicherst Dir die Ports in einer Liste oder Tupel in der Reihenfolge der Buttons, also nach Deiner Beschreibung etwa so:

Code: Alles auswählen

LED_PORTS = (4, 18, ...)
Damit kannst Du über den aktuellen Index der ComboBox an den korrespondierenden Port gelangen, indem Du mit diesem Index auf Deine Liste zugreifst.

2.) Wenn Du Namen oder Reihenfolge variieren willst, kannst Du auch eine Tupelstruktur wählen, und diese als Grundlage sowohl für die Einträge in der ComboBox als auch für das Vermerken der Ports nutzen:

Code: Alles auswählen

LED_PORTS = (
    ("LED1", 4),
    ("LED hinten links", 18),
    ...
)
Die Box kannst Du nun befüllen, indem Du über die Tupel iterierst und den ersten Eintrag jedes Tupels per ``AddItem`` oder so ähnlich setzt. (Also *nicht* per Designer schon die Einträge da reinpackst!)
Die Auswahl kann analog zu Lösung 1 erfolgen, also per ``currentIndex``.

Sollten die Namen wirklich immer gleich fortlaufend sein, also "LED1", "LED2", usw., so bietet es sich auch bei Lösung 1 an, diese im Programm zu generieren und zwar basierend auf der definierten Liste. Damit stellst Du sicher, dass es nie eine Option in der Combobox gibt, zu der *kein* index in der Liste existiert!
Dies kannst Du einfach über die ``enumerate``-Funktion oder ein ``range(len(...))``-Konstrukt erreichen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Sirius3 hat geschrieben:@Madmartigan: Du schreibst selbst von einem *Index*. Auf Listen wird per Index zugegriffen, auf Dictionaries per Key.
Du wirst recht haben, die Bezeichnung des Keys als Index bei Verwendung von Ganzzahlen ist natürlich sträflich falsch.
Sirius3 hat geschrieben:Also wo ist hier der Vorteil eines Wörterbuchs?
Das hatte ich bereits beschrieben. :roll:
Sirius3 hat geschrieben:Nein, die offensichtliche Datenstruktur ist eine Liste.
Du kennst den Satz vielleicht: Yeah, well, that's just, like, your opinion, man.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@Madmartigan: Ja, wenn man per Name auf die LED zugreifen will. Ein Dictionary hat aber zunächst den Nachteil, dass es unsortiert ist, d.h. man beim Einfügen in die ComboBox die Schlüssel noch sortieren oder ein ``OrderedDict`` nutzen müsste. Wenn dazu keine Notwendigkeiot besteht, erhöht das ganze lediglich die Komplexität - und hier sehe ich bisher keine Notwendigkeit. Der OP hat von Datenstrukturen vermutlich eh noch nicht so viel Ahnung, da sind die von mir beschriebenen Ansätzer zudem sicherlich einfacher für ihn.

Und bei einem Zugriff auf eine Datenstruktur mit fortlaufenden Nummern würde ich ein Dictionary definitiv als suboptimal ansehen, da es nur Overhead aber keinen Vorteil gegenüber einer Liste bietet.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Die Diskussion Liste vs. Wörterbuch ist für den OP wahrscheinlich sophistisch. Zum Glück hat Python nur zwei Standardcontainer und nicht die Phalanx an Containern wie C++ mit den stl::Containertypen. Da lässt sich sich wirklich trefflich drüber streiten ;)
smile-1969
User
Beiträge: 20
Registriert: Sonntag 22. März 2015, 16:46

Also, habe jetzt erstmal versucht die items im Programm einzufügen und nicht im QtDesigner direkt. Leider bekomme ich dabei folgende Fehlermeldung im Terminal angezeigt:

(gksudo:3253): GLib-CRITICAL **: g_str_has_prefix: assertion 'str != NULL' failed

Habe meinen Code mal angehängt, damit wird es vielleicht einfacher für euch.

Code: Alles auswählen

#! /usr/bin/env/python
# -*- coding: utf-8 -*-

import sys
from PyQt4.QtCore import*
from PyQt4.QtGui import*
from PyQt4.uic import*
import RPi.GPIO as GPIO     

GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.OUT)
GPIO.setup(18, GPIO.OUT)
GPIO.setup(24, GPIO.OUT)

def buttonEin():
       
    GPIO.output(4, 1)

def buttonAus():

    GPIO.output(4, 0)   


app = QApplication(sys.argv)
w = loadUi("ue-control.ui")

w.connect(w.buttonAUF, SIGNAL("clicked()"), buttonEin)
w.connect(w.buttonZu, SIGNAL("clicked()"), buttonAus)


combo = QTGui.QCombobox(self)

items = ('LED1', 'LED2')
combo.addItems(items)

w.show()
sys.exit(app.exec_())
BlackJack

@smile-1969: Das wird vom Programm ``gksudo`` ausgegeben. Ich sehe jetzt nicht was das mit Deinem Code zu tun haben soll.

Dein Programm müsste aber eine Fehlerausgabe liefern weil `QTGui` undefiniert ist. `self` wird auch nirgends definiert. Wenn Du die Combobox per Code erstellst, musst Du die auch irgendwo in die angezeigte GUI einbauen. Ich würde die aber nicht per Code erzeugen sondern im QtDesigner.

Durch die Sternchenimporte kann man nicht mehr nachvollziehen was eigentlich aus welchem Modul kommt. Das sollte man deswegen so nicht machen sondern die Namen die man verwendet explizit importieren oder über das jeweilige importierte Modul ansprechen.
smile-1969
User
Beiträge: 20
Registriert: Sonntag 22. März 2015, 16:46

O.k., dann belasse ich das erstmal bei der im QtDesigner erstellten Variante. Komme ich momentan eh besser klar mit.
Was den Rest angeht muss ich mich dann morgen nochmal step für step durcharbeiten/durchfragen, bin heute etwas knapp an Zeit.
Danke erstmal,

Gruß Uwe
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

smile-1969 hat geschrieben:O.k., dann belasse ich das erstmal bei der im QtDesigner erstellten Variante. Komme ich momentan eh besser klar mit.
Es ging auch nicht um das *Erstellen* der ComboBox, sondern um das *Befüllen* mit Werten ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

Komplett ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import sys
from functools import partial

from PyQt4.QtGui import QApplication
from PyQt4.uic import loadUi
from RPi import GPIO

LED_PINS = [4, 18, 24]


class LedUI(object):
    def __init__(self, led_pins):
        self.led_pins = led_pins
        for pin in self.led_pins:
            GPIO.setup(pin, GPIO.OUT)
        self.ui = loadUi('ue-control.ui')
        self.ui.ledCombo.addItems(
            ['LED{0}'.format(i + 1) for i in range(len(self.led_pins))]
        )
        self.ui.buttonAUF.clicked.connect(partial(self.on_led_button, True))
        self.ui.buttonZU.clicked.connect(partial(self.on_led_button, False))
        self.ui.show()

    def on_led_button(self, state):
        index = self.ui.ledCombo.currentIndex()
        if index != -1:
            GPIO.output(self.led_pins[index], state)


def main():
    GPIO.setmode(GPIO.BCM)
    application = QApplication(sys.argv)
    led_ui = LedUI(LED_PINS)
    sys.exit(application.exec_())


if __name__ == '__main__':
    main()
smile-1969
User
Beiträge: 20
Registriert: Sonntag 22. März 2015, 16:46

Tja also erstmal Danke für deine Mühe! Leider erhalte ich damit jetzt wieder diese ominöse "gksudo"-Fehlermeldung. Da ich nur über gksudo gleichzeitig auf die Ports + GUI Zugriff habe, weiß ich gerade nicht woran es hapert!? Irgendwelche Ideen?
BlackJack

Wie spielt denn gksudo da überhaupt rein? Was startest Du denn damit und wo erscheint diese Meldung? Normalerweise startet man das ja über den ”Launcher” und hat damit gar keine Ausgaben von dem Programm in irgendeinem Terminal.
smile-1969
User
Beiträge: 20
Registriert: Sonntag 22. März 2015, 16:46

Starte das Programm über Putty im Terminal. Wenn ich es allerdings über die Remotedesktopverbindung starte, kommt die Fehlermeldung:AttributeError: 'QMainwindow' object has no attribute 'led combo'
Ich kann zumindest mein Programm nur via gksudo vom Terminal starten, da ich nicht als 'root' auf den Rpi zugreife. Also muss ich aus dem Terminal via gksudo die root-Rechte erteilen. Anders kriege ich kein Programm mit Zugriff auf die Ports zum Laufen.
Antworten