📄 gtk-faq.sgml
字号:
}</programlisting><para>Callbacks require a bit of attention. Callbacks fromGTK+ (signals) are made within the GTK+ lock. Howevercallbacks from GLib (timeouts, IO callbacks, and idlefunctions) are made outside of the GTK+ lock. So, within asignal handler you do not need to call gdk_threads_enter(),but within the other types of callbacks, you do.</para><para>Erik Mouw contributed the following code example toillustrate how to use threads within GTK+ programs.</para><programlisting role="C">/*------------------------------------------------------------------------- * Filename: gtk-thread.c * Version: 1.99.1 * Copyright: Copyright (C) 1999, Erik Mouw * Author: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Description: GTK threads example. * Created at: Sun Oct 17 21:27:09 1999 * Modified by: Owen Taylor <otaylor@gtk.org> * Modified at: Wed May 28 10:43:00 2003 *-----------------------------------------------------------------------*//* * Compile with: * * cc -o gtk-thread gtk-thread.c `pkg-config --cflags --libs gtk+-2.0 gthread-2.0` * * Thanks to Sebastian Wilhelmi for pointing out some bugs in earlier versions. * */#include <unistd.h>#include <gtk/gtk.h>#define YES_IT_IS (1)#define NO_IT_IS_NOT (0)typedef struct{ GtkWidget *label; int what;} yes_or_no_args;G_LOCK_DEFINE_STATIC (yes_or_no);static volatile int yes_or_no = YES_IT_IS;void destroy(GtkWidget *widget, gpointer data){ gtk_main_quit();}void *argument_thread(void *args){ yes_or_no_args *data = (yes_or_no_args *)args; gboolean say_something; for(;;) { /* sleep a while */ sleep(g_random_int_range (1, 4)); /* lock the yes_or_no_variable */ G_LOCK(yes_or_no); /* do we have to say something? */ say_something = (yes_or_no != data->what); if(say_something) { /* set the variable */ yes_or_no = data->what; } /* Unlock the yes_or_no variable */ G_UNLOCK(yes_or_no); if(say_something) { /* get GTK thread lock */ gdk_threads_enter(); /* set label text */ if(data->what == YES_IT_IS) gtk_label_set_text(GTK_LABEL(data->label), "O yes, it is!"); else gtk_label_set_text(GTK_LABEL(data->label), "O no, it isn't!"); /* Make sure all X commands are sent to the X server; not strictly * necessary here, but always a good idea when you do anything * from a thread other than the one where the main loop is running. */ gdk_flush (); /* release GTK thread lock */ gdk_threads_leave(); } } return NULL;}int main(int argc, char *argv[]){ GtkWidget *window; GtkWidget *label; GError *error = NULL; yes_or_no_args yes_args, no_args; /* init threads */ g_thread_init(NULL); gdk_threads_init(); /* init gtk */ gtk_init(&argc, &argv); /* create a window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(window, "destroy", G_CALLBACK(destroy), NULL); gtk_container_set_border_width(GTK_CONTAINER (window), 10); /* create a label */ label = gtk_label_new("And now for something completely different ..."); gtk_container_add(GTK_CONTAINER(window), label); /* show everything */ gtk_widget_show(label); gtk_widget_show (window); /* create the threads */ yes_args.label = label; yes_args.what = YES_IT_IS; if (!g_thread_create(argument_thread, &yes_args, FALSE, &error)) { g_printerr ("Failed to create YES thread: %s\n", error->message); return 1; } no_args.label = label; no_args.what = NO_IT_IS_NOT; if (!g_thread_create(argument_thread, &no_args, FALSE, &error)) { g_printerr ("Failed to create NO thread: %s\n", error->message); return 1; } /* enter the GTK main loop */ gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return 0;}</programlisting></sect1><!-- ----------------------------------------------------------------- --><sect1><title>I'm doing some stuff with GTK+ in a separate thread, andproperly locking with gdk_threads_enter/gdk_threads_leave()but the display doesn't update properly. <emphasis>[GTK 2.x]</emphasis></title><para>For efficiency, the X window system batches up commandsand sends them to the X server in batches instead of sendingout immediately.</para><para>In a non-multithreaded program, you don't have to worry aboutthis, since the first thing that happens when control returnsto the main loop is that any outstanding X requests are sent to the X server.</para><para>However, if you are making GTK+ calls from a thread otherthan the main loop, then GTK+ doesn't know when to send batchedcommands out. For that reason, after making GTK+ calls in a separate thread, it is usually a good idea to callgdk_flush() before gdk_thread_leave().</para><para>Actually, gdk_flush() is more expensive than is necessary here,since it waits for the X server to finish outstanding commandsas well; if performance is an issue, you may want to callXFlush() directly:</para><programlisting role="C">#include <gdk/gdkx.h>void my_flush_commands (void){ GdkDisplay *display = gdk_display_get_default (); XFlush (GDK_DISPLAY_XDISPLAY (display);}</programlisting></sect1><!-- ----------------------------------------------------------------- --><sect1><title>What's an easy way to run a function in the thread withthe main loop? <emphasis>[GTK 2.x]</emphasis></title><para>Sometimes the simplest way to set up a threaded programis to make all the GTK+ calls in a single thread. In sucha program, you should still call g_threads_init(), butdon't need to call gdk_threads_init(), gkd_threads_enter(),and gdk_threads_leave().</para><para>If you set your program up this way, how then do you getthe thread making GTK+ calls and running the main loopto do something in response to another thread?</para><para>An easy way to do it is to take advantage of the fact thatthe GLib main loop functions are all thread safe, and canbe called from any thread by adding an idle functionwith g_idle_add(). The function provided will be calledat the next opportunity by the main thread. If you wantyour function to take priority over event handling anddrawing, you can instead use g_idle_add_full() and passin a priority of G_PRIORITY_HIGH.</para></sect1><!-- ----------------------------------------------------------------- --><sect1><title>Why does this strange 'x io error' occur when I<literal>fork()</literal> in my GTK+ app? <emphasis>[GTK 2.x]</emphasis></title><para>This is not really a GTK+ problem, and the problem isnot related to <literal>fork()</literal> either. If the 'x ioerror' occurs then you probably use the <literal>exit()</literal> functionin order to exit from the child process.</para><para>When GDK opens an X display, it creates a socket filedescriptor. When you use the <literal>exit()</literal>function, you implicitly close all the open file descriptors,and the underlying X library really doesn't like this.</para><para>The right function to use here is<literal>_exit()</literal>.</para> <para>Erik Mouw contributed the following code example toillustrate handling fork() and exit().</para><programlisting role="C">/*------------------------------------------------------------------------- * Filename: gtk-fork.c * Version: 0.99.2 * Copyright: Copyright (C) 1999, Erik Mouw * Author: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Description: GTK+ fork example * Created at: Thu Sep 23 21:37:55 1999 * Modified by: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Modified at: Thu Sep 23 22:39:39 1999 * Modified by: Tony Gale <gale@gtk.org> * Modified at: Wed Jan 14 12:38:00 2004 *-----------------------------------------------------------------------*//* * Compile with: * * cc -o gtk-fork gtk-fork.c `pkg-config gtk+-2.0 --cflags --libs` * */#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include <gtk/gtk.h>void sigchld_handler(int num){ sigset_t set, oldset; pid_t pid; int status, exitstatus; /* block other incoming SIGCHLD signals */ sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, &oldset); /* wait for child */ while((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) { if(WIFEXITED(status)) { exitstatus = WEXITSTATUS(status); fprintf(stderr, "Parent: child exited, pid = %d, exit status = %d\n", (int)pid, exitstatus); } else if(WIFSIGNALED(status)) { exitstatus = WTERMSIG(status); fprintf(stderr, "Parent: child terminated by signal %d, pid = %d\n", exitstatus, (int)pid); } else if(WIFSTOPPED(status)) { exitstatus = WSTOPSIG(status); fprintf(stderr, "Parent: child stopped by signal %d, pid = %d\n", exitstatus, (int)pid); } else { fprintf(stderr, "Parent: child exited magically, pid = %d\n", (int)pid); } } /* re-install the signal handler (some systems need this) */ signal(SIGCHLD, sigchld_handler); /* and unblock it */ sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_UNBLOCK, &set, &oldset);}gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data){ return(FALSE);}void destroy(GtkWidget *widget, gpointer data){ gtk_main_quit();}void fork_me(GtkWidget *widget, gpointer data){ pid_t pid; pid = fork(); if(pid == -1) { /* ouch, fork() failed */ perror("fork"); exit(-1); } else if(pid == 0) { /* child */ fprintf(stderr, "Child: pid = %d\n", (int)getpid()); execlp("ls", "ls", "-CF", "/", NULL); /* if exec() returns, there is something wrong */ perror("execlp"); /* exit child. note the use of _exit() instead of exit() */ _exit(-1); } else { /* parent */ fprintf(stderr, "Parent: forked a child with pid = %d\n", (int)pid); }}int main(int argc, char *argv[]){ GtkWidget *window; GtkWidget *button; gtk_init(&argc, &argv); /* the basic stuff: make a window and set callbacks for destroy and * delete events */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(G_OBJECT (window), "delete_event", G_CALLBACK(delete_event), NULL); g_signal_connect(G_OBJECT (window), "destroy", G_CALLBACK(destroy), NULL);#if (GTK_MAJOR_VERSION == 1) && (GTK_MINOR_VERSION == 0) gtk_container_border_width(GTK_CONTAINER (window), 10);#else gtk_container_set_border_width(GTK_CONTAINER (window), 10);#endif /* add a button to do something usefull */ button = gtk_button_new_with_label("Fork me!"); g_signal_connect(G_OBJECT (button), "clicked", G_CALLBACK(fork_me), NULL); gtk_container_add(GTK_CONTAINER(window), button); /* show everything */ gtk_widget_show (button); gtk_widget_show (window); /* install a signal handler for SIGCHLD signals */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -