第一件要做的是當然是取得一份GTK的原始碼並且安裝進您的系統中. 您可以從GIMP取得一份發行版, 或者是從Peter Mattis's的" 家中" ftp.xcf.berkely.edu/pub/pmattis(however, it has been changed to ftp.gimp.org)取得一份. GTK使用GNU的autoconf來設定. 一但您解開檔案 , 輸入configure --help來看看選項表列.
下一個函數是用來設定container物件的屬性. This just sets the window so it has a blank area along the inside of it 10 pixels wide where no widgets will go. There are other similar functions which we will look at in the section on Setting Widget Attributes
And again, GTK_CONTAINER is a macro to perform type casting. gtk_container_border_width (GTK_CONTAINER (window), 10);
#include /* 新改進的callback.輸入到該函數的資料被輸出到. */ void callback (GtkWidget *widget, gpointer *data) { g_print ("Hello again - %s was pressed ", (char *) data); } /* another callback */ void destroy (GtkWidget *widget, gpointer *data) { gtk_main_quit (); } int main (int argc, char *argv[]) { /* GtkWidget is the storage type for widgets */ GtkWidget *window; GtkWidget *button; GtkWidget *box1; /* this is called in all GTK applications.arguments are parsed from * the command line and are returned to the application. */ gtk_init (&argc, &argv); /* create a new window */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); /* 這是個新函數, 它設定title到新視窗上"Hello Buttons!" */ gtk_window_set_title (GTK_WINDOW (window), "Hello Buttons!"); /* 用這樣會比較簡單一點. */ gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (destroy), NULL); /* 設定邊框寬度. */ gtk_container_border_width (GTK_CONTAINER (window), 10); /* 我們產生一個box來封裝物件.這一點會在"packing"詳述. 這個box實際上看不見, 它只是用來當成是個工具來安排物件 */ box1 = gtk_hbox_new(FALSE, 0); /* 將box放到主視窗中. */ gtk_container_add (GTK_CONTAINER (window), box1); /* 產生一個新按鈕並帶有標籤"Button 1". */ button = gtk_button_new_with_label ("Button 1"); /* 當按鈕被按下的時候, 我們呼叫"callback"函數 * 並以其指標做為參數送到"button 1" */ gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (callback), (gpointer) "button 1"); /* instead of gtk_container_add, we pack this button into the invisible * box, which has been packed into the window. */ gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0); /* always remember this step, this tells GTK that our preparation for * this button is complete, and it can be displayed now. */ gtk_widget_show(button); /* do these same steps again to create a second button */ button = gtk_button_new_with_label ("Button 2"); /* call the same callback function with a different argument, * passing a pointer to "button 2" instead. */ gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (callback), (gpointer) "button 2"); gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0); /* The order in which we show the buttons is not really important, but I * recommend showing the window last, so it all pops up at once. */ gtk_widget_show(button); gtk_widget_show(box1); gtk_widget_show (window); /* rest in gtk_main and wait for the fun to begin! */ gtk_main (); return 0; }
#include "gtk/gtk.h" void destroy (GtkWidget *widget, gpointer *data) { gtk_main_quit (); } /* Make a new hbox filled with button-labels. Arguments for the * variables we're interested are passed in to this function. * We do not show the box, but do show everything inside. */ GtkWidget *make_box (gint homogeneous, gint spacing, gint expand, gint fill, gint padding) { GtkWidget *box; GtkWidget *button; char padstr[80]; /* create a new hbox with the appropriate homogeneous and spacing * settings */ box = gtk_hbox_new (homogeneous, spacing); /* create a series of buttons with the appropriate settings */ button = gtk_button_new_with_label ("gtk_box_pack"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); button = gtk_button_new_with_label ("(box,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); button = gtk_button_new_with_label ("button,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); /* create a button with the label depending on the value of * expand. */ if (expand == TRUE) button = gtk_button_new_with_label ("TRUE,"); else button = gtk_button_new_with_label ("FALSE,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); /* This is the same as the button creation for "expand" * above, but uses the shorthand form. */ button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,"); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); sprintf (padstr, "%d);", padding); button = gtk_button_new_with_label (padstr); gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding); gtk_widget_show (button); return box; } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; GtkWidget *box1; GtkWidget *box2; GtkWidget *separator; GtkWidget *label; GtkWidget *quitbox; int which; /* Our init, don't forget this! :) */ gtk_init (&argc, &argv); if (argc != 2) { fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3. "); /* this just does cleanup in GTK, and exits with an exit status of 1. */ gtk_exit (1); } which = atoi (argv[1]); /* Create our window */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); /* You should always remember to connect the destroy signal to the * main window.This is very important for proper intuitive * behavior */ gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (destroy), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); /* We create a vertical box (vbox) to pack the horizontal boxes into. * This allows us to stack the horizontal boxes filled with buttons one * on top of the other in this vbox. */ box1 = gtk_vbox_new (FALSE, 0); /* which example to show.These correspond to the pictures above. */ switch (which) { case 1: /* create a new label. */ label = gtk_label_new ("gtk_hbox_new (FALSE, 0);"); /* Align the label to the left side.We'll discuss this function and * others in the section on Widget Attributes. */ gtk_misc_set_alignment (GTK_MISC (label), 0, 0); /* Pack the label into the vertical box (vbox box1).Remember that * widgets added to a vbox will be packed one on top of the other in * order. */ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); /* show the label */ gtk_widget_show (label); /* call our make box function - homogeneous = FALSE, spacing = 0, * expand = FALSE, fill = FALSE, padding = 0 */ box2 = make_box (FALSE, 0, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* call our make box function - homogeneous = FALSE, spacing = 0, * expand = FALSE, fill = FALSE, padding = 0 */ box2 = make_box (FALSE, 0, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Args are: homogeneous, spacing, expand, fill, padding */ box2 = make_box (FALSE, 0, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* creates a separator, we'll learn more about these later, * but they are quite simple. */ separator = gtk_hseparator_new (); /* pack the separator into the vbox.Remember each of these * widgets are being packed into a vbox, so they'll be stacked * vertically. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); /* create another new label, and show it. */ label = gtk_label_new ("gtk_hbox_new (TRUE, 0);"); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); gtk_widget_show (label); /* Args are: homogeneous, spacing, expand, fill, padding */ box2 = make_box (TRUE, 0, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Args are: homogeneous, spacing, expand, fill, padding */ box2 = make_box (TRUE, 0, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* another new separator. */ separator = gtk_hseparator_new (); /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); break; case 2: /* create a new label, remember box1 is a vbox as created * near the beginning of main() */ label = gtk_label_new ("gtk_hbox_new (FALSE, 10);"); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); gtk_widget_show (label); /* Args are: homogeneous, spacing, expand, fill, padding */ box2 = make_box (FALSE, 10, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Args are: homogeneous, spacing, expand, fill, padding */ box2 = make_box (FALSE, 10, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); separator = gtk_hseparator_new (); /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); label = gtk_label_new ("gtk_hbox_new (FALSE, 0);"); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0); gtk_widget_show (label); /* Args are: homogeneous, spacing, expand, fill, padding */ box2 = make_box (FALSE, 0, TRUE, FALSE, 10); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* Args are: homogeneous, spacing, expand, fill, padding */ box2 = make_box (FALSE, 0, TRUE, TRUE, 10); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); separator = gtk_hseparator_new (); /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); break; case 3: /* This demonstrates the ability to use gtk_box_pack_end() to * right justify widgets.First, we create a new box as before. */ box2 = make_box (FALSE, 0, FALSE, FALSE, 0); /* create the label that will be put at the end. */ label = gtk_label_new ("end"); /* pack it using gtk_box_pack_end(), so it is put on the right side * of the hbox created in the make_box() call. */ gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0); /* show the label. */ gtk_widget_show (label); /* pack box2 into box1 (the vbox remember ? :) */ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* a separator for the bottom. */ separator = gtk_hseparator_new (); /* this explicitly sets the separator to 400 pixels wide by 5 pixels * high.This is so the hbox we created will also be 400 pixels wide, * and the "end" label will be separated from the other labels in the * hbox.Otherwise, all the widgets in the hbox would be packed as * close together as possible. */ gtk_widget_set_usize (separator, 400, 5); /* pack the separator into the vbox (box1) created near the start * of main() */ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5); gtk_widget_show (separator); } /* Create another new hbox.. remember we can use as many as we need! */ quitbox = gtk_hbox_new (FALSE, 0); /* Our quit button. */ button = gtk_button_new_with_label ("Quit"); /* setup the signal to destroy the window.Remember that this will send * the "destroy" signal to the window which will be caught by our signal * handler as defined above. */ gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); /* pack the button into the quitbox. * The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */ gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0); /* pack the quitbox into the vbox (box1) */ gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0); /* pack the vbox (box1) which now contains all our widgets, into the * main window. */ gtk_container_add (GTK_CONTAINER (window), box1); /* and show everything left */ gtk_widget_show (button); gtk_widget_show (quitbox); gtk_widget_show (box1); /* Showing the window last so everything pops up at once. */ gtk_widget_show (window); /* And of course, our main function. */ gtk_main (); /* control returns here when gtk_main_quit() is called, but not when * gtk_exit is used. */ return 0; }
#include /* create a new hbox with an image and a label packed into it * and return the box.. */ GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text) { GtkWidget *box1; GtkWidget *label; GtkWidget *pixmapwid; GdkPixmap *pixmap; GdkBitmap *mask; GtkStyle *style; /* create box for xpm and label */ box1 = gtk_hbox_new (FALSE, 0); gtk_container_border_width (GTK_CONTAINER (box1), 2); /* get style of button.. I assume it's to get the background color. * if someone knows the real reason, please enlighten me. */ style = gtk_widget_get_style(parent); /* now on to the xpm stuff.. load xpm */ pixmap = gdk_pixmap_create_from_xpm (parent->window, &mask, &style->bg[GTK_STATE_NORMAL], xpm_filename); pixmapwid = gtk_pixmap_new (pixmap, mask); /* create label for button */ label = gtk_label_new (label_text); /* pack the pixmap and label into the box */ gtk_box_pack_start (GTK_BOX (box1), pixmapwid, FALSE, FALSE, 3); gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3); gtk_widget_show(pixmapwid); gtk_widget_show(label); return (box1); } /* our usual callback function */ void callback (GtkWidget *widget, gpointer *data) { g_print ("Hello again - %s was pressed ", (char *) data); } int main (int argc, char *argv[]) { /* GtkWidget is the storage type for widgets */ GtkWidget *window; GtkWidget *button; GtkWidget *box1; gtk_init (&argc, &argv); /* create a new window */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!"); /* It's a good idea to do this for all windows. */ gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_exit), NULL); /* sets the border width of the window. */ gtk_container_border_width (GTK_CONTAINER (window), 10); /* create a new button */ button = gtk_button_new (); /* You should be getting used to seeing most of these functions by now */ gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (callback), (gpointer) "cool button"); /* this calls our box creating function */ box1 = xpm_label_box(window, "info.xpm", "cool button"); /* pack and show all our widgets */ gtk_widget_show(box1); gtk_container_add (GTK_CONTAINER (button), box1); gtk_widget_show(button); gtk_container_add (GTK_CONTAINER (window), button); gtk_widget_show (window); /* rest in gtk_main and wait for the fun to begin! */ gtk_main (); return 0; }
#include void destroy(GtkWidget *widget, gpointer *data) { gtk_main_quit(); } int main (int argc, char *argv[]) { static GtkWidget *window; GtkWidget *scrolled_window; GtkWidget *table; GtkWidget *button; char buffer[32]; int i, j; gtk_init (&argc, &argv); /* Create a new dialog window for the scrolled window to be * packed into.A dialog is just like a normal window except it has a * vbox and a horizontal seperator packed into it.It's just a shortcut * for creating dialogs */ window = gtk_dialog_new (); gtk_signal_connect (GTK_OBJECT (window), "destroy", (GtkSignalFunc) destroy, NULL); gtk_window_set_title (GTK_WINDOW (window), "dialog"); gtk_container_border_width (GTK_CONTAINER (window), 0); /* create a new scrolled window. */ scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10); /* the policy is one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS. * GTK_POLICY_AUTOMATIC will automatically decide whether you need * scrollbars, wheras GTK_POLICY_ALWAYS will always leave the scrollbars * there.The first one is the horizontal scrollbar, the second, * the vertical. */ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); /* The dialog window is created with a vbox packed into it. */ gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window, TRUE, TRUE, 0); gtk_widget_show (scrolled_window); /* create a table of 10 by 10 squares. */ table = gtk_table_new (10, 10, FALSE); /* set the spacing to 10 on x and 10 on y */ gtk_table_set_row_spacings (GTK_TABLE (table), 10); gtk_table_set_col_spacings (GTK_TABLE (table), 10); /* pack the table into the scrolled window */ gtk_container_add (GTK_CONTAINER (scrolled_window), table); gtk_widget_show (table); /* this simply creates a grid of toggle buttons on the table * to demonstrate the scrolled window. */ for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) { sprintf (buffer, "button (%d,%d) ", i, j); button = gtk_toggle_button_new_with_label (buffer); gtk_table_attach_defaults (GTK_TABLE (table), button, i, i+1, j, j+1); gtk_widget_show (button); } /* Add a "close" button to the bottom of the dialog */ button = gtk_button_new_with_label ("close"); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (window)); /* this makes it so the button is the default. */ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0); /* This grabs this button to be the default button. Simply hitting * the "Enter" key will cause this button to activate. */ gtk_widget_grab_default (button); gtk_widget_show (button); gtk_widget_show (window); gtk_main(); return(0); }
Progress Bars一般與timeouts及其它函數一起使用, (see section on Timeouts, I/O and Idle Functions) 這是因為多工的考量. gtk_progress_bar_update會處理這方面的事務.
這裡是使用Progress Bar的範例, 並用timeouts來更新. 同時也會展示如何重設Progress Bar.
#include static int ptimer = 0; int pstat = TRUE; /* This function increments and updates the progress bar, it also resets the progress bar if pstat is FALSE */ gint progress (gpointer data) { gfloat pvalue; /* get the current value of the progress bar */ pvalue = GTK_PROGRESS_BAR (data)->percentage; if ((pvalue >= 1.0)' '(pstat == FALSE)) { pvalue = 0.0; pstat = TRUE; } pvalue += 0.01; gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue); return TRUE; } /* This function signals a reset of the progress bar */ void progress_r (void) { pstat = FALSE; } void destroy (GtkWidget *widget, gpointer *data) { gtk_main_quit (); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; GtkWidget *label; GtkWidget *table; GtkWidget *pbar; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (destroy), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); table = gtk_table_new(3,2,TRUE); gtk_container_add (GTK_CONTAINER (window), table); label = gtk_label_new ("Progress Bar Example"); gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1); gtk_widget_show(label); /* Create a new progress bar, pack it into the table, and show it */ pbar = gtk_progress_bar_new (); gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2); gtk_widget_show (pbar); /* Set the timeout to handle automatic updating of the progress bar */ ptimer = gtk_timeout_add (100, progress, pbar); /* This button signals the progress bar to be reset */ button = gtk_button_new_with_label ("Reset"); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (progress_r), NULL); gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3); gtk_widget_show(button); button = gtk_button_new_with_label ("Cancel"); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (destroy), NULL); gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3); gtk_widget_show (button); gtk_widget_show(table); gtk_widget_show(window); gtk_main (); return 0; }
/* compile this program with: * $ gcc -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c */ /* include the gtk+ header files * include stdio.h, we need that for the printf() function */ #include #include /* this is our data identification string to store * data in list items */ constgchar*list_item_data_key="list_item_data"; /* prototypes for signal handler that we are going to connect * to the GtkList widget */ staticvoidsigh_print_selection(GtkWidget*gtklist, gpointerfunc_data); staticvoidsigh_button_event(GtkWidget*gtklist, GdkEventButton *event, GtkWidget*frame); /* main function to set up the user interface */ gint main (int argc, gchar *argv[]) { GtkWidget*separator; GtkWidget*window; GtkWidget*vbox; GtkWidget*scrolled_window; GtkWidget*frame; GtkWidget*gtklist; GtkWidget*button; GtkWidget*list_item; GList*dlist; guinti; gcharbuffer[64]; /* initialize gtk+ (and subsequently gdk) */ gtk_init(&argc, &argv); /* create a window to put all the widgets in * connect gtk_main_quit() to the "destroy" event of * the window to handle window manager close-window-events */ window=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "GtkList Example"); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); /* inside the window we need a box to arrange the widgets * vertically */ vbox=gtk_vbox_new(FALSE, 5); gtk_container_border_width(GTK_CONTAINER(vbox), 5); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show(vbox); /* this is the scolled window to put the GtkList widget inside */ scrolled_window=gtk_scrolled_window_new(NULL, NULL); gtk_widget_set_usize(scrolled_window, 250, 150); gtk_container_add(GTK_CONTAINER(vbox), scrolled_window); gtk_widget_show(scrolled_window); /* create the GtkList widget * connect the sigh_print_selection() signal handler * function to the "selection_changed" signal of the GtkList * to print out the selected items each time the selection * has changed */ gtklist=gtk_list_new(); gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist); gtk_widget_show(gtklist); gtk_signal_connect(GTK_OBJECT(gtklist), "selection_changed", GTK_SIGNAL_FUNC(sigh_print_selection), NULL); /* we create a "Prison" to put a list item in ;) */ frame=gtk_frame_new("Prison"); gtk_widget_set_usize(frame, 200, 50); gtk_container_border_width(GTK_CONTAINER(frame), 5); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); gtk_container_add(GTK_CONTAINER(vbox), frame); gtk_widget_show(frame); /* connect the sigh_button_event() signal handler to the GtkList * wich will handle the "arresting" of list items */ gtk_signal_connect(GTK_OBJECT(gtklist), "button_release_event", GTK_SIGNAL_FUNC(sigh_button_event), frame); /* create a separator */ separator=gtk_hseparator_new(); gtk_container_add(GTK_CONTAINER(vbox), separator); gtk_widget_show(separator); /* finaly create a button and connect it愀 "clicked" signal * to the destroyment of the window */ button=gtk_button_new_with_label("Close"); gtk_container_add(GTK_CONTAINER(vbox), button); gtk_widget_show(button); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); /* now we create 5 list items, each having it愀 own * label and add them to the GtkList using gtk_container_add() * also we query the text string from the label and * associate it with the list_item_data_key for each list item */ for (i=0; i<5; i++) { GtkWidget*label; gchar*string; sprintf(buffer, "ListItemContainer with Label #%d", i); label=gtk_label_new(buffer); list_item=gtk_list_item_new(); gtk_container_add(GTK_CONTAINER(list_item), label); gtk_widget_show(label); gtk_container_add(GTK_CONTAINER(gtklist), list_item); gtk_widget_show(list_item); gtk_label_get(GTK_LABEL(label), &string); gtk_object_set_data(GTK_OBJECT(list_item), list_item_data_key, string); } /* here, we are creating another 5 labels, this time * we use gtk_list_item_new_with_label() for the creation * we can憩 query the text string from the label because * we don憩 have the labels pointer and therefore * we just associate the list_item_data_key of each * list item with the same text string * for adding of the list items we put them all into a doubly * linked list (GList), and then add them by a single call to * gtk_list_append_items() * because we use g_list_prepend() to put the items into the * doubly linked list, their order will be descending (instead * of ascending when using g_list_append()) */ dlist=NULL; for (; i<10; i++) { sprintf(buffer, "List Item with Label %d", i); list_item=gtk_list_item_new_with_label(buffer); dlist=g_list_prepend(dlist, list_item); gtk_widget_show(list_item); gtk_object_set_data(GTK_OBJECT(list_item), list_item_data_key, "ListItem with integrated Label"); } gtk_list_append_items(GTK_LIST(gtklist), dlist); /* finaly we want to see the window, don憩 we? ;) */ gtk_widget_show(window); /* fire up the main event loop of gtk */ gtk_main(); /* we get here after gtk_main_quit() has been called which * happens if the main window gets destroyed */ return 0; } /* this is the signal handler that got connected to button * press/release events of the GtkList */ void sigh_button_event(GtkWidget*gtklist, GdkEventButton *event, GtkWidget*frame) { /* we only do something if the third (rightmost mouse button * was released */ if (event->type==GDK_BUTTON_RELEASE && event->button==3) { GList*dlist, *free_list; GtkWidget*new_prisoner; /* fetch the currently selected list item which * will be our next prisoner ;) */ dlist=GTK_LIST(gtklist)->selection; if (dlist) new_prisoner=GTK_WIDGET(dlist->data); else new_prisoner=NULL; /* look for already prisoned list items, we * will put them back into the list * remember to free the doubly linked list that * gtk_container_children() returns */ dlist=gtk_container_children(GTK_CONTAINER(frame)); free_list=dlist; while (dlist) { GtkWidget*list_item; list_item=dlist->data; gtk_widget_reparent(list_item, gtklist); dlist=dlist->next; } g_list_free(free_list); /* if we have a new prisoner, remove him from the * GtkList and put him into the frame "Prison" * we need to unselect the item before */ if (new_prisoner) { GListstatic_dlist; static_dlist.data=new_prisoner; static_dlist.next=NULL; static_dlist.prev=NULL; gtk_list_unselect_child(GTK_LIST(gtklist), new_prisoner); gtk_widget_reparent(new_prisoner, frame); } } } /* this is the signal handler that gets called if GtkList * emits the "selection_changed" signal */ void sigh_print_selection(GtkWidget*gtklist, gpointerfunc_data) { GList*dlist; /* fetch the doubly linked list of selected items * of the GtkList, remember to treat this as read-only! */ dlist=GTK_LIST(gtklist)->selection; /* if there are no selected items there is nothing more * to do than just telling the user so */ if (!dlist) { g_print("Selection cleared "); return; } /* ok, we got a selection and so we print it */ g_print("The selection is a "); /* get the list item from the doubly linked list * and then query the data associated with list_item_data_key * we then just print it */ while (dlist) { GtkObject*list_item; gchar*item_data_string; list_item=GTK_OBJECT(dlist->data); item_data_string=gtk_object_get_data(list_item, list_item_data_key); g_print("%s ", item_data_string); dlist=dlist->next; } g_print(" "); }
If you got stuck here, it's mostly not your problem. Don't feel frustration. The List Widget itself is pretty complicated. You may drop me a word if you need. I will try to improve it.
13. Undocumented Widgets
These all require authors! :) Please consider contributing to our tutorial.
If you must use one of these widgets that are undocumented, I strongly suggest you take a look at their respective header files in the GTK distro. GTK's function names are very descriptive. Once you have an understanding of how things work, it's not easy to figure out how to use a widget simply by looking at it's function declarations. This, along with a few examples from others' code, and it should be no problem.
When you do come to understand all the functions of a new undocumented widget, please consider writing a tutorial on it so others may benifit from your time.
13.1 Text Entries
13.2 Color Selections
13.3 Range Controls
13.4 Rulers
13.5 Text Boxes
13.6 Previews
(This may need to be rewritten to follow the style of the rest of the tutorial)
Previews serve a number of purposes in GIMP/GTK. The most important one is this. High quality images may take up to tens of megabytes of memory - easy! Any operation on an image that big is bound to take a long time. If it takes you 5-10 trial-and-errors (i.e. 10-20 steps, since you have to revert after you make an error) to choose the desired modification, it make take you literally hours to make the right one - if you don't run out of memory first. People who have spent hours in color darkrooms know the feeling.Previews to the rescue!
But the annoyance of the delay is not the only issue. Oftentimes it is helpful to compare the Before and After versions side-by-side or at least back-to-back. If you're working with big images and 10 second delays, obtaining the Before and After impressions is, to say the least, difficult. For 30M images (4"x6", 600dpi, 24 bit) the side-by-side comparison is right out for most people, while back-to-back is more like back-to-1001, 1002, ..., 1010-back! Previews to the rescue!
But there's more. Previews allow for side-by-side pre- previews. In other words, you write a plug-in (e.g. the filterpack simulation) which would have a number of here's-what-it-would-look-like-if-you-were-to-do-this previews.An approach like this acts as a sort of a preview palette and is very effective fow subtle changes. Let's go previews!
There's more. For certain plug-ins real-time image- specific human intervention maybe necessary. In the SuperNova plug-in, for example, the user is asked to enter the coordinates of the center of the future supernova. The easiest way to do this, really, is to present the user with a preview and ask him to intereactively select the spot. Let's go previews!
Finally, a couple of misc uses. One can use previews even when not working with big images. For example, they are useful when rendering compicated patterns. (Just check out the venerable Diffraction plug-in + many other ones!) As another example, take a look at the colormap rotation plug-in (work in progress). You can also use previews for little logo's inside you plug-ins and even for an image of yourself, The Author. Let's go previews!
When Not to Use Previews
Don't use previews for graphs, drawing etc. GDK is much faster for that. Use previews only for rendered images!
Let's go previews!
You can stick a preview into just about anything. In a vbox, an hbox, a table, a button, etc. But they look their best in tight frames around them. Previews by themselves do not have borders and look flat without them. (Of course, if the flat look is what you want...) Tight frames provide the necessary borders.
[Image][Image]
Previews in many ways are like any other widgets in GTK (whatever that means) except they possess an addtional feature: they need to be filled with some sort of an image! First, we will deal exclusively with the GTK aspect of previews and then we'll discuss how to fill them.
GtkWidget *preview! Without any ado: /* Create a preview widget, set its size, an show it */ GtkWidget *preview; preview=gtk_preview_new(GTK_PREVIEW_COLOR) /*Other option: GTK_PREVIEW_GRAYSCALE);*/ gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT); gtk_widget_show(preview); my_preview_rendering_function(preview); Oh yeah, like I said, previews look good inside frames, so how about: GtkWidget *create_a_preview(intWidth, intHeight, intColorfulness) { GtkWidget *preview; GtkWidget *frame; frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_border_width (GTK_CONTAINER(frame),0); gtk_widget_show(frame); preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR :GTK_PREVIEW_GRAYSCALE); gtk_preview_size (GTK_PREVIEW (preview), Width, Height); gtk_container_add(GTK_CONTAINER(frame),preview); gtk_widget_show(preview); my_preview_rendering_function(preview); return frame; }
That's my basic preview. This routine returns the "parent" frame so you can place it somewhere else in your interface. Of course, you can pass the parent frame to this routine as a parameter. In many situations, however, the contents of the preview are changed continually by your application. In this case you may want to pass a pointer to the preview to a "create_a_preview()" and thus have control of it later.
One more important note that may one day save you a lot of time. Sometimes it is desirable to label you preview. For example, you may label the preview containing the original image as "Original" and the one containing the modified image as "Less Original". It might occure to you to pack the preview along with the appropriate label into a vbox. The unexpected caveat is that if the label is wider than the preview (which may happen for a variety of reasons unforseeable to you, from the dynamic decision on the size of the preview to the size of the font) the frame expands and no longer fits tightly over the preview. The same problem can probably arise in other situations as well.
The solution is to place the preview and the label into a 2x1 table and by attaching them with the following paramters (this is one possible variations of course. The key is no GTK_FILL in the second attachment):
Making a preview clickable is achieved most easily by placing it in a button. It also adds a nice border around the preview and you may not even need to place it in a frame. See the Filter Pack Simulation plug-in for an example.
This is pretty much it as far as GTK is concerned.
Filling In a Preview
In order to familiarize ourselves with the basics of filling in previews, let's create the following pattern (contrived by trial and error):
void
my_preview_rendering_function(GtkWidget *preview) { #define SIZE 100 #define HALF (SIZE/2) guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */ gint i, j; /* Coordinates*/ double r, alpha, x, y; if (preview==NULL) return; /* I usually add this when I want */ /* to avoid silly crashes. You*/ /* should probably make sure that */ /* everything has been nicely */ /* initialized!*/ for (j=0; j < ABS(cos(2*alpha)) ) { /* Are we inside the shape?*/ /* glib.h contains ABS(x).*/ row[i*3+0] = sqrt(1-r)*255;/* Define Red*/ row[i*3+1] = 128;/* Define Green*/ row[i*3+2] = 224;/* Define Blue*/ }/* "+0" is for alignment!*/ else { row[i*3+0] = r*255; row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255; row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255; } } gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE); /* Insert "row" into "preview" starting at the point with*/ /* coordinates (0,j) first column, j_th row extending SIZE */ /* pixels to the right */ } free(row); /* save some space */ gtk_widget_draw(preview,NULL); /* what does this do? */ gdk_flush(); /* or this? */ }
Non-GIMP users can have probably seen enough to do a lot of things already.
For the GIMP users I have a few pointers to add.
Image Preview
It is probably wize to keep a reduced version of the image around with just enough pixels to fill the preview. This is done by selecting every n'th pixel where n is the ratio of the size of the image to the size of the preview. All further operations (including filling in the previews) are then performed on the reduced number of pixels only. The following is my implementation of reducing the image. (Keep in mind that I've had only basic C!)
(UNTESTED CODE ALERT!!!) typedef struct { gintwidth; gintheight; gintbbp; guchar*rgb; guchar*mask; } ReducedImage; enum { SELECTION_ONLY, SELCTION_IN_CONTEXT, ENTIRE_IMAGE }; ReducedImage *Reduce_The_Image(GDrawable *drawable, GDrawable *mask, gint LongerSize, gint Selection) { /* This function reduced the image down to the the selected preview size */ /* The preview size is determine by LongerSize, i.e. the greater of the*/ /* two dimentions. Works for RGB images only!*/ gint RH, RW;/* Reduced height and reduced width*/ gint width, height;/* Width and Height of the area being reduced*/ gint bytes=drawable->bpp; ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage)); guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B; gint i, j, whichcol, whichrow, x1, x2, y1, y2; GPixelRgn srcPR, srcMask; gint NoSelectionMade=TRUE; /* Assume that we're dealing with the entire*/ /* image. */ gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2); width= x2-x1; height = y2-y1; /* If there's a SELECTION, we got its bounds!) if (width != drawable->width && height != drawable->height) NoSelectionMade=FALSE; /* Become aware of whether the user has made an active selection*/ /* This will become important later, when creating a reduced mask. */
/* If we want to preview the entire image, overrule the above!*/ /* Of course, if no selection has been made, this does nothing! */ if (Selection==ENTIRE_IMAGE) { x1=0; x2=drawable->width; y1=0; y2=drawable->height; } /* If we want to preview a selection with some surronding area we */ /* have to expand it a little bit. Consider it a bit of a riddle. */ if (Selection==SELECTION_IN_CONTEXT) { x1=MAX(0,x1-width/2.0); x2=MIN(drawable->width,x2+width/2.0); y1=MAX(0,y1-height/2.0); y2=MIN(drawable->height, y2+height/2.0); } /* How we can determine the width and the height of the area being */ /* reduced.*/ width= x2-x1; height = y2-y1; /* The lines below determine which dimension is to be the longer*/ /* side. The idea borrowed from the supernova plug-in. I suspect I */ /* could've thought of it myself, but the truth must be told.*/ /* Plagiarism stinks!*/ if (width>height) { RW=LongerSize; RH=(float) height * (float) LongerSize/ (float) width; } else { RH=LongerSize; RW=(float)width * (float) LongerSize/ (float) height; } /* The intire image is stretched into a string! */ tempRGB= (guchar *) malloc(RW*RH*bytes); tempmask= (guchar *) malloc(RW*RH); gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&srcMask, mask, x1, y1, width, height, FALSE, FALSE); /* Grab enough to save a row of image and a row of mask. */ src_row= (guchar *) malloc (width*bytes); src_mask_row= (guchar *) malloc (width); for (i=0; i < RH; i++) { whichrow=(float)i*(float)height/(float)RH; gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y1+whichrow, width); gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x1, y1+whichrow, width); for (j=0; j < RW; j++) { whichcol=(float)j*(float)width/(float)RW; /* No selection made = each point is completely selected! */ if (NoSelectionMade) tempmask[i*RW+j]=255; else tempmask[i*RW+j]=src_mask_row[whichcol]; /* Add the row to the one long string which now contains the image! */ tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0]; tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1]; tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2]; /* Hold on to the alpha as well */ if (bytes==4) tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3]; } } temp->bpp=bytes; temp->width=RW; temp->height=RH; temp->rgb=tempRGB; temp->mask=tempmask; return temp; } The following is a preview function which used the same ReducedImage type! Note that it uses fakes transparancy (if one is present by means of fake_transparancy which is defined as follows: gint fake_transparency(gint i, gint j) { if ( ((i%20)- 10) * ((j%20)- 10)>0) return 64; else return 196; } Now here's the preview function: void my_preview_render_function(GtkWidget *preview, gintchangewhat, gintchangewhich) { gint Inten, bytes=drawable->bpp; gint i, j, k; float partial; gint RW=reduced->width; gint RH=reduced->height; guchar *row=malloc(bytes*RW);; for (i=0; i < RH; i++) { for (j=0; j < RW; j++) { row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0]; row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1]; row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2]; if (bytes==4) for (k=0; k<3; k++) { float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0; row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j); } } gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW); } free(a); gtk_widget_draw(preview,NULL); gdk_flush(); } Applicable Routines guintgtk_preview_get_type(void); /* No idea */ voidgtk_preview_uninit (void); /* No idea */ GtkWidget*gtk_preview_new(GtkPreviewTypetype); /* Described above */ voidgtk_preview_size(GtkPreview*preview, gint width, gint height); /* Allows you to resize an existing preview.*/ /* Apparantly there's a bug in GTK which makes*/ /* this process messy. A way to clean up a mess */ /* is to manually resize the window containing*/ /* the preview after resizing the preview.*/ voidgtk_preview_put(GtkPreview*preview, GdkWindow*window, GdkGC*gc, gint srcx, gint srcy, gint destx, gint desty, gint width, gint height); /* No idea */ voidgtk_preview_put_row(GtkPreview*preview, guchar*src, guchar*dest, gint x, gint y, gint w); /* No idea */ voidgtk_preview_draw_row(GtkPreview*preview, guchar*data, gint x, gint y, gint w); /* Described in the text */ voidgtk_preview_set_expand (GtkPreview*preview, gint expand); /* No idea */ /* No clue for any of the below but*/ /* should be standard for most widgets */ voidgtk_preview_set_gamma(doublegamma); voidgtk_preview_set_color_cube (guintnred_shades, guintngreen_shades, guintnblue_shades, guintngray_shades); voidgtk_preview_set_install_cmap(gint install_cmap); voidgtk_preview_set_reserved(gint nreserved); GdkVisual*gtk_preview_get_visual (void); GdkColormap*gtk_preview_get_cmap(void); GtkPreviewInfo* gtk_preview_get_info(void);
使用gtk_menu_bar_new來產生一個menu bar. 這一步僅需做一次, 當我們產生一系列選單在menu bar上.
使用gtk_menu_bar_append來將主選單放到menubar.
14.2 Manual Menu範例
我們來做做看, 看看一個範例會比較有幫助.
#include int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *menu; GtkWidget *menu_bar; GtkWidget *root_menu; GtkWidget *menu_items; char buf[128]; int i; gtk_init (&argc, &argv); /* create a new window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW (window), "GTK Menu Test"); gtk_signal_connect(GTK_OBJECT (window), "destroy", (GtkSignalFunc) gtk_exit, NULL); /* Init the menu-widget, and remember -- never * gtk_show_widget() the menu widget!! */ menu = gtk_menu_new(); /* This is the root menu, and will be the label will be the menu name displayed on * the menu bar.There won't be * a signal handler attached, as it only pops up the rest of the menu when pressed. */ root_menu = gtk_menu_item_new_with_label("Root Menu"); gtk_widget_show(root_menu); /* Next we make a little loop that makes three menu-entries for "test-menu". * Notice the call to gtk_menu_append.Here we are adding a list of menu items * to our menu.Normally, we'd also catch the "clicked" signal on each of the * menu items and setup a callback for it, but it's omitted here to save space. */ for(i = 0; i < 3; i++) { /* Copy the names to the buf. */ sprintf(buf, "Test-undermenu - %d", i); /* Create a new menu-item with a name... */ menu_items = gtk_menu_item_new_with_label(buf); /* ...and add it to the menu. */ gtk_menu_append(GTK_MENU (menu), menu_items); /* Show the widget */ gtk_widget_show(menu_items); } /* Now we specify that we want our newly created "menu" to be the menu for the "root menu" */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu); /* Create a menu-bar to hold the menus and add it to our main window*/ menu_bar = gtk_menu_bar_new(); gtk_container_add(GTK_CONTAINER(window), menu_bar); gtk_widget_show(menu_bar); /* And finally we append the menu-item to the menu-bar -- this is the "root" * menu-item I have been raving about =) */ gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu); /* always display the window as the last step so it all splashes on the screen at once. */ gtk_widget_show(window); gtk_main (); return 0; }