Forum: PC-Programmierung QLCDNumber in Python


von Sven N. (admiral_adonis)


Lesenswert?

Servus,

ich bin momentan etwas mit Python am experimentieren. Leider helfen mir 
alle Tutorials nicht weiter bei meinem simplen Problem:

Ich möchte zum Test einfach erstmal eine nackte GUI erstellen, die eine 
LCD-Nummernanzeige enthält welche bei Programmstart einen festen Wert 
bekommt.

Wie man die GUI erstellt etc. ist soweit klar, ich komme so weit, dass 
ich ein Fenster mit meiner LCD-Anzeige sehe.

Da ich bisher nur MATLAB kenne, bin ich es gewohnt fertige Funktionen 
nach der Art:

set(QLCDNumber, 'Value', 20)

zu verwenden. Leider finde ich nirgends mal eine konkrete Beschreibung 
wie das unter Python mit PyQt läuft.
In MATLAB würde der obige Code den Wert '20' in das LCD-Fenster 
schreiben.

Wie schaut da das Äquivalent in Python/Qt aus?

von Tom K. (ez81)


Lesenswert?

QT-typisch wäre, ein Signal ("Wert geändert") mit dem passenden Slot zu 
verbinden:
http://zetcode.com/tutorials/pyqt4/eventsandsignals/

Direkt ins LCD schreiben geht mit lcd.display(4711). Wenn du das im 
verlinkten Beispiel ans Ende von initUI() schreibst, steht im LCD zuerst 
4711, bis du den Fader verschiebst.

von Sven N. (admiral_adonis)


Angehängte Dateien:

Lesenswert?

Danke für die Antwort, hat soweit auch geklappt. Aber irgendwie war mir 
Qt doch zu kompliziert, der ganze Aufbau mit Signalen und Slots ist iwie 
nix für Anfänger wie mich.
Ums mal konkreter zu beschreiben was ich vorhatte:
Da ich bis jetzt nur MATLAB kann, wollte ich mal meinen Horizont in 
Richtung andere Sprachen erweitern. Da ich kürzlich einen Raspberry Pi 
bekommen hatte, schien mir Python da das Richtige.
Da ich aber nicht so gern Tutorial-Beispiele durcharbeite, sondern 
lieber an Konkreten Problemstellungen arbeite, wollte ich einfach mal 
ein kleines Tool proggen welches mir in einer einfachen GUI auf dem 
Raspbi Sachen wie Temperatur, Takt, Spannung etc. ausgibt.

Lange Rede, kurzer Sinn: Ich bin doch erstmal auf Tkinter umgestiegen 
und habs damit auch geschafft was auf die Beine zu stellen. Den Code hab 
ich mal angehängt, geschrieben für python3.
Auf dem Raspberry starte ich das Skript über Rechtsklick, 'open with' 
und dann "Custom command line": python3 %f
Dann hat man diesen Eintrag später im Kontextmenü und muss nicht immer 
per Terminal starten.

Ich hänge das Skript mal an, falls es ein anderer Raspberry Besitzer mal 
ausprobieren möchte. Die Benutzung erfolgt auf EIGENE GEFAHR!

Interessieren würde mich allgemein, was könnte man besser machen, wie 
wird der Programmcode "sauberer" etc.?
Wirklich komplett verstanden hab ich den Ablauf auch nicht, vieles kam 
durch probieren. Gerade die Konstruktion mit der .after(..) - Anweisung 
kommt mir immer noch komisch vor. Eigentlich kenne ich solche 
Endlosschleifen mit dem Befehl while und dann einem sleep-timer am ende.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Sven N. schrieb:
> Interessieren würde mich allgemein, was könnte man besser machen, wie
> wird der Programmcode "sauberer" etc.?

Im Großen und Ganzen ist das Programm in Ordnung. Ein paar
Kleinigkeiten:

Die vielen Globals (temp, volt usw.) brauchst du nicht, die
Initialisierungen temp=0, volt=0 usw. auch nicht.

Anstelle der Funktion hauptroutine, die die lokal definierte Funktion
endlosschleife aufruft, kannst du endlosschleife auch gleich auf dem
Top-Level definieren. hauptroutine fällt dann weg.

Ich würde im Hauptprogramm erst alle GUI-Elemente (einschließlich dem
Schließen-Button) anlegen und dann erst hauptroutine bzw.
endlosschleife aufrufen. Das ändert zwar nichts an der Funktion des
Programms, fasst aber logisch zusammengehörende Dinge im Quellcode
zusammen, so dass dieser besser lesbar wird.

> Wirklich komplett verstanden hab ich den Ablauf auch nicht, vieles kam
> durch probieren. Gerade die Konstruktion mit der .after(..) - Anweisung
> kommt mir immer noch komisch vor.

Die Funktion endlosschleife wird beim Programmstart in deinem Code
einmal explizit aufgerufen, danach läuft das Programm in die mainloop
des TK-Frameworks, das von da an die vollständige Kontrolle über das
Programm hat. In der mainloop werden bspw. sämtliche Eingabeelemente
des GUIs überwacht und bei Betätigung derselben die entsprechenden
Callback-Funktionen aufgerufen. Bei dir wäre das der Schließen-Button
mit der Callback-Funktion Hauptfenster.destroy.

Du willst nun zusätzlich eine Funktion zyklisch aufrufen.

> Eigentlich kenne ich solche Endlosschleifen mit dem Befehl while und
> dann einem sleep-timer am ende.

Das geht nicht ohne weiteres, da das Programm dann zwei Endlosschleifen
hätte, die simultan ausgeführt werden müssten. In Tk (und allen anderen
gängigen GUI-Frameworks) wird das deswegen anders gemacht: Statt die
Funktion jedesmal selber aufzurufen, bittest du das Framework, dies zu
einem vorgegebenen Zeitpunkt zu tun. Genau dafür ist die /after/-Methode
da. Du bittest damit das Framework, nach 3000 ms die Funktion
endlosschleife aufzurufen. Da das Framework dies aber nur einmal tut,
steht der Aufruf von after in der zyklisch aufzurufenden Funktion
selbst, so dass jeder Aufruf gleich den nächsten Aufruf (mit einem
Zeitversatz) aktiviert.

Da endlosschleife selbst keine Endlosschleife ist und auch keine
enthält, würde ich den Namen ändern, bspw. in updateFields o. ä.

Den Aufruf von after würde ich auch nicht an das Label wlan_signal
binden, da damit ja auch die Labels cpu_temp, cpu_volt und
takt_mhz beeinflusst werden. Es sollte genauso möglich sein (hab's
aber nicht getestet), die Signalierung über das Hauptfenster laufen zu
lassen, also so:
1
    Hauptfenster.after(3000, endlosschleife)

von Sven N. (admiral_adonis)


Lesenswert?

>Da endlosschleife selbst keine Endlosschleife ist und auch keine
>enthält, würde ich den Namen ändern, bspw. in updateFields o. ä.

Es verhält sich also eher so, dass die Funktion endlosschleife() immer 
wieder durch Hauptfenster() aus sich selbst heraus aufgerufen wird (mit 
3 sek. verzögerung)?
Ich hab immer Probleme mit der Rekursion und komm auch nur schwer aus 
meiner eingefahrenen MATLAB-Denke raus^^.

>die Signalierung über das Hauptfenster laufen zu
>lassen

Funktioniert astrein und sieht auch viel logischer aus. Die 
Signalisierung über das Label stammte noch aus irgend einem 
Tutorial-Beispiel.

>Die vielen Globals (temp, volt usw.) brauchst du nicht, die
>Initialisierungen temp=0, volt=0 usw. auch nicht.

Richtig, lediglich ganz am Anfang beim Programmeinstieg muss man die 
Variablen einmal deklarieren.
Die global's können entfallen weil die Werte direkt in der Funktion in 
die Labels geschrieben werden, richtig?
Falls ich diese Werte in anderen Funktionen nutzen wollte, entweder über 
global deklarieren oder mit Rückgabewerten (return temp etc.) arbeiten?

Jetzt werden sicher wieder einige Leser lachen und mir raten ich soll 
erstmal die Grundlagen lernen, bevor ich mit GUI anfange. Aber: Alles 
schon versucht, ich muss immer konkret an irgendwas arbeiten, sonst 
verliere ich zu schnell das Interesse^^

Vielen Dank schonmal und
vg

von Yalu X. (yalu) (Moderator)


Lesenswert?

Sven N. schrieb:
> Es verhält sich also eher so, dass die Funktion endlosschleife() immer
> wieder durch Hauptfenster() aus sich selbst heraus aufgerufen wird (mit
> 3 sek. verzögerung)?

Ja.

> Ich hab immer Probleme mit der Rekursion und komm auch nur schwer aus
> meiner eingefahrenen MATLAB-Denke raus^^.

Auch wenn die Sache leicht nach Rekursion riecht, es ist keine. Bei
einer Rekursion wird eine Funktion ein zweites Mal aufgerufen, bevor der
erste Aufruf abgeschlossen ist. In deinem Beispiel wird die Funktion
endlosschleife komplett abgearbeitet, bevor sie (sogar mit 3 s
Verzögerung) erneut aufgerufen wird. Wäre dies nicht so, müsstest du
früher oder später mit einem Stackoverflow rechnen, und das Programm
wäre für die Tonne.

>> Die vielen Globals (temp, volt usw.) brauchst du nicht, die
>> Initialisierungen temp=0, volt=0 usw. auch nicht.
>
> Richtig, lediglich ganz am Anfang beim Programmeinstieg muss man die
> Variablen einmal deklarieren.

Nein. Die Variablen temp, volt, takt und wlan_percent werden nur
in diesem Code-Abschnitt gebraucht:
1
    #Auslese-Funktionen (s.u.) ausfuehren:
2
    temp = temp_auslesen()
3
    volt = volt_auslesen()
4
    takt = takt_auslesen()
5
    wlan_percent = wlan_auslesen()
6
7
    #Werte in Labels schreiben:
8
    cpu_temp.config(text=str(temp))
9
    cpu_volt.config(text=str(volt))
10
    takt_mhz.config(text=str(takt)+" MHz")
11
    wlan_signal.config(text=str(wlan_percent)+" %")

In der oberen Hälfte werden ihnen Werte zugewiesen, die dann in der
unteren Hälfte verwendet werden. Vor und nach diesem Code-Abschnitt
werden die Variablen nicht benötigt, somit müssen sie weder global
gemacht noch explizit angelegt oder initialisiert werden.

Anmerkung: Die globale Variable temp verwendest du weiter unten beim
Erzeugen des Labels cpu_temp noch einmal (als Anfangsinhalt). Du kannst
dort aber statt temp genausogut einen Leerstring eintragen, wie du es
auch bei den anderen Labels gemacht hast. Evtl. kannst du sogar das
"text=" ganz weglassen. Wahrscheinlich setzt dann Tk automatisch einen
Leerstring ein. Python und Tk sind diesbezüglich ziemlich flexibel.

> Falls ich diese Werte in anderen Funktionen nutzen wollte, entweder über
> global deklarieren oder mit Rückgabewerten (return temp etc.) arbeiten?

Ja. Anstelle vieler globaler Einzelvariablen kannst du aber auch ein
globales Objekt erstellen, dass alle diese Variablen als Elemente
enthält. Das musst du dann nicht einmal als global deklarieren, da du
dieser Objektvariable selbst nichts zuweist, sondern nur ihren
Elementen.

Um das Ganze objektorientiert zu strukturieren und eine klarere
Trennlinie zwischen GUI und eigentlicher Funktion zu ziehen, könntest du
auch zwei Klassen, CPU und GUI, anlegen:

CPU enthält dabei die vier Variablen und eine Methode zum Auslesen
der vier Werte aus der Hardware.

GUI enthält die GUI-Elemente, eine zyklisch aufzurufende Methode und
eine Referenz auf ein CPU-Objekt. Die Methode wird mit der
"after-Methode" alle 3 s aufgerufen, ruft ihrerseits die Auslesefunktion
des CPU-Objekts auf, liest die Ergebnisse aus dem CPU-Objekt aus und
aktualisiert die GUI-Elemente entsprechend.

Diese Struktur ist bei diesem Mini-Programm sicher etwas mit Kanonen auf
Spatzen geschossen, erhöht aber bei größeren Anwendungen die
Übersichtlichkeit und die Wiederverwendbarkeit einzelner Programmteile.

von Sven N. (admiral_adonis)


Lesenswert?

>Nein. Die Variablen temp, volt, takt und wlan_percent werden nur
>in diesem Code-Abschnitt gebraucht:

Du hast natürlich recht. Die Fehlermeldung kam durch "temp" als 
Anfangsinhalt.

Dann werd ich jetzt mal weiter schauen wie ich z.B. die einfach 
Textanzeige fürs WLAN durch eine Balkenanzeige ersetzen kann und was ich 
sonst noch so reinbasteln kann ohne dass das Programm zu viele 
Ressourcen braucht.

Besten Dank dass Du dir die Zeit genommen hast. In manch anderem Forum 
bekommt man bei solchen Themen nur geringschätzige Bemerkungen von 
Leuten, denen die Programmierkenntnisse scheinbar in die Wiege gelegt 
wurden.

vg
sn

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.