GTK+ Dauerschleife



  • Hallo,

    ich benutze GTK+ in CodeBlocks. Ich habe eine Tabelle erstellt und möchte, sich permanent änderte Werte die sich in Variablen befinden (z.B. in einem Struct verpackt) in der Tabelle ausgeben.

    Wie sage ich dem Programm, das es permanent, sagen wir alle 100ms durchlaufen und nicht auf irgendein Event warten soll. Da ich keine Buttons oder sonstige Eingaben in meiner Oberfläche habe. Ich möchte nur Werte ausgeben.

    Gruß



  • Gängiges Konzept in allen UI Bibliotheken: Timer. In GTK wird er wohl GTimer heißen.



  • Mechanics schrieb:

    Gängiges Konzept in allen UI Bibliotheken: Timer. In GTK wird er wohl GTimer heißen.

    Habe ich mir auch schon überlegt. Allerdings, wo soll ich den denn reintun?

    Das Programm läuft einmal komplett durch und jetzt wartet es ja auf ein Event. Soll ich quasi am Ende des Programms den Timer setzen und das Event zum restart ist der abgelaufene Timer? -> Wenn ja, kennt jemand eine solche Funktion?



  • JohnDillinger schrieb:

    Habe ich mir auch schon überlegt. Allerdings, wo soll ich den denn reintun?

    In dein Programm 😉

    Was heißt, es läuft einmal durch? Zumindest so lang ein Fenster angezeigt wird, läuft das Programm.



  • Mechanics schrieb:

    JohnDillinger schrieb:

    Habe ich mir auch schon überlegt. Allerdings, wo soll ich den denn reintun?

    In dein Programm 😉

    Was heißt, es läuft einmal durch? Zumindest so lang ein Fenster angezeigt wird, läuft das Programm.

    😃

    Ich habe es so verstanden, dass ein GTK Programm so funktioniert, das der Code abgearbeitet wird und er am Ende bei gtk_main() stehen bleibt, bis ein Event auftritt. Also es beendet sich nicht, aber "wartet"

    Wenn es hilft kann ich ja mal meinen Code posten?



  • Schau dir einfach ein Beispiel an:

    https://developer.gnome.org/gtkmm-tutorial/stable/sec-timeouts.html.en

    Ein Timer verschickt auch so ein "Event".



  • JohnDillinger schrieb:

    Hallo,

    ich benutze GTK+ in CodeBlocks. Ich habe eine Tabelle erstellt und möchte, sich permanent änderte Werte die sich in Variablen befinden (z.B. in einem Struct verpackt) in der Tabelle ausgeben.

    Wie sage ich dem Programm, das es permanent, sagen wir alle 100ms durchlaufen und nicht auf irgendein Event warten soll. Da ich keine Buttons oder sonstige Eingaben in meiner Oberfläche habe. Ich möchte nur Werte ausgeben.

    Gruß

    Hier gibts ein Timer-Beispiel. Beachte aber, dass es GTK+2 ist und nicht GTK+3:

    http://zetcode.com/gui/gtk2/gtkevents/



  • Das ist C. Ich würde dringend gtkmm empfehlen.



  • Danke für eure Links, schaue ich mir jetzt an.

    Mein anderes Programm das ich hier noch nicht erwähnt habe und das mir laufend neue Werte in meine Variablen(oder z.B. Structs) schreibt, ist auch in C geschrieben.
    Welchen Vorteil habe ich mit gtkmm (C++) ggü. der gtk+ (C) Variante? Mit gtk+ kann man auch Objektorientiert programmieren.

    ---

    Ich hänge gleich noch eine Frage dran, bevor ich mir einen Wolf programmiere und es am Ende nicht funktioniert.

    Ich habe wie gesagt 2. Programmteile.

    Der erste Teil empfängt Daten und lädt ständig neue Werte (überschreibt die alten) in meine Structs/Variablen und zwar mit seinem eigenen Takt. Z.B. alle 100ms.

    Der 2. Teil (der hier mit GTK) soll mir eben diese sich ständig aktualisierenden Werte in einer Tabelle grafisch ausgeben. Ebenfalls mit seinem eigenen Takt (z.B. alle 50ms nur als Beispiel).

    Mal angenommen ich bekomme das mit dem Timer-Event gebacken und das GTK-Programm startet immer wieder von neu. Dann möchte ich ja den 1. Programmteil (eigene Funktion) nicht ständig erneut sondern nur einmal am Anfang aufrufen(da es von alleine permanent läuft).

    Ich habe es mir so überlegt, weis aber noch nicht ob das geht:
    die main Funktion ruft die 1. Funktion auf (den 1. Programmteil). Diese schreibt die sich ändernden Werte in ein Pointer-struct. Anschließend kommt die GTK-Dauerschleife ODER ich schreibe die GTK-Dauerschleife in einen eigene Funktion (wenn das funktioniert) und rufe diese auf. Diese greift über einen Pointer auf das Struct mit den sich aktualisierenden Werten zu.

    Könnte das so klappen?

    Viele Grüße



  • Wenn man GTK in C programmiert, schreibt man sehr viel Boiler Plate Code. Und überhaupt finde ich C++ viel besser als C, seh echt keinen Grund, sich C anzutun, wenn man die Wahl hat.
    Ja, man kriegt alles irgendwie hin, aber es hört sich so an, als ob du nicht so viel Programmiererfahrung hast, deswegen hören sich die Fragen auch etwas banal an. Wie gut die Lösung wäre kann man so einfach also nicht beantworten, dir fehlt wahrscheinlich das Verständnis für Softwaredesign und -architekturen.



  • Mechanics schrieb:

    Wenn man GTK in C programmiert, schreibt man sehr viel Boiler Plate Code. Und überhaupt finde ich C++ viel besser als C, seh echt keinen Grund, sich C anzutun, wenn man die Wahl hat.
    Ja, man kriegt alles irgendwie hin, aber es hört sich so an, als ob du nicht so viel Programmiererfahrung hast, deswegen hören sich die Fragen auch etwas banal an. Wie gut die Lösung wäre kann man so einfach also nicht beantworten, dir fehlt wahrscheinlich das Verständnis für Softwaredesign und -architekturen.

    Ja, aber ich habe eben auch nur was mit C gemacht und nicht C++.
    Nein sehr viel Erfahrung habe ich noch nicht, daher bin ich ja hier 😉



  • Du kannst es natürlich auch in C schreiben (ich hab eigentlich automatisch, angenommen, dass es um C++ geht). C++ ist schon eine komplexe Sprache, die lernt man nicht einfach so nebenbei.



  • JohnDillinger schrieb:

    Mal angenommen ich bekomme das mit dem Timer-Event gebacken und das GTK-Programm startet immer wieder von neu. Dann möchte ich ja den 1. Programmteil (eigene Funktion) nicht ständig erneut sondern nur einmal am Anfang aufrufen(da es von alleine permanent läuft).

    Ich habe es mir so überlegt, weis aber noch nicht ob das geht:
    die main Funktion ruft die 1. Funktion auf (den 1. Programmteil). Diese schreibt die sich ändernden Werte in ein Pointer-struct. Anschließend kommt die GTK-Dauerschleife ODER ich schreibe die GTK-Dauerschleife in einen eigene Funktion (wenn das funktioniert) und rufe diese auf. Diese greift über einen Pointer auf das Struct mit den sich aktualisierenden Werten zu.

    Könnte das so klappen?

    Viele Grüße

    Wenn Du mit GTK+ programmieren willst, bist Du mit C auf der sicheren Seite. Vor allem, wenn Du auch die Funktionen der GLib nutzt. GTKmm oder etwa PyGTK und andere Wrapper verlangsamen das Ganze nur...

    Was ich jetzt nicht wirklich verstehe, ist Dein Programmablauf:

    1. Du bekommst von irgendwoher Daten
    2. Diese Daten möchtest Du ständig aktualisiert anzeigen.

    Wie ist da die konkrete Konstellation? Ein Programm? - Schnittstelle? - Mehrere Programme?

    Jenachdem wäre auch eine Multithreadlösung denkbar.

    Und vielleicht die wichtigste Frage: Wie schätzt Du Deinen Programmierskill ein?

    Btw: Bei der Timerlösung startet das GTK Programm nicht immer wieder von vorne, sondern es wird im definierten Zeitintervall ein Callback aufgerufen



  • abcdefg schrieb:

    JohnDillinger schrieb:

    Mal angenommen ich bekomme das mit dem Timer-Event gebacken und das GTK-Programm startet immer wieder von neu. Dann möchte ich ja den 1. Programmteil (eigene Funktion) nicht ständig erneut sondern nur einmal am Anfang aufrufen(da es von alleine permanent läuft).

    Ich habe es mir so überlegt, weis aber noch nicht ob das geht:
    die main Funktion ruft die 1. Funktion auf (den 1. Programmteil). Diese schreibt die sich ändernden Werte in ein Pointer-struct. Anschließend kommt die GTK-Dauerschleife ODER ich schreibe die GTK-Dauerschleife in einen eigene Funktion (wenn das funktioniert) und rufe diese auf. Diese greift über einen Pointer auf das Struct mit den sich aktualisierenden Werten zu.

    Könnte das so klappen?

    Viele Grüße

    Wenn Du mit GTK+ programmieren willst, bist Du mit C auf der sicheren Seite. Vor allem, wenn Du auch die Funktionen der GLib nutzt. GTKmm oder etwa PyGTK und andere Wrapper verlangsamen das Ganze nur...

    Was ich jetzt nicht wirklich verstehe, ist Dein Programmablauf:

    1. Du bekommst von irgendwoher Daten
    2. Diese Daten möchtest Du ständig aktualisiert anzeigen.

    Wie ist da die konkrete Konstellation? Ein Programm? - Schnittstelle? - Mehrere Programme?

    Jenachdem wäre auch eine Multithreadlösung denkbar.

    Und vielleicht die wichtigste Frage: Wie schätzt Du Deinen Programmierskill ein?

    Btw: Bei der Timerlösung startet das GTK Programm nicht immer wieder von vorne, sondern es wird im definierten Zeitintervall ein Callback aufgerufen

    Ok Danke.

    Ich versuche es nochmal zu beschreiben.

    1. Über externe Hardware bekomme ich über eine Schnittstelle Werte rein. Z.B. Temperaturwerte. Allerdings als Rohwerte. Die Schnittstelle bringt eine eigene Library und Funktionen mit. Damit kann ich die Rohwerte empfangen und verarbeiten, wie filtern oder in dezimal Zahlen umrechnen (Z.B. Grad Celcius etc.). Anschließend möchte ich diese in ein Struct packen. Und das mit mehreren Variablen, zum Beispiel Temperatur A, Temperatur B, ...
    In einem festen Zeitintervall kommen ständig neue Werte rein (z.B. alle 100ms) und sollen die alten Werte für Temp A, Temp B, ... im Struct überschreiben.

    Das ist somit mein erster Programmteil. Ich dachte mir ich mache daraus eine Funktion die ich dann einmal aufrufe und von alleine ständig weiterläuft.

    2. Diese Werte aus dem Struct (z.B. TempA, Temp B, ...) in einer Tabelle über eine GUI auf dem Bildschirm ausgeben. Die Tabelle soll ständig die neuen Werte anzeigen, wobei hier z.B. ein Update alle 200ms-500ms völlig ausreicht.

    ------

    Zur Timerlösung: Ok, könnte das aber trotzdem klappen indem ich die Funktion für den 1. Teil vor dem Callback einmalig ausführe und die Tabelle über den Dauercallback aktualisiere?

    Programmierkenntnisse: Ich hatte Programmieren in meinem Technischen Studium. Ich kenne in C denke ich alle (wichtigen)Funktionen, Structs, Pointer, verkettete Listen und kann mich halt mit Recherche in Code, wie von der Timerlösung einarbeiten.
    Komplexeres haben wir aber nicht gemacht. Unter einer Multithreadlösung kann ich mir zwar was vorstellen, habe ich aber noch nie behandelt.

    Gruß

    edit: Ich muss nicht mit GTK programmieren, ich möchte einfach auf dem kürzesten Weg ans Ziel kommen. Wenn es anders z.B. mit qt einfacher geht, dann arbeite ich mich halt da nochmal rein.



  • Ich befürchte Dein Problem liegt weniger am Anzeigen Deiner Daten in einem "Timer-Callback", sondern in der Synchronisation mit den Eingabedaten, die ständig überschrieben werden.

    Bevor ich jetzt darauf eingehe - liege ich da richtig?

    In einem festen Zeitintervall kommen ständig neue Werte rein (z.B. alle 100ms) und sollen die alten Werte für Temp A, Temp B, ... im Struct überschreiben.

    Vielleicht so als Hinweis: Nehmen wir an, Du synchronisierst das irgendwie. Was machste aber, wenn das eine oder andere System überlastet ist.
    Das Stichwort heisst wohl Datenübertragung, das Du lösen musst (Stichwort: etwa via TCP/IP oder welches auch immer).



  • abcdefg schrieb:

    Ich befürchte Dein Problem liegt weniger am Anzeigen Deiner Daten in einem "Timer-Callback", sondern in der Synchronisation mit den Eingabedaten, die ständig überschrieben werden.

    Bevor ich jetzt darauf eingehe - liege ich da richtig?

    In einem festen Zeitintervall kommen ständig neue Werte rein (z.B. alle 100ms) und sollen die alten Werte für Temp A, Temp B, ... im Struct überschreiben.

    Vielleicht so als Hinweis: Nehmen wir an, Du synchronisierst das irgendwie. Was machste aber, wenn das eine oder andere System überlastet ist.
    Das Stichwort heisst wohl Datenübertragung, das Du lösen musst (Stichwort: etwa via TCP/IP oder welches auch immer).

    Entschuldigung, ich glaube ich habe eine Info vergessen.
    Es kommen zwar alle 100ms neue Werte rein, ich brauche aber nicht jeden dieser Werte anzeigen. Beispiel:

    TempA: t=0s -> T=20.00K
    TempA: t=100ms -> T=20.01K
    TempA: t=200ms -> T=20.12K
    TempA: t=300ms -> T=20.09K
    TempA: t=400ms -> T=20.06K
    TempA: t=500ms -> T=20.10K

    Es ist okay wenn ich nur den Wert für t=0 und für t=500ms Anzeige und die Werte dazwischen "verloren" gehen. Also zum Zeitpunkt t=0 werden mir 20.00K angezeigt und eine halbe Sekunde später updatet er den Anzeigewert auf dem Display auf 20.10K.

    Erübrigt sich das mit der Synchronisation dann?



  • JohnDillinger schrieb:

    Erübrigt sich das mit der Synchronisation dann?

    Ich denke ja...

    Dein Problem ist ja dann damit gelöst - Nimm das Timer Beispiel in meinem ersten Posting - schmeisse das Cairo raus (es sei denn Du willst was malen) - aktiviere den Timer

    g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
    

    1000 steht für 1000 ms -> also nach Wunsch anpassen.

    und im Callback time_handler holst Du dann Deine Temperaturwerte und zeigst diese in einem Fenster an...

    Fertig 🙂



  • Melde mich spaeter!



  • ########header.h
    
    #include <gtk/gtk.h>
    
    typedef struct {
        int temp;
        char abfrage; //erwartet z.B. "OK"
    } sensorik;
    ######################
    
    #include "header.h"
    
    void einlesenDaten(sensorik *innenraum, sensorik *aussenraum){
    
        int tempSchnittstelle1, tempSchnittstelle2;
        char abfrageSchnittstelle1, abfrageSchnittstelle2;
    
        // ... blabla meine Schnittstellenfunktionen ...
    
        innenraum->temp = tempSchnittstelle1;
        innenraum->abfrage = abfrageSchnittstelle1;
        aussenraum->temp = tempSchnittstelle2;
        aussenraum->abfrage = abfrageSchnittstelle2;
    
    }
    
    gboolean time_handler(GtkWidget *widget, sensorik *innenraum, sensorik *aussenraum) {
    
        GtkWidget *table, *label;
    
        if (widget->window == NULL) return FALSE;
    
        gtk_widget_destroy(table) 
    
        table = gtk_table_new(2, 2, 0);
    
        gchar *buffer = malloc(sizeof(char) *1024);
        strcpy(&buffer, innenraum->temp); //davor int-Wert in char umwandeln
        label = gtk_label_new(buffer);
        gtk_table_attach(GTK_TABLE(table), label, 0,1,0,1, GTK_FILL, GTK_FILL, 0,0);
        g_free(buffer);
    
        gchar *buffer = malloc(sizeof(char) *1024);
        strcpy(&buffer, innenraum->abfrage);
        label = gtk_label_new(buffer);
        gtk_table_attach(GTK_TABLE(table), label, 1,2,0,1, GTK_FILL, GTK_FILL, 0,0);
        g_free(buffer);
    
        gchar *buffer = malloc(sizeof(char) *1024);
        strcpy(&buffer, aussenraum->temp); //davor int-Wert in char umwandeln
        label = gtk_label_new(buffer);
        gtk_table_attach(GTK_TABLE(table), label, 0,1,1,2, GTK_FILL, GTK_FILL, 0,0);
        g_free(buffer);
    
        gchar *buffer = malloc(sizeof(char) *1024);
        strcpy(&buffer, aussenraum->abfrage);
        label = gtk_label_new(buffer);
        gtk_table_attach(GTK_TABLE(table), label, 1,2,1,2, GTK_FILL, GTK_FILL, 0,0);
        g_free(buffer);
    
        gtk_container_add(GTK_CONTAINER(window), table);
    
        return TRUE;
    }
    
    int main(int argc, char *argv[]) {
    
      GtkWidget *window; 
      sensorik innenraum, aussenraum;
    
     einlesenDaten(&innenraum, &aussenraum);
    
      gtk_init(&argc, &argv);
    
      window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    
      g_signal_connect(window, "???", //?????
          G_CALLBACK(time_handler), table); 
      g_signal_connect(window, "destroy",
          G_CALLBACK(gtk_main_quit), NULL);
    
      gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
      gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
      gtk_window_set_title(GTK_WINDOW(window), "blabla");
    
      g_timeout_add(200, (GSourceFunc) time_handler, (gpointer) window, &innenraum, &aussenraum); 
      gtk_widget_show_all(window);
    
      gtk_main();
    
      return 0;
    }
    

    Hier mein Code. Es passt noch nicht , ich habe mal ein paar Fragen zusammengefasst:

    1. Wenn ich es richtig verstanden habe kann ich die on_expose_event Funktion weglassen, da ich kein cairo benutze.

    2. Plotte ich meine Ausgaben mittels eines "labels". Ich bin mir nicht sicher ob das dafür vorgesehen ist, variable Werte auszugeben. Alternativ habe ich noch die TextView und TextBuffer Funktion gefunden. Weis aber nicht ob ich das unbedingt brauche.

    3. Habe ich mehrere Parameter der time_handler Funkion übergeben, ich glaube aber, ich kann nur eine übergeben wenn ich sie mittels g_timeout_add aufrufe. Alternativ müsste ich die 2 sensorik-structs global deklarieren?

    4. Habe ich die Tabelle erst im time_handler erzeugt, verarbeitet und auch da mit dem Container der windows verknüpft. Des Weiteren zerstöre ich die Tabelle bei und erstelle Sie immer wieder neu. Ist die Vorgehensweise OK?

    5. muss ich für den buffer Pointer Speicherplatz reservieren und habe ich das mit der Freigabe richtig programmiert?

    6. Da das Label-Widget nur einen char Pointer übergeben haben möchte, muss ich die int-Werte in Chars umwandeln. (Alternative?)
    Hier sind aber noch weitere Fehler, so wie es da steht, stimmt es nicht. Was habe ich falsch gemacht?

    7. Was meinst du mit dem Callback vom time_handler, bist du sicher das ich das hier überhaupt brauche? Schliesslich ruft sich die Funktion eh ständig neu auf bis ein FALSE zurückgegeben wird. --> Wie würde das Signal und Callback denn aussehen.

    8. In deinem Beispiellink gibt es am Ende noch einen time_handler aufruf, der aber meiner Meinung nach unnütz ist. Hab ich rausgeworfen.

    9. Für weitere Fehlerkorrekturen wäre ich dankbar. 👍

    Hier noch ein weiteres Beispiel:
    http://stackoverflow.com/questions/12255383/refresh-table-in-gtk



  • Hier ist ein Beispiel, das die meisten Deiner Fragen beantwortet:

    #include <stdlib.h>
    #include <string.h>
    #include <gtk/gtk.h>
    //
    typedef struct _WorkFields // In der main() wird der Storage zugeordnet (g_slice_new)
    {
    	GtkWidget    	*window1;	// Der angezeigte Bildschirm
    //
    	GtkWidget      	*label1;	// Anzeige der Temperatur oder was auch immer
    //	Eventuell weitere Widgets
    //	.
    //	.
    //	Hier kommen die Arbeitsvariablen
    	gint			temperatur_updater; // Es wird hier als Beispiel nur was hochgezählt
    //	.
    //	.
    //
    } WorkFields;
    //
    gboolean time_handler(WorkFields *work) 
    {
    	gchar			val[40];
    //
    	if (work->window1 == NULL) return FALSE;
    
    	work->temperatur_updater++; // Hier sollten die Temperaturwerte irgendwie gelesen werden
    //        
    	sprintf(val, "Current Temp.:\n%d K", work->temperatur_updater); // um dann angezeigt zu werden...
    	gtk_label_set_text (GTK_LABEL (work->label1), val);
    
    	return TRUE;
    }
    //
    int main(int argc, char *argv[]) {
    //
    	const gchar 	*title = "Temperatur";
    	WorkFields   	*work;	// Variable, die das gesamte Programm benötigt
    //	
    	work = g_slice_new(WorkFields); // Speicher für die gesamte Struct
    //
    //	Standard für jedes GTK Programm
    	gtk_init(&argc, &argv);
    	work->window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(work->window1), title);
        gtk_window_set_default_size(GTK_WINDOW(work->window1), 200, 100);
        gtk_window_set_position(GTK_WINDOW(work->window1), GTK_WIN_POS_CENTER);
        work->label1 = gtk_label_new (title); // Ein einfaches Label
        gtk_container_add(GTK_CONTAINER(work->window1), work->label1);
    	g_signal_connect(G_OBJECT(work->window1), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    //
    //
    	g_timeout_add(1000, (GSourceFunc) time_handler, work); // Start des Timers im Intervall 1000 ms
    	gtk_widget_show_all(work->window1);
    	time_handler(work); // Nötig, da der Timeraufruf erst nach der angegebenen Intervallzeit startet
    									// Kommentiere es aus, dann sieht man den Effekt
    	gtk_main();
    //
    	g_slice_free(WorkFields, work);
    	return 0;
    }
    

    Die Struktur des Programms entspricht ja in etwa Deinem Ansatz. Es läuft unter GTK+2 und GTK+3.
    zu (1) richtig
    zu (2) Siehe Programm. Natürlich kannst Du auch andere Widgets benutzen.
    zu (3) Siehe Programm. Es kann nur eine Adresse übergeben werden - mit einer struct kannst Du natürlich mehrere Variable übergeben
    zu (4) Das verlinkte Beispiel ist eher unüblich. Man legt idR. ein Fenster ausserhalb einer Timer-Funktion fest
    zu (5) Siehe Programm ( g_slize_new() )
    zu (6) Siehe Programm. Das hat nichts mit GTK+ zu tun, sondern ist C (etwa gdouble zur Anzeige aufbereiten)
    zu (7) Der g_timeout_add () verhält sich wie ein Callback - eben periodisch...
    zu (8) Siehe Programm. In Deinem Fall ist es wohl nicht wichtig - Der Timerstart erfolgt zum ersten Mal nach der angegebenen Zeit

    Btw: Grundsätzlich programmiere ich GTK+ mit XML definiertem GUI. Die wichtigsten Fensterinhalte kann ich dann via Glade-Designer einfach zusammenklicken. Aber natürlich - jeder wie ers mag...

    🙂


Anmelden zum Antworten