Gtk3 Problem mit Aktualisierung des Bildinhalts

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
ElektroLurch
Beiträge: 2
Registriert: 05.10.2021 14:40:41

Gtk3 Problem mit Aktualisierung des Bildinhalts

Beitrag von ElektroLurch » 05.10.2021 15:36:25

Hallo zusammen,

ich habe ein Problem mit der Aktualisierung des Fensterinhalts in einem GTK-Programm. Das Programm soll in regelmäßigen Abständen ein jpeg-Bild von einem Server laden, mit diversen Filtern verarbeiten und in einem Fenster darstellen.
Herunterladen und verarbeiten funktioniert. Nur das (ich dachte einfache) Aktualisieren der Darstellung klappt nicht.
Der Bildinhalt wird nicht aktualisiert. Im Anhang mein Testprogramm.
Dies liest zur Vereinfachung im Wechsel 2 unterschiedliche Jpeg-Bilder identischer Größe mit gdk_pixbuf_new_from_file() ein.
Inzwischen habe ich festgestellt, dass die Verarbeitungszeit meiner download() Routine eine Rolle spielt. Diese wird über g_timeout_add() alle 0,2 Sekunden aufgerufen.
Wenn die Dauer der download() Routine länger zur Verarbeitung benötigt, klappt es nicht mehr (Simuliert durch usleep(250000)).
Laut Doku von g_timeout_add() wird nicht versucht, verpasste Zeitpunkte aufzuholen sondern einfach das nächste passende Intervall verwendet.

Wäre schön, wenn jemand einen Tipp hat ...


P.S.:
Gibt es nicht noch eine bessere Methode ein GdkPixbuf Objekt zu Aktualisieren ohne jedes mal alle Puffer zu löschen, neu aufzubauen? (Siehe Teil unter "UPDATE IMAGE" Meldung) Die von mir verwendet Methode finde ich sehr umständlich und nicht sehr effizient. Sowas wie get_image_update() wäre schön...

Ab hier mein Testprogramm (in C) :

Code: Alles auswählen

// gcc ReloadTest.c `pkg-config gtk+-3.0 --cflags` `pkg-config gtk+-3.0 --libs` -ljpeg -o ReloadTest

#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>

struct geo_data {
	GdkPixbuf *pixbuf;
	GtkWidget *image;
	GtkWidget *window;
};

void on_destroy (GtkWidget *widget G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
    gtk_main_quit ();
}

int download(struct geo_data *geo)
{
	static int flag=0;
	flag=!flag;

	// Platzhalter für Download-Routine
	// BUG : If delay > timeout, window content isn't updated and image area stays transparent!
	usleep(250000);	

	// Bilddarstellung aktualisieren
	if(geo->pixbuf==NULL) {
		puts("*** NEUER PIXBUF ***");
		geo->pixbuf=gdk_pixbuf_new_from_file("DUMMY.jpg",NULL);
	}
	if(geo->image==NULL) {
		puts("*** NEUES IMAGE ***");
		geo->image=gtk_image_new_from_pixbuf(geo->pixbuf);
		gtk_container_add(GTK_CONTAINER (geo->window), geo->image);
		gtk_widget_show_all(geo->window);
	} else {
		printf("*** UPDATE IMAGE %d ***\n",flag);
		gtk_widget_destroy(geo->image);
		geo->pixbuf=gdk_pixbuf_new_from_file(flag?"dummy.jpg":"DUMMY.jpg",NULL);
		geo->image=gtk_image_new_from_pixbuf(geo->pixbuf);
		gtk_container_add(GTK_CONTAINER (geo->window), geo->image);
		gtk_widget_show_all(geo->window);
		// gtk_widget_show(geo->image);
		// gtk_widget_queue_draw(geo->image);
	}
	return 1;
}

int main(int argc, char *argv[])
{
	struct geo_data geo;
	memset(&geo,0,sizeof(struct geo_data));
	gtk_init (&argc, &argv);
	geo.window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
	g_signal_connect (geo.window, "destroy", G_CALLBACK(on_destroy), NULL);
	g_signal_connect (geo.window, "close", G_CALLBACK(on_destroy), NULL);
	//geo.pixbuf=gdk_pixbuf_new_from_file("dummy.jpg",NULL);
	//geo.image=gtk_image_new_from_pixbuf(geo.pixbuf);
	//gtk_container_add(GTK_CONTAINER (geo.window), geo.image);

	// Einmal manuell aufrufen vor gtk_main();
	download(&geo);

	g_timeout_add(200,(GSourceFunc)download,(gpointer)&geo);
	//gtk_widget_show_all(geo.window);
	puts("*** GTK_MAIN START ***");
	gtk_main();
	return 0;
}
Es werden noch 2 Jpeg-Bilder mit identischer Größe benötigt (ca. 1024x768) : "dummy.jpg" und "DUMMY.jpg".
https://geo-laser.de/support/dummy.jpg
https://geo-laser.de/support/DUMMY.jpg

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: Gtk3 Problem mit Aktualisierung des Bildinhalts

Beitrag von eggy » 06.10.2021 18:29:29

Kennst Du das Konzept eines Mutex? Damit sollte sich das lösen lassen, vor dem Laden des Bildes den Mutex nehmen, Bild laden, wenn Bild ok, Mutex freigeben. Jede Funktion die was mit dem Bild machen will, muss eingangs prüfen, ob es den Mutex bekommen kann, falls nicht, warten bis der verfügbar ist (und somit auch das Bild).

leicht offtopic: Wenn Funktionen die Möglichkeit bereitstellen, dass man über aufgetretene Fehler informiert wird, dann wär's sinnvoll das auch anzunehmen und da ne entsprechende Variable zu übergeben und nicht NULL zu schreiben, außer man ist sich 100% sicher, dass es keine Rolle spielt, ob die Ergebnisse, die man bekommen hat, valide sind.

ElektroLurch
Beiträge: 2
Registriert: 05.10.2021 14:40:41

Re: Gtk3 Problem mit Aktualisierung des Bildinhalts

Beitrag von ElektroLurch » 07.10.2021 11:52:23

@ eggy

Danke für deine Hilfe.
Ich habe es mit dem Mutex versucht aber keine Änderung feststellen können.
Wobei ich dachte, dass die Mutex nur für multi-threaded Anwendungen benötigt werden ?

Ich habe mir das so vorgestellt:
In gtk_main() wird irgendwo abgefragt, ob der Timeout
(welcher mit g_timeout_add() gestartet wurde) abgelaufen ist. Falls ja wird download() aufgerufen und nach dessen Ende wieder nach gtk_main() zurückgekehrt. Alles schön single-threaded :-)

Mit usleep(250000) wird nur das erste Bild (durch den manuellen download() Aufruf in main()) angezeigt und danach nicht mehr aktualisiert. mit usleep(10000) klappt alles.

Mir gefällt der gesamte Konstrukt nicht, da ich jedes mal die image Struktur und den container zerstöre und neu aufbaue. Habe aber keine Funktionen gefunden, wie man bei Änderungen an einem pixbuf dessen Darstellung aktualisieren kann.

Versuche mit gtk_widget_queue_draw() oder gdk_window_invalidate_rect() haben auch keine Verbesserung ergeben :-(

P.S.: Mit dem abfangen von möglichen Fehlerrückgaben hast du natürlich Recht.
Wollte den Test-Quellcode so übersichtlich wie möglich halten und habe alles "überflüssige" entfernt.

Hier der modifizierte Quellcode:

Code: Alles auswählen

// gcc ReloadTest.c `pkg-config gtk+-3.0 --cflags` `pkg-config gtk+-3.0 --libs` -ljpeg -o ReloadTest

#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
// #include <gdk/gdk.h>

struct geo_data {
	GdkPixbuf *pixbuf;
	GtkWidget *image;
	GtkWidget *window;
};

void on_destroy (GtkWidget *widget G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
	gtk_main_quit();
}

int download(struct geo_data *geo)
{
	static GMutex mutex;
	static int flag=0;
//	GdkWindow *gdkwin;
//	GdkRectangle gdkrect;
//	int width, height;

	flag=!flag;
	g_mutex_lock(&mutex);
	// Platzhalter für Download-Routine
	usleep(25000);		// BUG : If delay > timeout, window content isn't updated
	if(geo->pixbuf==NULL) {
		puts("*** NEW PIXBUF ***");
		geo->pixbuf=gdk_pixbuf_new_from_file("DUMMY.jpg",NULL);
	}
	if(geo->image!=NULL) {
		printf("*** UPDATE IMAGE %d ***\n",flag);
		gtk_widget_destroy(geo->image);
		geo->pixbuf=gdk_pixbuf_new_from_file(flag?"dummy.jpg":"DUMMY.jpg",NULL);
	}
	geo->image=gtk_image_new_from_pixbuf(geo->pixbuf);
	gtk_container_add(GTK_CONTAINER (geo->window), geo->image);
	gtk_widget_show_all(geo->window);

//	gtk_widget_queue_draw(geo->window);

/*	gdkwin=gtk_widget_get_window(geo->window);
	width=gdk_pixbuf_get_width(geo->pixbuf);
	height=gdk_pixbuf_get_height(geo->pixbuf);
	gdkrect.x=0;
	gdkrect.y=0;
	gdkrect.width=width;
	gdkrect.width=height;
	gdk_window_invalidate_rect(gdkwin,&gdkrect,TRUE);
*/

	g_mutex_unlock(&mutex);
	return 1;
}

int main(int argc, char *argv[])
{
	struct geo_data geo;
	memset(&geo,0,sizeof(struct geo_data));
	gtk_init (&argc, &argv);
	geo.window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
	g_signal_connect (geo.window, "destroy", G_CALLBACK(on_destroy), NULL);
	g_signal_connect (geo.window, "close", G_CALLBACK(on_destroy), NULL);
	// Einmal manuell aufrufen vor gtk_main();
	download(&geo);
	g_timeout_add(200,(GSourceFunc)download,(gpointer)&geo);
	//gtk_widget_show_all(geo.window);
	puts("*** GTK_MAIN START ***");
	gtk_main();
	return 0;
}

Antworten