GTK+ Dauerschleife
-
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 ZeitBtw: 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...
-
Super Code Danke! Damit kann ich das denke ich lösen.
UPDATE: -andere Fragen geklärt und gelöscht-
----------
UPDATE: -Diese Frage ist noch offen-:
Noch was anderes, ich muss ja die Funktion die mit dem Sensoren zusammenarbeitet und mir die Daten in die Structs schiebt einmalig am Anfang aufrufen.
Wenn ich es richtig verstanden habe muss ich dazu einen Thread erstellen.
Wie funktioniert das genau, mal angenommen ich möchte die Funktion "readData" einmalig starten. Soviel wie ich bisher rausgefunden habe geht das in etwa so:
void readData(WorkFields *work){ //aus Schnittstelle Daten empfangen und ins Struct schreiben } int main(){ const gchar *title = "Temperatur"; WorkFields *work; work = g_slice_new(WorkFields); pthread_t thread1; g_thread_init (NULL); gdk_threads_init (); gdk_threads_enter (); //blabla das restliche Programm pthread_create (&thread1, NULL, readData, work); g_timeout_add(1000, (GSourceFunc) time_handler, work); gtk_widget_show_all(work->window1); time_handler(work); tk_main (); gdk_threads_leave (); return 0; }
Wenn es Fehler gibt, gerne verbessern. Ich habe das jetzt mal für dein Programm geschrieben. Sprich die Reihenfolge der Befehle hätte ich wie im Code gemacht.
-
Schade wenn abcdefg hier nicht mehr reinschauen sollte. Gibt es jemanden anderen der das weis? Ist ja doch eher eine allgemeine Frage, das Thema Threads.
Gruß
-
JohnDillinger schrieb:
Schade wenn abcdefg hier nicht mehr reinschauen sollte. Gibt es jemanden anderen der das weis? Ist ja doch eher eine allgemeine Frage, das Thema Threads.
Gruß
JohnDillinger schrieb:
UPDATE: -andere Fragen geklärt und gelöscht-
Nach wie vor denke ich, dass Du nicht mit Threads in Zusammenhang Deiner Temperaturabfrage arbeiten musst. Die Abfrage eines Sensors - sei es Temperatur, Helligkeit, Wasserstand oder was auch immer, gehört eigentlich zu den trivialsten Aufgaben.
abcdef schrieb:
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?
Wieso kannst Du in dieser Timerschleife nicht einen Port abfragen, der Dir die aktuellen Temperaturen liefert?
-
abcdefg schrieb:
JohnDillinger schrieb:
Schade wenn abcdefg hier nicht mehr reinschauen sollte. Gibt es jemanden anderen der das weis? Ist ja doch eher eine allgemeine Frage, das Thema Threads.
Gruß
JohnDillinger schrieb:
UPDATE: -andere Fragen geklärt und gelöscht-
Nach wie vor denke ich, dass Du nicht mit Threads in Zusammenhang Deiner Temperaturabfrage arbeiten musst. Die Abfrage eines Sensors - sei es Temperatur, Helligkeit, Wasserstand oder was auch immer, gehört eigentlich zu den trivialsten Aufgaben.
abcdef schrieb:
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?
Wieso kannst Du in dieser Timerschleife nicht einen Port abfragen, der Dir die aktuellen Temperaturen liefert?
Weil ich die Funktionen der Schnittstelle brauche.
Zum einem kann ich mit Ihnen mit der Hardware kommunizieren (Sensorwerte einlesen) und zum anderen bieten Sie mir weitere Optionen wie Filterungen.
Mal angenommen ich frage einen Sensor (nicht nur Temp.sensoren) alle 100ms ab. Es dauert aber einmal 500ms bis ein neuer Wert kam. Dann kann ich das als Meldung ausgeben und so weiter...
Es ist ein wenig umfangreicher und komplizierter. Ich versuche es nur so einfach wie möglich zu beschreiben damit es, unter anderem auch für passive Mitleser, einfacher zu verstehen ist.Ich muss das also einmalig anstoßen und dann rennt das Ding und schiebt mir meine gewünschten Werte in die Structs, mit denen ich dann parallel in einer anderen Funktion weiterarbeite (veraendern, ausgeben...).
Ich dachte dafür braucht man Threads. Liege ich da richtig?
Wenn ja, ist mein Code denn so in etwa korrekt?Gruß
-
JohnDillinger schrieb:
Ich muss das also einmalig anstoßen und dann rennt das Ding und schiebt mir meine gewünschten Werte in die Structs, mit denen ich dann parallel in einer anderen Funktion weiterarbeite (veraendern, ausgeben...).
Wo sind diese Structs denn angesiedelt? - Wenn diese Structs in einem eigenen Prozess sind, wirst Du darauf von einem anderen Prozess nicht zugreifen können, der seperat gestartet wird (GTK+ Programm)
JohnDillinger schrieb:
Ich dachte dafür braucht man Threads. Liege ich da richtig?
Wenn ja, ist mein Code denn so in etwa korrekt?Ich weiss nicht, ob Du diesen Link schon gefunden hast - aber hier mal eine deutsche Seite, die das Konzept von Threadprogrammierung auf einfache Art erläutert (ist zwar recht alt, aber geändert hat sich am Konzept nichts):
http://www.ijon.de/comp/tutorials/threads/index.htmlDieses "shared Memory" bezieht sich eigentlich nur auf eine "Unit of Compile/Link" - sprich: Es gibt ein Hauptprogramm, das diese Threads "managed", also verwaltet - auch Dein Portabfrageprogramm müsste in dieser "Unit of Compile/Link" dort eingebunden werden.
Wenn jemand mitliest, der es besser weiss - please fix me...
-
Jap hatte ich schon angeschaut.
Hier sagt er aber, das die Threads schon auf den selben Speicherbereich zugreifen können um sich auszutauschen. Ist deine Aussage nicht das Gegenteil davon oder habe ich es falsch verstanden?
Die Structs liegen wie in deinem Beispiel in den WorkFields. Sprich ich mache alles (labels, table, window, structs...) über einen Pointer *work und reserviere Speicherplatz dafür. Dann kann ich von überall darauf zugreifen.
Geht das nicht Threadübergreifend wenn ich dem Thread, den Pointer *work übergebe?Dann gibt es ja noch das Thema Mutexe, um zu verhindern das 2 Threads gleichzeitig z.B. eine Variable verändern wollen und es zu undefinierten Zuständen kommt.
Bei mir soll der "2. Thread" die Werte in das Struct schieben und der andere Programmteil (time_handler) entnimmt diese Werte nur, z.B. kopiert sie in eigene lokale Variablen und verarbeitet nur diese Variablen (umrechnen, ausgeben).
--> Dann kann ich mir das mit den Mutexen doch auch sparen, da ich nur von "einer Seite" schreibe und von der "anderen Seite" nur lese, stimmt das?