Gui mit Glade und pyserial

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
igni
User
Beiträge: 6
Registriert: Mittwoch 17. Februar 2016, 19:14

Hallo!
Ich hab ein Problem mit meiner Gui. Ich habe mit Glade eine Gui zusammengestellt (aus mehreren tutorials). Über pyserial schicke ich Daten an ein Arduino. Das klappt auch soweit gut. Ich versuche im Moment verzweifelt vom Arduino einen String zu empfangen und diesen dann in einem Lable über set.text darzustellen. Das Problem ist das ich wie ein "button clicked event" brauche um das realisieren. Ein thread event in einer anderen Class funktioniert auch nur um die readline in die Console zu schreiben damit bekomme ich die Daten aber nicht in mein Lable. Eine while Schleife schiesst mir die komplette Gui ab :(

Hat jemand eine Idee?

Ich habe python 2.7 und gtk+
BlackJack

@igni: Wie die meisten GUI-Rahmenwerke ist auch Gtk nicht einfach so threadsicher, das heisst wenn man das Lesen der seriellen Schnittstelle in einen Thread auslagert (`threading`-Modul) muss man Gtk sagen das Threads verwendet werden (`GObject.init_threads()`) und man darf die GUI nur von dem Thread aus verändern in dem die Gtk-Hauptschleife läuft. Das kann man mit `glib.idle_add()` und `glib.timeout_add()` erreichen.
igni
User
Beiträge: 6
Registriert: Mittwoch 17. Februar 2016, 19:14

Danke für den Tipp! Das hört sich gut an! Hab das hier:
https://wiki.gnome.org/Projects/PyGObject/Threading
über die von Dir genannte Funktion gefunden. Das ist eigentlich genau das was ich suche.
Nur die Frage ob ich es umgesetzt bekomme :?
Vielen Dank! Ich werd es ausprobieren.
igni
User
Beiträge: 6
Registriert: Mittwoch 17. Februar 2016, 19:14

Klappt leider immernoch nicht. Der Thread wird aufgerufen aber nicht für die Funktion der Gui. Print etc. funktioniert, aber wenn das "self" Attribut von set.text oder ähnlichem dazu kommt, wird die Fehlermeldung self not defined oder auch not global ausgegeben...
BlackJack

@igni: Ah ja, dann machst Du irgend etwas falsch. Und zwar…

Hier wird es jetzt echt schwierig ohne zu wissen was Du denn eigentlich genau machst und was die jeweiligen Folgen *genau* sind. Also nicht schwammige Beschreibungen von Fehlermeldungen sondern Code und dazugehörige Tracebacks 1:1 kopiert. Ein minimales aber lauffähiges Codebeispiel wäre praktisch. Minimal: Keine UI-Datei, kein PySerial, einfach nur eine GUI mit einem Label und einem Thread der x Millisekunden des Text des Labels änderd beispielsweise. Also so, dass das jeder einfach ausprobieren/nachvollziehen kann.
igni
User
Beiträge: 6
Registriert: Mittwoch 17. Februar 2016, 19:14

Hallo!
Hab eben noch was ausprobiert:
Es funktioniert wenn ich den Thread direkt unter die def __init__(self) Block der UI class schreibe.

Das funktioniert ganz gut bis auf das mir Python jetzt sporadisch abstürzt. Das umkopieren in die globalvarialbeln unten hab ich gemacht, da hinter dem empfangenen String irgendein Datenmüll dranhängt (obwohl er in der Consolenausgabe sauber ist) und damit die Anzeige nur bei Änderung des Strings aktualisiert wird. Kann man bestimmt schönder lösen, aber hier mal der Code:

Code: Alles auswählen

#!/usr/bin/python -tt

import os
#...weitere imports


connection = serial.Serial('COM6', 9600, timeout=5)     #bluetooth Verbindung

response = "waiting....."

class UI:

    def __init__(self):
        self.builder = Gtk.Builder()
        self.builder.add_from_file("menue.glade")
        self.mstatus = self.builder.get_object("Mstatus")   #####Mstatus ist das Lable in dem der String dargestellt wird
        #... wertere Gui inits
        threading.Thread(target=self.arduinoread).start() #########hier musst der Thread hin damit set.text funktioniert

    def on_btmenue1_clicked(self, widget):
        #... weitere Button def

    def arduinoread(self):
        while True:
           global response
           rawreadarduino = connection.readline()
           self.mstatus.set_text(response[:-2])
           time.sleep(1.0)
           if rawreadarduino != response:
              response = rawreadarduino
              
if __name__ == '__main__':
    ui = UI()
    Gtk.main()

EDIT:
Warscheinlich muss ich die "rawreadarduino" igendwie als Line behandeln wenn ich eine readline einlese und der "Datenmüll" ist der carriage return der readline...

Alles Anfägerfehler tut mir leid... :K
igni
User
Beiträge: 6
Registriert: Mittwoch 17. Februar 2016, 19:14

Carrige Return kann man angeblich mit .readline(eol='\r') ausblenden. Ich werde es morgen/übermorgen testen.
BlackJack

@igni: Das ``global`` macht keinen Sinn und Du solltest am besten gleich die Existenz dieses Schlüsselworts. Weder `response` noch `connection` haben etwas auf Modulebene zu suchen. Dort sollten nur Konstanten, Funktionen, und Klassen definiert werden.

Funktioniert sehr gut bis auf sporadische Abstürze? Ernsthaft‽ Ich würde sporadische Abstürze ja eher so deuten das es alles andere als gut läuft. Man darf aus einem anderen Thread heraus nicht einfach so die GUI manipulieren, das ist doch gerade das Problem um das es hier geht und das mit `idle_add()` gelöst werden kann, weil die Funktion den Aufruf der die GUI manipuliert, in den Thread verschiebt, der die GUI-Hauptschleife ausführt.

Statt `readline()` in einer Endlosschleife-``while``-Schleife aufzurufen wäre es naheliegender eine ``for``-Schleife über das `Serial`-Objekt laufen zu lassen, wie man das bei anderen Dateiobjekten auch machen würde.

Wozu das `sleep()` in der Schleife? Der Leseaufruf ist blockierend. Das warten riskiert bloss das sich Daten aufstauen und eventuell auch verworfen werden wenn man Pech hat und man dann mittem im Datenstrom aufsetzt wenn man wieder liest.
igni
User
Beiträge: 6
Registriert: Mittwoch 17. Februar 2016, 19:14

Hallo!
Danke für Deine Antwort! Ich hatte gedacht ich könnte mit dem time sleep die häufigkeit der Threadausführung beeinflussen.
Klar will ich nicht das Python abstürzt, aber ohne eine Rückmeldung vom Arduino ist das Projekt sinnlos. Also lieber ein Programm mit Fehlern die man hoffentlich noch herausbekommt, als ein Programm das garnicht funktioniert :(
Ich hab es nicht hinbekommen die Variabeln als Class intern zu deklarieren, Python hat andauernd Fehlermeldungen ausgespuckt. Deshalb der Versuch mit den Globalen. Ich setzt mich nochmal dran die als intern umzuschreiben.

For Schleife klingt sinnig. Ich werde versuchen das auch zu benutzen.

Danke für Deine Hilfe, ich Programmiere normalerweise nur Zyklisch ablaufende Sps Programme, Python reagiert da ein bisschen anderns als ich das gewohnt bin...
Antworten