Re: GUI programming vs encapsulation



Daniel Haude wrote:

as my Glade/GTK project is growing larger, I find myself confronted
with code organization issues. I have all my callbacks in a single
file, but of course there are groups of related widgets that logically
go together (say, the widgets inside a top-level dialog window). Also
the widgets of one dialog only need to know about each other, but not
about widgets in other windows.

The issue is how to separate these things logically other than just 
splitting up the source code in comment-separated chunks.

1. Separate source files for each dialog.
    -> unpractical because this is a single Glade project, and Glade 
only writes single source files.

I use different source files for handling different logical parts of
larger programs. My rule is this: all event handlers that are really
short and simple (2 or 3 statements at most) are kept in callbacks.c.
I reorder them to pack together all logically related ones, i.e. all
handlers for one dialog are together, separated by clearly visible
comment blocks. Glade doesn't care about their orders after they've
been created by it.

More complex handlers are outsourced to custom source files which are
grouped by logical program parts, i.e. one or more source files per
dialog (or one or more dialogs per source file). In this case the
handler in callbacks.c just consists of a single line like this:
    return my_dialog_on_delete_event (widget, event, data);
The additional call overhead is negligible for event handlers. Depending
on the structure of the program, the unambiguousness of your functions
and the operations to perform, the call may even be simplified to this:
    return my_dialog_on_delete_event ();

2. Storing information about widgets (i.e., pointers to widgets)
inside the parent top-level widget using the G_OBJECT methods 
(g_object_set_data() and friends), and finding the widget with the 
lookup_widget() funcion supplied with Glade
    -> I somehow don't like it. I like mean, lean, and fast programs, 
and the mere thought that each push of a button or movement of a
slider generates an avalanche of hash lookups gives me ulcers, and it
litters the code with "magic" lookup key strings.

Hash lookups are actually fast. How frequently do you expect your
buttons to be pushed? More than a hundred times per second? If so, GTK+
may develop a small performance problem indeed. However, this regards
rerendering the states of the button all the time. There will certainly
be no performance problem regarding hash lookups. Nowadays machines are
capable of performing many hundreds of thousands, if not millions, of
hash lookups a second. The slow parts of GTK+ are mainly the actual
rendering operations, not the I/O-less low level functions.

3. For each widget, declare a global pointer variable, a "create" and
a "delete" callback. In the create callback I set the global variable
to the pointer to the widget, and in the delete callback I set the
global variable to NULL so that other parts of the program know that
this widget doesn't exist any more.
    -> No encapsulation and more callbacks just to set/unset global 
variables.

Currently I'm using a mixture of 2. and 3. (depending on how I feel
that day), but leaning towards 3. without much passion.

My preferred way resembles your way 2., I think. Although I use neither
global widget pointer variables nor G_OBJECT methods nor Glade
extensions to find widgets. I wrote my own widget_find_by_name ()
function, which is independent from Glade and doesn't require anything
else but the widgets you want to reference to be named properly. This
way I need local (to the functions) widget pointers only.

This is certainly not the fastest way (it consists of numerous string
comparisons). It's even slower than hash lookups. However, I found this
to be the easiest way to reference and access large numbers of widgets,
without having to care about any pre- or postcautions, like initializing
and deinitializing pointers or objects. To me, the benefits of not
having to store and maintain any global widget pointers, which
simplifies code a lot and averts an entire class of programming errors,
clearly outweigh the tiny performance decrease, which is absolutely
beyond notice, even on slowest machines and with large widget lists to
traverse.

The version at
http://www.spamkiller.bytechase.cx/democode/widget_find_by_name.c is
just optimized for GTK+ compliance, not for speed. A version directly
accessing GTK+ internals would be (probably much) faster but I never
have encountered any real need for that so far.

So how do you guys deal with this problem? I guess there is no real
way out of having all the callbacks spread out in this "flat" fashion 
because from the C program's point of view there's no telling in what 
order they might be called. Is writing unwieldy spaghetti code a 
necessary part of GUI programming, or have I missed some important
point?

No telling in what order handlers might be called is not a C-specific
issue. The order in which they are put in the source code is absolutely
unrelated to the runtime calling order.

You have a great freedom in grouping together your functions in blocks
within a single file or in separate files. Spaghetti code is not an
inevitable result of this freedom but usually of inexperience, lack of
structural (or logical) concepts or discipline.

Another tip: you know, you don't need to use distinct handler functions
for distinct widgets. Especially groups of checkboxes or radiobuttons,
but possibly groups of buttons or other widgets as well, can perfectly
share the same handler function (for the same type of event). To which
particular widget the event belongs can be determined by the widget
pointer which is delivered to all event handlers. Widgets should be
named properly to use this strategy. This way you can significantly
reduce the number of handler functions and simplify later changes to the
event handlers of those groups of widgets. This simplification is
supported by Glade as well.



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]