GTK+ GUI friert ein (Multithread)



  • Hallo

    Ich habe Probleme mit einer GTK GUI. Das Programm soll eigentlich alle 250ms Daten über die serielle Schnittstelle von einem µController abrufen.
    Diese Datenabfrage läuft in einem Thread. Das funktioniert auch so wie es soll. Allerdings friert das Programm komplett ein, wenn ich ein Bedienelement (button, Slider) betätige.
    Ich habe mit gdk_threads_enter (); / gdk_threads_leave (); schon versucht, das Problem zu lösen. Leider ohne Erfolg.
    Kann ich die Datenabfrage/Darstellung irgendwie anders lösen? Bin nicht an einen Thread gebunden. Könnte es evt. auch über einen Timer lösen. Hauptsache das Userinterface ist nachher sauber zu bedienen (kein stockender slider etc.).
    Wie mache ich so etwas am besten?

    Hier mal mein code:

    void *update_data (void)
    {
    	char buf[40];
    
    	for(;;)
    	{
    		pthread_mutex_lock (&mutex_UART);		
    		temperature = getTemperature();		//Abfrage über serielle Schnittstelle
    		g_forceX = getForceX();			//Abfrage über serielle Schnittstelle
    		g_forceY = getForceY();			//Abfrage über serielle Schnittstelle
    		pthread_mutex_unlock(&mutex_UART);
    
    		gdk_threads_enter ();
    		if ( temperature & 0x80 )		//Bit15=0 positiv, Bit15=1 negativ
    		{
    			sprintf(buf, "Temperatur: -%i°C", (((~temperature)+1) & 0x7F) );
    		}
    		else
    		{
    			sprintf(buf, "Temperatur: +%i°C", temperature&0x7F);
    		}
    		gtk_label_set_text(GTK_LABEL(label1), buf);	//Darstellung der abgefragten Daten
    
    		sprintf(buf, "ForceX: %dN", g_forceX);
    		gtk_label_set_text(GTK_LABEL(label2), buf);	//Darstellung der abgefragten Daten
    
    		sprintf(buf, "ForceY: %dN", g_forceY);
    		gtk_label_set_text(GTK_LABEL(label3), buf);	//Darstellung der abgefragten Daten
    
    		gdk_threads_leave ();
    
    		usleep (250000);
    	}
    }
    
    int main (int argc, char *argv[])
    {
    	GtkWidget *window;
    	GtkWidget *box;
    
    	GtkWidget *vscale;
    
    	g_thread_init (NULL);
    	gdk_threads_init ();
    	gdk_threads_enter ();
    
    	pthread_attr_t thread_attr;
    	pthread_t udata; 
    
    	uart_port = argv[1];	
    	gtk_init(&argc, &argv);
    	pthread_mutex_init(&mutex_UART,NULL);
    	openUART(&uart_fd);
    	if(uart_fd==-1)
    	{
    		printf("uart_open failed...\n");
    	}
    
    	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    	gtk_window_set_default_size(GTK_WINDOW(window), 1024, 768);
    
    	box = gtk_fixed_new();
    
    	label1 = gtk_label_new("Temperatur:");
    	gtk_widget_modify_font (label1,pango_font_description_from_string ("Monospace 12"));
    	gtk_fixed_put(GTK_FIXED(box), label1, MW1_XC, MW1_YC);
    	label2 = gtk_label_new("ForceX:");
    	gtk_widget_modify_font (label2,pango_font_description_from_string ("Monospace 12"));
    	gtk_fixed_put(GTK_FIXED(box), label2, MW2_XC, MW2_YC);
    	label3 = gtk_label_new("ForceY:");
    	gtk_widget_modify_font (label3,pango_font_description_from_string ("Monospace 12"));
    	gtk_fixed_put(GTK_FIXED(box), label3, MW3_XC, MW3_YC);
    
    	gtk_container_add(GTK_CONTAINER(window), box);
    
    //Slider (fan control)
    
    //value, lower, upper, step_increment, page_increment, page_size
    //Note that the page_size value only makes a difference for
    //scrollbar widgets, and the highest value you'll get is actually
    //(upper - page_size).
    	adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 25.0, 25.0, 1.0);
    
    	vscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1));
    	gtk_widget_set_size_request (GTK_WIDGET (vscale), 200, -1);
    	gtk_scale_set_digits (GTK_SCALE (vscale), 0);
    	gtk_scale_set_value_pos (GTK_SCALE (vscale), GTK_POS_RIGHT);
    //	gtk_box_pack_start (GTK_BOX (box), vscale, FALSE, FALSE, 2);
    	gtk_widget_set_tooltip_text( vscale, "controls the rpm of the fan");
    	g_signal_connect (G_OBJECT (vscale), "value_changed", G_CALLBACK (vscale_value_changed), (gpointer) window);
    	gtk_fixed_put(GTK_FIXED(box), vscale, 400, 700);
    
    //	gtk_container_add(GTK_CONTAINER(window), box);
    
    	gtk_widget_show_all(box);
    
    	gtk_widget_set_app_paintable(window, TRUE);
    
    	gtk_widget_show_all(window);
    
    	pthread_attr_init(&thread_attr);
    	pthread_attr_setstacksize (&thread_attr, PTHREAD_STACK_MIN);
    	pthread_create (&udata, &thread_attr, (void*(*)(void*))&update_data, NULL);
    	pthread_detach (udata);
    
    	gtk_main();
    
    	gdk_threads_leave ();
    
    	return 0;
    }
    

    Die Deklaration der globalen Variablen habe ich wegen der Übersichtlichkeit weggelassen. Denke auch nicht dass das Problem hierin liegt.

    Vielen Dank im Voraus

    Gruß

    Bean



  • Mr Bean schrieb:

    Hallo

    Ich habe Probleme mit einer GTK GUI. Das Programm soll eigentlich alle 250ms Daten über die serielle Schnittstelle von einem µController abrufen.
    Bean

    Du willst also diese Werte aus der ser. Schnittstell in einem Fenster ständig aktualisiert darstellen?

    Mr Bean schrieb:

    Diese Datenabfrage läuft in einem Thread. Das funktioniert auch so wie es soll. Allerdings friert das Programm komplett ein, wenn ich ein Bedienelement (button, Slider) betätige.
    Bean

    Ok, Du benötigst also noch eine gewisse Manipulation dieser Abfrage/Anzeige?
    Diese Frage stelle ich, weil ich da was von "mutex" in Deinem Programm gesehen habe.

    Mr Bean schrieb:

    Kann ich die Datenabfrage/Darstellung irgendwie anders lösen? Bin nicht an einen Thread gebunden. Könnte es evt. auch über einen Timer lösen. Hauptsache das Userinterface ist nachher sauber zu bedienen (kein stockender slider etc.).
    Wie mache ich so etwas am besten?
    Bean

    Vordergründig würde ich so etwas einfaches eher mit einem Timer lösen.

    Aber eben, Deine Angaben sind etwas dürftig - ich weiss ja nicht, ob sich sonst noch etwas in Deinem System abspielt, was für die Aufgabenstellung relevant sein könnte, um einen eigenen Thread zu rechtfertigen...



  • Hallo

    Vielen Dank für die Antwort.

    Ja genau, ich möchte die Werte von der seriellen Schnittstelle ständig aktualisiert in einem Fenster darstellen. Derzeit mache ich das eben über Labels.

    Den Mutex habe ich drin, damit es zu keinen Problemen kommt, wenn der Thread auf den UART Port zugreift und der User über einen Button auch eine Aktion auf dem UART Port auslöst. Ich brauche nicht unbedingt einen extra Thread. Das war halt die Möglichkeit die mir eingefallen ist.
    Hast Du mir ein Beispiel wie ich soetwas mit einem Timer löse? Muss ich hier einen speziellen gtk Timer verwenden damit es zu keinen Konflikten bei User aktionen (Button gedrückt, slider "geschoben") kommt?

    Gruß,

    Bean



  • Mr Bean schrieb:

    [...]
    Den Mutex habe ich drin, damit es zu keinen Problemen kommt, wenn der Thread auf den UART Port zugreift und der User über einen Button auch eine Aktion auf dem UART Port auslöst.
    [...]

    Genau das ist der Punkt, um den es geht. Daher meine Frage:

    Ich schrieb:

    Ok, Du benötigst also noch eine gewisse Manipulation dieser Abfrage/Anzeige?
    Diese Frage stelle ich, weil ich da was von "mutex" in Deinem Programm gesehen habe.

    Ich wüsste keine Timerlösung wenn Du gewisse Bereiche/Zugriffe auf Ports schützen musst und daher nochmal die Frage: Was machen Deine Buttons und Slider (in GTK+ heissen die GtkScale..). Und gibt es ausser diesen - ich sage mal "applikationsinternen" Events sonst noch Einflüsse?
    Wenn es so ist, wie ich vermute (also nur interne), dann google mal nach <"timeout" & "gtk+">.
    Auf der zweiten Seite wirst Du fündig:
    http://www.gtk.org/api/2.6/glib/glib-The-Main-Event-Loop.html
    Wenn Du ein einfaches Beispiel brauchst - vor etwa zwei Monaten habe ich ein solches hier gepostet:
    http://www.c-plusplus.net/forum/305191?highlight=timeout
    Ist allerdings nocht Gtk+2 - für Gtk+3 müssen ein paar Änderungen vorgenommen werden.



  • Hallo

    Also als Erklärung zu dem Programm:
    Das Programm kommuniziert über die serielle Schnittstelle mit einem µController. Dieser misst Analogspannungen die auf dem PC kontinuierlich (ohne Zutun des users) visualisiert werden sollen. Außerdem erzeugt der µController ein PWM Signal (puls weiten moduliertes Rechtecksignal). Die Pulsbreite soll über einen slider (gtkscale) eingestellt werden können. Das geht auch über die serielle Schnittstelle.
    Weiter soll es möglich sein, über das Betätigen von Buttons Ausgänge des µC auf High bzw. Low zu setzen.
    Hierzu habe ich ein Protokoll implementiert, wodurch der µC entscheiden kann, ob er die Analogwerte zurückliefern soll oder Werte für die PWM empfängt. Oder eben eine andere Aktion ausführen soll.

    Sehe ich das richtig, dass es durch die oben genannten Anforderungen, nicht möglich ist, dieses Problem über einen Timer zu lösen? Gibt es noch andere Möglichkeiten das Problem zu lösen?

    Tut mir Leid dass ich so "dumm" fragen muss, aber hab jetzt schon einige Sachen ausprobiert und leider noch keine Lösung gefunden, die wirklich gut funktioniert hat... 😞

    Danke im Voraus für jeden Tipp.

    Gruß

    Bean


Anmelden zum Antworten