g_object_{new,set,get} vs. class-specific function

Gabriel Schulhof nix at go-nix.ca
Sun May 20 09:16:25 EDT 2007


Hi, Richard!

You asked me why I prefer using GObject properties rather than
class-specific accessors and constructors. A bit of an essay follows, so
please feel free to ignore it and instruct me to use the class-specific
functions in Pidgin :o)

I have found several reasons:

- Using g_object_new instead of the class-specific constructors is more
flexible, primarily because you're not restricted to the constructors
available for the given type. For example:

gtk_window_new (GTK_WINDOW_TOPLEVEL);

Here, the argument will be GTK_WINDOW_TOPLEVEL 99.99% of the time. This
also happens to be the default value of the "type" property, so it
shouldn't need to be specified. OTOH, because of this lone constructor,
you need to make additional calls to properly configure the window:
gtk_window_set_title
gtk_window_set_resizable
gtk_container_set_border_width
gtk_window_set_default_size
gtk_window_set_role
With g_object_new, you could create and configure your window in one
call:
g_object_new(GTK_TYPE_WINDOW,
  "title",         _("My Window"),    "resizable",      FALSE,
  "border-width",  PIDGIN_HIG_BORDER, "role",           "demo",
  "default-width", 320,               "default-height", 240, NULL) ;

And, even if that doesn't float your boat, you can at least replace the
useless GTK_WINDOW_TOPLEVEL with, say, the more useful "title":
-gtk_window_new(GTK_WINDOW_TOPLEVEL);
+g_object_new(GTK_TYPE_WINDOW, "title", _("My Window"), NULL);

Another example:

label = gtk_label_new("Some Really Long Text");
gtk_widget_show(label);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);

vs.

g_object_new(GTK_TYPE_LABEL,
  "visible", TRUE, "label",   _("Some Really Long Text"),
  "wrap",    TRUE, "justify", GTK_JUSTIFY_LEFT,
  "xalign",  0.0,  "yalign",  0.0, NULL);

This particular example illustrates a sequence that is present for
almost all child widgets ever created:

new_widget = gtk_some_widget_new();
gtk_widget_show(new_widget);

If you do nothing else, you could at least replace the above two lines
with new_widget = g_object_new (GTK_TYPE_SOME_WIDGET, "visible", TRUE,
NULL) and save a line of code for each child widget created. For
constructors with more than 0 parameters, you can still specify those
parameters on the one line of code that creates and now shows the
widget.

- Another benefit of g_object_new is that you can free yourself from
having to track uninteresting widgets. For example:

GtkWidget *label = NULL, *important_entry = NULL;

label = gtk_label_new("Temperature:");
gtk_widget_show(label);
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0) ;

important_entry = gtk_entry_new() ;
gtk_widget_show(important_entry);
gtk_box_pack_start(GTK_BOX(hbox), important_entry, TRUE, TRUE, 0);

vs.

GtkWidget *important_entry = NULL;

gtk_container_add_with_properties (GTK_CONTAINER (hbox),
  g_object_new (GTK_TYPE_LABEL, "visible", TRUE, "justify",
GTK_JUSTIFY_RIGHT, "xalign", 1.0, "yalign", 0.5, NULL),
  "expand", FALSE, NULL);

gtk_container_add (GTK_CONTAINER (hbox),
  important_entry = g_object_new (GTK_TYPE_ENTRY, "visible", TRUE,
NULL));

This sequence brings up another point:

- With properties, you are not forced to specify values that should be
kept default

Your example:
gtk_vbox_new (gboolean homogeneous, gint spacing);

Why should I be forced to specify a spacing whenever I create a VBox?
Keeping the default would probably eliminate a lot of hardcoded values.
In fact (and with a little more blue-sky thinking) it would allow one to
set a system-wide default value that all apps would all-of-a-sudden
reflect. Kinda like style properties, actually, which (I speculate)
would then become just like normal properties.

Side note: "homogeneous" is also FALSE by default, and specified as
"FALSE" 99.99% of the time when creating a vbox or and hbox. So, again,
completely useless. g_object_new (GTK_TYPE_VBOX, "visible", TRUE, NULL)
would be far more useful.

In a similar vein, specifying the padding when calling
gtk_box_pack_start is also a source of hardcoded values. So, why have
it? Just use gtk_container_add_with_properties and specify only those
child properties whose default values are not what you want.

- Using g_object_{get,set} allows you to become a little more
object-type-agnostic. Consider, for example, an object class that
"sports" the "active" property. It could be a GtkToggleButton or a
GtkToggleToolButton. If we were to always use g_object_get (G_OBJECT
(widget), "active", &active, NULL); instead of
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)), we could
change the type of the widget (and therefore our UI) without having to
touch those parts of the code where we retrieve the "active" property,
by virtue of the fact that different widget classes all implement this
"active" property. I have experienced the
GtkToggleButton/GtkToggleToolButton scenario when I introduced a
GtkToolbar into one of my apps, and I had to hunt the code for calls to
gtk_toggle_button_{get,set}_active (GTK_TOGGLE_BUTTON
(the_toggle_button)) and change it to
gtk_toggle_tool_button_{get,set}_active (GTK_TOGGLE_TOOL_BUTTON
(the_toggle_button))

Of course, if you use g_object_{get,set}, you can also connect a signal
handler to multiple different object types, as long as they all have the
property your signal handler is interested in.




Gabriel





More information about the Devel mailing list