Property set_property and get_property

Hello people

I am trying to develop a program in gtk, I have already made several implementations to the program, however I have a doubt about how the properties work in g_object, I have read the documentation but I did not understand much. Can someone explain me better what the properties and functions of some_name_set_property and some_name_get_property are for.

If someone can show me some practical examples.
Thanks for the help.

The standard documentation for GObject properties is pretty good, and you can find that here, if that’s not the documentation you’ve read.

More of a high-level overview goes like this:

GObject classes can register “formal” properties, which makes them easier to work with in language bindings, Glade UI files and so on. Generally speaking these use GValue as a variable type container to pass around the values.

The special functions g_object_set() and g_object_get() are convenience functions (usually only used in C) that will take a property name and a property value, pack them into or retrieve them from a GValue appropriate for the type it was registered as.

In contrast, a function like my_object_get_name() is just a regular function that returns a string function; it doesn’t have to be a “formal” property. In your GObject or GtkWidget subclass, the functions my_object_get_property() and my_object_set_property() are functions you override to implement the “formal” property packing/unpacking into GValue.

enum
{
  PROP_0, // Special, don't use this
  PROP_NAME,
  N_PROPERTIES
};

static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };

static void
my_object_set_property (GObject      *object,
                        guint         property_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
  MyObject *self = MY_OBJECT (object);

  switch (property_id)
    {
    // Here we are being asked by a caller to "set" the property, so
    // we are required to unpack the property from a GValue and
    // use the unpacked value to *actually* set the property.
    case PROP_NAME:
      my_object_set_name (self, g_value_get_string (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
my_object_get_property (GObject    *object,
                          guint       property_id,
                          GValue     *value,
                          GParamSpec *pspec)
{
  MyObject *self = MY_OBJECT (object);

  switch (property_id)
    {
    // Here we are being asked by a caller to "get" the property, so
    // we are required to pack the property into a GValue.
    case PROP_NAME:
      g_value_set_string (value, my_object_get_name (self));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
my_object_class_init (MyObjectClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->set_property = my_object_set_property;
  object_class->get_property = my_object_get_property;

  // Here is where we are defining the type and
  // constraints of the formal property...
  obj_properties[PROP_NAME] =
    g_param_spec_string ("name",
                         "Name",
                         "Name of the file to load and display from.",
                         NULL,
                         G_PARAM_READWRITE);

  // ...and then installing (aka registering) it  as
  // a "formal" property
  g_object_class_install_properties (object_class,
                                     N_PROPERTIES,
                                     obj_properties);
}

So in other words, all this g_object_get_property() stuff is just another interface you are implementing in your subclass. It’s just that it’s a interface that is very central to the GObject framework; like GObject signals are ultimately just another function->callback framework.

Does that sort of answer your question?

I have the following problem I have a class that has GtkComboBox and another class that has a GtkButton, when clicking on GtkButton, I need to update the GtkComboBox of the other class. To solve this problem I used this concept of set_property and get_property, in the class that has GtkButton I created a pot for the class that has GtkComboBox. Can this algorithm be implemented in this way? Or is this not the correct pattern?

If all you need to do is change a property of the GtkComboBox (probably active-id?) when the GtkButton is clicked, you should just use a signal callback.

void
on_clicked (GtkButton *button,
            GtkComboBox *my_combo)
{
  char *some_id;

  // With a custom, installed property. Using the GObject property
  // framework results in the value being copied, so it must be freed after
  g_object_get (G_OBJECT (button), "some-prop", &some_id, NULL);
  gtk_combo_box_set_active_id (my_combo, some_id);
  g_free (some_id);
}

g_signal_connect (G_OBJECT (my_button),
                  "clicked",
                  G_CALLBACK (on_clicked),
                  my_combo);

Is that what you mean?

There’s only one problem are two different files

See this:

/* 
 * Copyright 2019 Danilo Fernandes Galete
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "fnc-combo-box-category.h"

struct _FncComboBoxCategory{
	GtkComboBox		parent;

	/* The Widgets */
	GtkListStore		*liststore;
	
	/* The Parameters */
	GtkMenuButton		*button_add_subcategory;
	FncComboBoxSubcategory	*combo_box_subcategory;
	FncPopoverSubcategory	*popover_subcategory;
 };

enum{
	PROP_0,
	PROP_BUTTON_ADD_SUBCATEGORY,
	PROP_COMBO_BOX_SUBCATEGORY,
	PROP_POPOVER_SUBCATEGORY,
	N_PROPERTIES
};

static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (FncComboBoxCategory, fnc_combo_box_category, GTK_TYPE_COMBO_BOX);

static void
on_combo_box_category_changed (GtkComboBox	*combo_box,
			       gpointer		user_data)
{
	FncComboBoxCategory	*self;
	GtkTreeModel		*model;
	GtkTreeIter		iter;
	gint			col_id;
	
	self = FNC_COMBO_BOX_CATEGORY (user_data);
	
	col_id = gtk_combo_box_get_active (combo_box);
	
	if (col_id != -1)
	{
		model = gtk_combo_box_get_model (combo_box);
		
		gtk_combo_box_get_active_iter (combo_box, &iter);
		
		gtk_tree_model_get (model, &iter,
				    0, &col_id, -1);
		
		gtk_widget_set_visible (GTK_WIDGET (self->button_add_subcategory), TRUE);
		
		fnc_combo_box_subcategory_update (FNC_COMBO_BOX_SUBCATEGORY (self->combo_box_subcategory), col_id);
		
		g_object_set (G_OBJECT (self->popover_subcategory), "id_category", col_id, NULL);
	}
	else
	{
		gtk_widget_set_visible (GTK_WIDGET (self->button_add_subcategory), FALSE);
		
		fnc_combo_box_subcategory_clean (FNC_COMBO_BOX_SUBCATEGORY (self->combo_box_subcategory));
	}
}
static void
fnc_combo_box_category_set_property (GObject		*object,
				     guint		property_id,
				     const GValue	*value,
				     GParamSpec		*pspec)
{
	FncComboBoxCategory *self = FNC_COMBO_BOX_CATEGORY (object);
	
	switch (property_id)
	{
		case PROP_BUTTON_ADD_SUBCATEGORY:
			g_assert (self->button_add_subcategory == NULL);
			self->button_add_subcategory = g_value_get_object (value);
			break;
		case PROP_COMBO_BOX_SUBCATEGORY:
			g_assert (self->combo_box_subcategory == NULL);
			self->combo_box_subcategory = g_value_get_object (value);
			break;
		case PROP_POPOVER_SUBCATEGORY:
			g_assert (self->popover_subcategory == NULL);
			self->popover_subcategory = g_value_get_object (value);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
			break;
	}
}

static void
fnc_combo_box_category_get_property (GObject		*object,
				     guint		property_id,
				     GValue		*value,
				     GParamSpec		*pspec)
{
	FncComboBoxCategory *self = FNC_COMBO_BOX_CATEGORY (object);
	
	switch (property_id)
	{
		case PROP_BUTTON_ADD_SUBCATEGORY:
			g_value_set_object (value, self->button_add_subcategory);
			break;
		case PROP_COMBO_BOX_SUBCATEGORY:
			g_value_set_object (value, self->combo_box_subcategory);
			break;
		case PROP_POPOVER_SUBCATEGORY:
			g_value_set_object (value, self->popover_subcategory);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
			break;
	}
}

static void
fnc_combo_box_category_init (FncComboBoxCategory *self)
{
	gtk_widget_init_template (GTK_WIDGET (self));
	
	/* Create Table */
	fnc_model_category_create_table ();
}

static void
fnc_combo_box_category_class_init (FncComboBoxCategoryClass *klass)
{
	GObjectClass	*object_class = G_OBJECT_CLASS (klass);
	GtkWidgetClass	*widget_class = GTK_WIDGET_CLASS (klass);
	
	object_class->set_property = fnc_combo_box_category_set_property;
	object_class->get_property = fnc_combo_box_category_get_property;
	
	obj_properties[PROP_BUTTON_ADD_SUBCATEGORY] = g_param_spec_object ("button_add_subcategory",
									   "ButtonAdd of the ComboBoxCategory",
									   "The Button Add of the ComboBoxCategory",
									   GTK_TYPE_MENU_BUTTON,
									   G_PARAM_READWRITE);
	
	obj_properties[PROP_COMBO_BOX_SUBCATEGORY] = g_param_spec_object ("combo_box_subcategory",
									  "ComboBoxSubcategory of the ComboBoxCategory",
									  "The Combo Box Subcategory of the ComboBoxCategory",
									  FNC_TYPE_COMBO_BOX_SUBCATEGORY,
									  G_PARAM_READWRITE);
	
	obj_properties[PROP_POPOVER_SUBCATEGORY] = g_param_spec_object ("popover_subcategory",
									"PopoverSubcategory of the ComboBoxCategory",
									"The Popover Subcategory of the ComboBoxCategory",
									FNC_TYPE_POPOVER_SUBCATEGORY,
									G_PARAM_READWRITE);
									
	
	g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);

	gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/finance/combo-box-category.ui");

	gtk_widget_class_bind_template_child (widget_class, FncComboBoxCategory, liststore);	
	
	gtk_widget_class_bind_template_callback (widget_class, on_combo_box_category_changed);
}

FncComboBoxCategory*
fnc_combo_box_category_new (void)
{
	return g_object_new (FNC_TYPE_COMBO_BOX_CATEGORY, NULL);
}

void
fnc_combo_box_category_update (FncComboBoxCategory *self)
{
	fnc_model_category_list (self->liststore);
}
/* 
 * Copyright 2019 Danilo Fernandes Galete
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "fnc-popover-category.h"

struct _FncPopoverCategory{

	GtkPopover		parent;
	
	/* The Widgets */
	GtkEntry		*entry_name;
	GtkButton		*button_add;
	GtkLabel		*label_inf;
	
	/* The Parameters */
	FncComboBoxCategory	*combo_box_category;
 };

enum{
	PROP_0,
	PROP_COMBO_BOX_CATEGORY,
	N_PROPERTIES
};

static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (FncPopoverCategory, fnc_popover_category, GTK_TYPE_POPOVER);

static void
on_popover_category_clean (FncPopoverCategory *self)
{
	gtk_entry_set_text (GTK_ENTRY (self->entry_name), "");
	
	gtk_widget_set_visible (GTK_WIDGET (self->label_inf), FALSE);
	
	gtk_popover_popdown (GTK_POPOVER (self));
}

static void
on_button_add_clicked (GtkButton	*button,
		       gpointer		user_data)
{
	FncPopoverCategory		*self;
	gchar				*normalized_name;
	
	self = FNC_POPOVER_CATEGORY (user_data);
	
	normalized_name = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (self->entry_name))));
	
	if (strlen (normalized_name) > 0)
	{
		if(!fnc_model_category_insert (normalized_name))
		{
			
		}
		else
		{
			fnc_combo_box_category_update (FNC_COMBO_BOX_CATEGORY (self->combo_box_category));
		
			on_popover_category_clean (self);
		}
								
	}
	else
	{
		gtk_widget_set_visible (GTK_WIDGET (self->label_inf), TRUE);
		gtk_entry_set_text (GTK_ENTRY (self->entry_name), "");
	}
	
	g_free (normalized_name);
}

static void
fnc_popover_category_set_property (GObject      *object,
                                   guint        property_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
{
	FncPopoverCategory *self = FNC_POPOVER_CATEGORY (object);
  
	switch (property_id)
	{
		case PROP_COMBO_BOX_CATEGORY:
			g_assert (self->combo_box_category == NULL);
			self->combo_box_category = g_value_get_object (value);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
			break;
	}
}

static void
fnc_popover_category_get_property (GObject      *object,
                                   guint        property_id,
                                   GValue       *value,
                                   GParamSpec   *pspec)
{
	FncPopoverCategory *self = FNC_POPOVER_CATEGORY (object);
  
	switch (property_id)
	{
		case PROP_COMBO_BOX_CATEGORY:
			g_value_set_object (value, self->combo_box_category);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,pspec);
			break;
	}
}

static void
fnc_popover_category_init (FncPopoverCategory *self)
{
	gtk_widget_init_template (GTK_WIDGET (self));
}

static void
fnc_popover_category_class_init (FncPopoverCategoryClass *klass)
{
	GObjectClass    *object_class = G_OBJECT_CLASS (klass);
	GtkWidgetClass	*widget_class = GTK_WIDGET_CLASS (klass);
  
	object_class->set_property = fnc_popover_category_set_property;
	object_class->get_property = fnc_popover_category_get_property;
	
	obj_properties[PROP_COMBO_BOX_CATEGORY] = g_param_spec_object ("combo_box_category",
								       "ComboBoxCategory of the PopoverCategory",
								       "The Combo Box Category of the PopoverCategory",
								       FNC_TYPE_COMBO_BOX_CATEGORY,
								       G_PARAM_READWRITE);
	
	g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);
	
	gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/finance/popover-category.ui");
	
	gtk_widget_class_bind_template_child (widget_class, FncPopoverCategory, entry_name);
	gtk_widget_class_bind_template_child (widget_class, FncPopoverCategory, button_add);
	gtk_widget_class_bind_template_child (widget_class, FncPopoverCategory, label_inf);
	
	gtk_widget_class_bind_template_callback (widget_class, on_button_add_clicked);
	gtk_widget_class_bind_template_callback (widget_class, on_format_text);
	gtk_widget_class_bind_template_callback (widget_class, on_popover_category_clean);
}

FncPopoverCategory*
fnc_popover_category_new (void)
{
	return g_object_new (FNC_TYPE_POPOVER_CATEGORY, NULL);
}

This algorithm works just do not know if this problem solves this way

It’s kind of hard to read the code paste, since it’s not quite formatted properly :slight_smile: You can use triple backticks around your code to clear that up (eg. ```c CODE_HERE ```).

But if it works, it works. If I understand correctly, you’re just passing your combo box as a property to your button, then calling the setter function. That’s fine to do, but organizing things like that is more a matter of experience and preference, rather than the right/wrong way to do it :slight_smile:

One thing to note though, is as I mention above using GObject properties/GValue will usually result in a copy (in the case of a string) or a reference being added (in the case of a GObject), so you’ll want to make sure you’re dropping that reference later.

Thanks for the tip

So every time I give a g_object_set of the same object will a new reference be created in the memory?

And if it’s a gint you need to release the memory

It depends how you implement your property getters and setters, but yes generally strings will be copied (eg. g_strdup()), objects will get an extra ref (eg. g_object_ref()). Usually people will just allocate integer types on the stack, but you could allocate them if you wanted. Just notice that g_object_get() takes a pointer:

// Stack
int some_integer;
g_object_get (G_OBJECT (object), "some-integer", &some_integer);

// Heap
int *some_integer = g_malloc (sizeof (int));
g_object_get (G_OBJECT (object), "some-integer", some_integer);
g_free (some_integer);

However, there are more options as described here. You’ll just have to use your judgement based on the situation whether it’s safe to pass the “original” reference, or whether you want to take a copy. GValue can also work with pointers directly, if that’s convenient.

Thanks my friend for the explanation, I will check my code to implement the best possible solution.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.