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?
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.
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.
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) |
>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
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.
>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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.