GTK+ / Gnome Application Development | |||
---|---|---|---|
<<< Previous | Home | Next >>> |
This chapter contains some commonly-asked questions, and answers, with references to the rest of the book. See the table of contents for a summary of the questions.
Call the gdk_beep() function.
See the section called Widget Life Cycle in the chapter called GTK+ Basics for the simple answer, and the section called Object Finalization in the chapter called The GTK+ Object and Type System for more details.
Normally g_malloc() and g_free() are just wrappers around malloc() and free(), with a couple of extra features described in the section called Memory in the chapter called glib: Portability and Utility. However, when you turn on memory profiling, they are no longer interchangeable with malloc() and free(). So anytime you incorrectly mix the two pairs of functions, your program will crash.
If you're using the GNU C library, which comes with nearly all Linux distributions, it has a special feature which can help you debug this. Set the MALLOC_CHECK_ environment variable to 2 before running your program, then run the program in gdb. As soon as free() gets a pointer not created by malloc(), abort() will be called.
You are probably fighting a losing battle. Widgets really aren't what you want, most likely. Consider using a GtkDrawingArea or the GnomeCanvas to create your custom display.
If you really need interactive widgets, such as a GtkEntry or GtkButton, you can try to use GtkLayout or GtkFixed.
If you have very specialized needs, you probably need to write your own widget. the chapter called Writing a GtkWidget tells you how to do so.
glib does not call malloc() every time it needs a new node in a data structure. If it did, building linked lists (for example) would be substantially slower. Instead, glib caches pools of equal-sized "memory chunks" for use in these data structures. Since the chunks are still available for recycling when your program exits, they are never free()d. (Of course, the operating system will reclaim the memory, but tools such as ccmalloc and Purify will report it as a memory leak.)
To get around this, you can plug a new GAllocator into most of the data structures. A GAllocator is a pool of memory as described above. Just create an allocator manually, so you have a pointer to it; you can then free the allocator when you are finished. Figure 1 summarizes the relevant functions for GList. A quick glance through glib.h will reveal the corresponding functions for other data structures.
The name argument to g_allocator_new() is used in debugging messages; the n_preallocs argument is passed through to g_mem_chunk_new().
These come from the g_return_if_fail() checks at the beginning of many GTK+ functions. (They will only appear if your copy of GTK+ was compiled with debugging turned on---and hopefully it was if you are writing an application.) You will need to look at the exact assertion that failed to see what causes the warning. A common one: if you accidentally access a destroyed widget or object, you will have a pointer to memory garbage. Among other things, this means the type tag will be invalid; so GTK+'s runtime type checks will fail.
Historical accident, mostly. Sometimes there is a reason; for example, GTK+ does not include gdk_imlib, so does not include any widgets that rely on it. In very general terms, GTK+ imposes less "policy" than Gnome; some Gnome widgets are deliberately inflexible to keep people from creating an inconsistent user interface. GTK+ does not take this approach. Finally, some of the Gnome widgets were considered too "experimental" to go in GTK+ at the time. However, the core Gnome widgets discussed in this book are not in this category.
If the window is a GnomeDialog, this is user-configurable and you should not do it. In most other cases it would be a bit strange; but there are exceptions, such as splash screens. The function you want is gtk_window_set_position(); you can leave the window's position up to the window manager (the default), ask to have it centered, or ask to have it appear wherever the mouse pointer is. There is an enumeration which corresponds to these settings: GTK_WIN_POS_NONE, GTK_WIN_POS_CENTER, GTK_WIN_POS_MOUSE. For example:
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); |
You should do this before calling gtk_widget_show(), because the function affects where the window appears when it is first placed on-screen.
No. When people ask this question they are usually looking for an abstract interface that draws either to the screen or to a printer. There is nothing like that in GTK+ right now. GnomeCanvas will probably have a feature like this in a future version.
There is a gnome-print library available, which handles many unpleasant low-level details when dealing with fonts and PostScript. It also comes with a printer-selection dialog.
There are two things to remember:
The child process must not try to use the GUI; since it shares file descriptors with the parent, including GTK+'s connection to the X server, GTK+ will become very confused.
The child process must be terminated with _exit() rather than exit(); calling exit() will shut down GTK+ and confuse the parent process. (GTK+ registers a "cleanup" function using atexit().)
the section called Realizing, Mapping, and Showing in the chapter called GTK+ Basics goes into some detail on this. But here is a brief summary.
Showing a widget implies mapping it eventually (to be precise, it schedules the widget to be mapped when its parent widgets are mapped). Mapping a widget means calling gdk_window_show() to display the widget's GdkWindow on the screen (if it has a GdkWindow, some widgets don't). To map a widget you must first realize it. Therefore showing a widget implies realizing it. Therefore if you show a widget you don't need to explicitly realize it with gtk_widget_realize() because it will be realized eventually anyway.
There's one exception, however. To realize a widget means to allocate X server resources for it, most notably a GdkWindow. Some things you might want to do require the GdkWindow to exist, so you might want to force a widget to be realized immediately. gtk_widget_realize() does this. Since parent widgets must be realized before their children, gtk_widget_realize() will immediately realize all of a widget's parents as well. One of these parents must be a toplevel window, or realization will not be possible.
If you force-realize a widget, you still have to call gtk_widget_show() since realization does not map the widget.
A good but not foolproof rule of thumb: if you are using GTK_WIDGET(widget)->window, you will need widget to be realized.
However, it should be noted that force-realizing a widget is always a mildly bad idea; it is inefficient and uncomfortably low-level. In many cases you can work around the need to do so.
Creating a pixmap requires a colormap. gdk_pixmap_create_from_xpm_d() requires a GdkWindow argument in order to extract a colormap. You are probably trying to use the window field of an unrealized widget, which is NULL. You might try the newer function, gdk_pixmap_colormap_create_from_xpm_d() which accepts a colormap argument; if you pass in a colormap, its window argument can be NULL. However, using Imlib instead is a still better solution; Imlib's pixmap routines are faster anyway.
For a variety of reasons, an application's graphical interface tends to be an exceptionally volatile and ever-changing piece of software. It's the focus of most user requests for change. It is difficult to plan and execute well the first time around---often you will discover that some aspect of it is unpleasant to use only after you have written it. Making things worse, graphical interfaces are not portable across machines; Gnome works on X windows, but if your application is useful, it won't be long before someone wants to run your application on another system, or have a command-line version, or have a web-based interface. You might even want to have two interfaces in the same version---perhaps the GUI, and a scripting language such as Guile.
In practical terms, this means that any large application should have a radical separation between its various frontends, or interfaces, and the backend. The backend should contain all the ``hard parts'': your algorithms and data structures, the real work done by the application. Think of it as an abstract ``model'' being displayed to and manipulated by the user.
Each frontend should be a ``view'' and a ``controller.'' As a ``view,'' the frontend must note any changes in the backend, and change the display accordingly. As a ``controller,'' the frontend must allow the user to relay requests for change to the backend (it defines how manipulations of the frontend translate into changes in the model).
There are many ways to discipline yourself to keep your application separated. A couple of useful ideas:
Write the backend as a library; if this becomes undesirable for any reason, you can always statically link.
Write at least two frontends from the start; one or both can be ugly prototypes, you just want to get an idea how to structure the backend. Remember, frontends should be easy; the backend has the hard parts.
If one of your frontends is Gnome- or GTK+- based, an excellent choice for the other is an interactive Guile terminal. Your non-expert end users probably won't use it, but it's a great debugging tool; you can prototype and test the backend using easy-to-write Guile bindings, and add the graphical controls only when things are working. When you're done, you'll have a scriptable application almost for free.
If your application can potentially be run in batch mode, command line and web interfaces are also relatively easy to write, useful for debugging, and will keep you disciplined.
Finally, if your project is large enough to justify the bother and complexity, consider using a cross-platform frontend layer to share code between GUI frontends on different platforms. This approach is taken by Mozilla (http://www.mozilla.org), and the AbiSource office suite (http://www.abisource.com). It might be interesting to have a look at their code.
Don't program your preferences. GTK+ unfortunately has all sorts of look and feel settings that the programmer can affect. For example, you can change the appearance of the ``expanders'' in a GtkCTree---they can be triangles, squares, or circles. By default they are squares. You change them by calling gtk_ctree_set_expander_style().
There's no good reason to call this function in an application. Ever. Think about why you would call it---because you happen to like that expander style better. It's a purely cosmetic issue. However, if you do call it, you've just made your application's look and feel different from that of every other application. This is harmful, because it confuses users and even gives them a sense that your application is ``unprofessional'' or ``not quite right.''
``But I want my favorite expanders!,'' you might whine. Don't despair. There is a correct way to handle this situation. Variable aspects of look and feel should be configurable at runtime by users. What's more, it should be configurable globally, for all applications at once. GTK+ provides themes for precisely this purpose.
Unfortunately themes do not yet cover all aspects of look and feel, and so the temptation remains to hard-code these in your application. You must resist. If you are dead-set against the default expander style, or the default dialog position, or whatever, then do the work to make it configurable on the library level and submit that code to the GTK+ or Gnome maintainers.
You have to do this on the library level---think about it. If you provide an application-specific way to configure look and feel, nothing has really been gained; if someone does like a particular expander style, they have to go through each program deciding if and how the style can be changed. Some programs will invariably be ``stuck'' with the default, since the authors of those programs didn't make it configurable. The resulting mess is very annoying to users.
Gnome already has solutions for a number of common cases. For example, GTK+ lets you pop up a dialog at the mouse pointer, in the center of the screen, or wherever the window manager wants; there is no reason you should pick your favorite and use it in your application. Thus GnomeDialog loads a user preference for the dialog's initial position. This preference can be set from the Gnome control center.
Strings are nicer. They are easier to type and less headache for GtkObject authors to maintain. They don't clutter the C namespace. Typing a string incorrectly will trigger a runtime error so macros don't improve error checking. Finally, strings are internally converted to a numeric ID so there is no loss in efficiency.
Consider the maintenance headache of using enumerations instead: both enumeration values and their names would have to be unique across GTK+, Gnome, and third-party extensions. A nightmare.
First and foremost: asking this question in any public forum is strongly discouraged. Don't do it. Check the archives for several extended off-topic flamefests if you're interested.
Here are some reasons:
The original authors wanted to write it in C, and now many C-only applications are based on it. The current authors enjoy C.
GTK+ handles types and objects much more flexibly than C++; it is runtime-oriented, more like Java or Objective C than C++ system. This is convenient for GUI builders and language bindings.
C is the lingua franca of UNIX development; most people know how to code in it.
There are already nice toolkits for languages such as Java and Objective C. There are C++ wrappers for GTK+; several, in fact.
C is more portable than C++; ANSI C++ is not yet widely implemented, so only an ill-defined subset of C++ can actually be used.
When GTK+ development first started, there was no free, working C++ compiler.
Again: do not ask this question on any mailing lists, because people will not be amused.
If you specify GDK_POINTER_MOTION_HINT_MASK, you must call gdk_window_get_pointer() to get more motion events. One motion event is sent each time you get the pointer location. See the section called Mouse Movement Events in the chapter called GDK Basics.
There is an Xlib routine called XWarpPointer() that does this, but GDK does not wrap it. It is almost certainly a bad idea to use this feature (in fact it is intended for window managers only); you might consider writing to one of the GTK+ or Gnome mailing lists to ask for another way to achieve whatever you are trying to achieve. However, you can always use Xlib routines (such as XWarpPointer()) by including gdk/gdkx.h and gdk/gdkprivate.h, then manipulating the private parts of the GDK data structures. If that sounds unsavory, it probably should.
First and foremost: remember that a pixmap is a server-side resource, i.e. possibly across a network and definitely across some kind of socket. Therefore, you do not want to request its pixels one by one. Iterating over a pixmap that way could easily take many seconds.
GDK wraps an Xlib object called XImage. The wrapper is called GdkImage. A GdkImage is essentially a local copy of the data in a pixmap. You can copy a region of a pixmap or window into a GdkImage with the gdk_image_get() routine, then get and set pixels with gdk_image_get_pixel() and gdk_image_put_pixel(). You can also access the image's data structures directly, but this is quite complicated (due to visuals, depths, differences between host and network byte order, and so on). If you modify the image, you use gdk_draw_image() to copy it back to a server-side drawable.
Copying a pixmap to a GdkImage, or copying a GdkImage to a pixmap, still involves moving quite a bit of data over the network; however, since it's all in one burst the speed can be tolerable in many cases. Also, if the client and the server are on the same machine, and the X shared memory extension is available, GDK will automatikcally set up a shared memory segment to copy the data.
Most of the time, if you plan to do a lot of image manipulation, you are better off using RGB buffers as your primary data structure (see the section called RGB Buffers in the chapter called GDK Basics). The functions in gdk/gdkrgb.h allow you to copy an RGB buffer to a drawable. These functions use GdkImage internally, but they are tuned to be very fast and handle all the complexities for you.
See the previous question. You should probably use the GDK RGB functions (the section called RGB Buffers in the chapter called GDK Basics).
GtkLabel is a windowless widget; it is "transparent" and draws on its parent container's background. If you want to set the background, place the label in a GtkEventBox. The same answer applies to other windowless widgets, such as GtkImage.
gtk_whatever_foo() is typically a public function which emits the "foo" signal, taking care of any necessary details before and after emission (remember that only GTK_RUN_ACTION signals can be emitted without special actions before and after). gtk_whatever_real_foo() will be the default handler for the signal, installed in the object's class struct. the chapter called Writing a GtkWidget has many examples of this.
See the section called Sensitivity in the chapter called GTK+ Basics. Short answer:
gtk_widget_set_sensitive(widget, FALSE); |
There are several possibilities:
The widget has no GdkWindow (i.e. the GTK_NO_WINDOW flag is set), so it does not receive events (other than synthesized expose events).
The event you're trying to monitor isn't in the event mask for the widget's GdkWindow. Use gtk_widget_add_events() to add more events to the mask.
The widget is a container, and some child widget is "handling" the event by returning TRUE from the event signal emission. Only "unhandled" events are propagated from child to parent.
See the section called Receiving GDK Events in GTK+ in the chapter called GDK Basics for more details on events and how they are passed to widgets.
Key press handling is somewhat complex. You might want to read the section called Keyboard Focus in the chapter called GDK Basics and the section called Focus in the chapter called GTK+ Basics for a brief overview. the section called Receiving GDK Events in GTK+ in the chapter called GDK Basics is also relevant.
In short, key events are initially received by a toplevel GtkWindow. GTK+'s key event behavior is more or less defined by default key press event handler in gtkwindow.c (looking at this function is instructive). It works as follows:
If there's a focus widget, the key event signal is emitted on the focus widget. If this emission returns TRUE, as described in the section called Receiving GDK Events in GTK+ in the chapter called GDK Basics, processing stops.
If any of the accelerator groups attached to the window contain an accelerator matching the event, then processing stops.
If the key event hasn't been handled yet, there are some default bindings; the arrow keys move the focus around, for example.
Thus, to override the arrow key behavior, you can return TRUE from the focus widget's signal emission, install an accelerator for the arrow keys, or connect to "key_press_event" on the toplevel window and use gtk_signal_emit_stop_by_name() to end the signal emission before the GtkWindow default handler runs.
No, but "interfaces" (in Java terms) or "pure virtual classes" (in C++ terms) are planned for the next version. See the section called Overridable Signals in the chapter called Writing a GtkWidget for a discussion of an ugly workaround used in GtkWidget to create "activatable" and "scrollable" interfaces.
First, run your program with the --sync option. This invokes XSynchronize() to turn off event buffering; it slows down the application, but causes errors to be reported as soon as they occur. Alternatively, some Xlib implementations let you turn on synchronization by setting the global variable _Xdebug to TRUE in a debugger.
Once errors are being reported synchronously, just run your app in a debugger and wait for abort() to be called. For warnings, set a breakpoint at g_logv() which is the function called by the g_warning() macro.
Just do this:
while (gtk_events_pending()) gtk_main_iteration(); |
This code will handle all pending events, then return control to you. You can also run nested instances of gtk_main(); each call to gtk_main_quit() exits one instance. gnome_dialog_run() uses this technique to block waiting for user input.
The GTK+ coding style is basically the GNU coding style (http://www.gnu.org/prep/standards_toc.html). The Gnome libraries are less consistent, but lean toward the Linux kernel coding style (documented in /usr/src/linux/Documentation/CodingStyle on many Linux systems).
The GTK+ style uses two-space indentation, puts all braces on a new line, and leaves one space between identifiers and opening parentheses, like this:
if (whatever) { foo (arg1, arg2); } |
Emacs uses this style by default.
The Gnome style uses eight-space indentation and Kernighan and Ritchie braces, like so:
if (whatever) { foo (arg1, arg2); } |
It also leaves a space between identifiers and opening parentheses. To make Emacs use the Gnome style, add a line like this to the top of your source files:
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 c-style: "K&R" -*- */ |
When preparing a patch for any piece of free software, it's polite the style of the preexisting code. It's customary to include a file called HACKING in source code distributions addressing this and similar issues; read it if it exists.
A very promising GUI builder called Glade is being developed. Glade can generate source code in several languages, or an XML description of your widgets. An add-on module called libglade loads these XML descriptions at runtime and creates the described widgets. The next release of the Gnome libraries will very likely include or require libglade.
GTK+ 1.2 supports most European and Asian languages. GDK contains an API for loading fontsets and rendering multibyte strings, though this book does not cover it. The stock GTK+ widgets that handle text use this API and will deal with multibyte strings correctly. GTK+ also supports input methods for Asian languages. GTK+ 1.2 does not support right-to-left scripts, or scripts that require complex ligatures and unusual line breaks. However, support for these languages is a high priority for GTK+ 1.4. For details on future plans, Owen Taylor's white paper at http://www.gnome.org/white-papers/i18n/gtki18n/ is an excellent resource.
Both GTK+ and Gnome use the gettext message catalog system to translate user-visible strings, so any string the toolkit knows how to render can be translated into foreign languages. the section called Internationalization in the chapter called Gnome Application Basics covers this topic.