I'm new to GTK and C. I started making UI for the embedded system. The interface should represent several tabs with information, switching between which is carried out by buttons on the device itself or by JS commands from a paired device (server). I am trying to implement this with GTK and Glade. I got a problem while using GtkStack. I need to pass the name of the stack page to the server so that the server sends back the information that will be displayed on the page.
I tried to get the Name of the page from the glade file using the function gtk_widget_style_get, but the compiler issues a warning ../gtk+-3.24.27/gtk/gtkwidget.c:13324: widget class 'GtkStack' has no property named 'title' and the program displays "garbage" instead of the page name. I also tried to get the "label" of the button. gtk_widget_style_get(btn_1, "title", &temp, NULL) The result was the same.
Here is the test code I am experimenting with
main.c
#include <gtk/gtk.h>
#define UI_FILE "for_test.glade"
GtkBuilder *builder;
GtkWidget *window;
GtkWidget *stack;
GtkWidget *fix_1;
GtkWidget *lbl_1;
GtkWidget *btn_1;
GtkWidget *fix_2;
GtkWidget *lbl_2;
GtkWidget *btn_2;
void
on_btn_1_clicked(GtkWidget *widget)
{
gchar *temp[100];
gtk_widget_style_get(stack, "title", &temp, NULL);
gtk_label_set_text(GTK_LABEL(lbl_1), *temp);
}
void
on_window_destroy()
{
gtk_main_quit();
}
int
main (int argc, char *argv[])
{
gtk_init(&argc, &argv);
builder = gtk_builder_new_from_file(UI_FILE);
window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
stack = GTK_WIDGET(gtk_builder_get_object(builder, "stack"));
fix_1 = GTK_WIDGET(gtk_builder_get_object(builder, "fix_1"));
lbl_1 = GTK_WIDGET(gtk_builder_get_object(builder, "lbl_1"));
btn_1 = GTK_WIDGET(gtk_builder_get_object(builder, "btn_1"));
fix_1 = GTK_WIDGET(gtk_builder_get_object(builder, "fix_1"));
lbl_2 = GTK_WIDGET(gtk_builder_get_object(builder, "lbl_2"));
btn_2 = GTK_WIDGET(gtk_builder_get_object(builder, "btn_2"));
g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL);
g_signal_connect(btn_1, "clicked", G_CALLBACK(on_btn_1_clicked), NULL);
//g_signal_connect(btn_2, "clicked", G_CALLBACK(on_btn_2_clicked), NULL);
gtk_builder_connect_signals(builder, NULL);
g_object_unref(builder);
gtk_widget_show(window);
gtk_main();
return 0;
}
for_test.glade
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkWindow" id="window">
<property name="can-focus">False</property>
<property name="default-width">100</property>
<property name="default-height">200</property>
<signal name="destroy" handler="on_window_destroy" swapped="no"/>
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkFixed" id="fix_1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="btn_1">
<property name="label" translatable="yes">button 1</property>
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_btn_1_clicked" swapped="no"/>
</object>
<packing>
<property name="y">100</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lbl_1">
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">label</property>
</object>
</child>
</object>
<packing>
<property name="name">page0</property>
<property name="title" translatable="yes">page0</property>
</packing>
</child>
<child>
<object class="GtkFixed" id="fix_2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="btn_2">
<property name="label" translatable="yes">button 2</property>
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_btn_2_clicked" swapped="no"/>
</object>
<packing>
<property name="y">100</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lbl_2">
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">label</property>
</object>
</child>
</object>
<packing>
<property name="name">page1</property>
<property name="title" translatable="yes">page1</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
I suspect that I am using the wrong function to get the properties of the stackab page, but I haven't found another way to get them. Where did I go wrong?
Here is one of the solutions:
GValue k = {0,};
g_value_init(&k, G_TYPE_STRING);
gtk_container_child_get_property(GTK_CONTAINER(stack), fix_1, "name", &k);
g_message("%s", g_value_get_string(&k));
Related
As I am new to glade and C, I am trying to build a very simple GUI which has an entry box a label and a button. When the button is pressed whatever values present in the text box will we show it in label. This is my glade,
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="windows1">
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkFixed">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="button1">
<property name="label" translatable="yes">button</property>
<property name="width_request">100</property>
<property name="height_request">80</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_clicked" swapped="no"/>
</object>
<packing>
<property name="x">182</property>
<property name="y">146</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry1">
<property name="width_request">100</property>
<property name="height_request">80</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="x">68</property>
<property name="y">45</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="width_request">100</property>
<property name="height_request">80</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">label</property>
</object>
<packing>
<property name="x">321</property>
<property name="y">44</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
And this is the C logic which on button press prints clicked in terminal.
#include <gtk/gtk.h>
void on_clicked(GtkButton * b, gpointer data)
{
(void)b;
(void)data;
printf("clicked");
}
int main (int argc, char *argv[])
{
GtkBuilder *builder;
GtkWidget *window;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file (builder, "example.glade", NULL);
window = GTK_WIDGET(gtk_builder_get_object(builder, "window1"));
gtk_builder_connect_signals(builder, NULL);
g_object_unref(builder);
gtk_widget_show(window);
gtk_main();
return 0;
}
// called when window is closed
void on_window_main_destroy()
{
gtk_main_quit();
}
I compiled the c code without any error using
gcc main.c $(pkg-config --cflags --libs gtk+-3.0) -o main -export-dynamic
But when I run I get this warning but no gui
Gtk-CRITICAL **: : gtk_widget_show: assertion 'GTK_IS_WIDGET (widget)' failed
How can I solve this problem? The second question is how can I fetch values from the text box and show it in label using c.
The error message you're getting is because your GtkWindow has id "windows1", while you're trying to fetch it using id "window1" (note that the former has an 's' in the middle, while the latter doesn't).
For your other question: you should fetch the GtkEntry widget (also using gtk_builder_get_object()) and save the resulting pointer somewhere. You can then fetch its contents once you are inside your callback.
I have made a glade interface where there are three expanders. I only want one to be open at any given time. So when the signal "activate" is used it calls a function expandlights() and all other expanders should close. I cant figure out how to affect the other expanders from the function. below was my best try, its clearly very wrong. I'm SO new to this.
MAIN:
int main(int argc, char *argv[])
{
GtkBuilder *builder;
GtkWidget *window;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file (builder, "interface.ui", NULL);
window = GTK_WIDGET(gtk_builder_get_object(builder, "window1"));
gtk_builder_connect_signals(builder, NULL);
g_object_unref(builder);
gtk_widget_show(window);
gtk_main();
return 0;
}
this is the function i attempted which clearly fails
void expandlights( GtkWidget *expander1,
GtkWidget *expander2,
GtkWidget *expander3)
{
gtk_expander_set_expanded(expander2, FALSE);
gtk_expander_set_expanded(expander3, FALSE);
}
I dont know if im on the right track or way off here. Could use a schoolin!
Glade file:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkExpander" id="expander1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="activate" handler="expandlights" swapped="no"/>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">lighting stuff</property>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Lights</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkExpander" id="expander2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="activate" handler="expandalarm" swapped="no"/>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Alarm stuff</property>
</object>
</child>
<child type="label">
Your activate handler must have the signature defined for the activate signal.
void
expandlights (GtkExpander *expander, gpointer user_data);
To be able to modify the other expanders from this function you'll need to use the user_data pointer: typically it's made to point to a struct that contains pointers to all widgets you may need. You can set the pointer value with gtk_builder_connect_signals() in your initialization when using glade if I recall correctly.
You should be able to use the same handler for all three expanders if you code it carefully (if the expander is being expanded then unexpand all of the expanders except the one that emitted the signal). Note that when you call gtk_expander_set_expanded() the activate signal handler for that expander will be called. This shouldn't be a problem for your case but keep it in mind -- don't create a never-ending expand loop.
Problem
I have a problem where closing a widget with the delete event, via gtk_widget_hide_on_delete fails to show the menu widget again via a swapped gtk_widget_show_all with the corresponding widget passed to it as an argument.
Background
I'm creating a program consisting of a selection menu with a number of button widgets. Pressing one button will hide the menu, and show a window widget containing a label and a TextEntry, the delete event of this window widget will hide the widget, and show the menu again.
Example program
I've created a minimal example for this problem. The code simply loads the ui via GtkBuilder and then enters the main gtk loop.
I rely on gtk_widget_show_all, gtk_widget_hide and gtk_widget_hide_on_delete, but when I close the exercise widget using the delete-event, the window disappears, but the menu widget isn't shown.
#include <gtk/gtk.h>
#include <stdio.h>
int main(int argc, char **argv)
{
GtkBuilder * builder;
GtkWidget * menu;
GError *error = NULL;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
if (!gtk_builder_add_from_file(builder, "example.glade", &error)) {
g_warning("%s", error->message);
g_free(error);
fprintf(stderr, "Failed to load build file");
}
menu = (GtkWidget *) gtk_builder_get_object(builder, "menu");
gtk_builder_connect_signals(builder, NULL);
gtk_widget_show(menu);
gtk_main();
return 0;
}
The XML file example.glade has been created using Glade.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkWindow" id="exercise">
<property name="can_focus">False</property>
<property name="window_position">center</property>
<signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
<signal name="delete-event" handler="gtk_widget_show_all" object="menu" after="yes" swapped="yes"/>
<child>
<object class="GtkGrid" id="grid2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEntry" id="entry1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="label" translatable="yes">A window where stuff will happen.</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkWindow" id="menu">
<property name="can_focus">False</property>
<property name="window_position">center</property>
<signal name="destroy" handler="gtk_main_quit" swapped="no"/>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="button1">
<property name="label" translatable="yes">button1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<signal name="clicked" handler="gtk_widget_hide" object="menu" swapped="yes"/>
<signal name="clicked" handler="gtk_widget_show_all" object="exercise" swapped="yes"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button2">
<property name="label" translatable="yes">button2</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<signal name="clicked" handler="gtk_widget_hide" object="menu" swapped="yes"/>
<signal name="clicked" handler="gtk_widget_show_all" object="exercise" swapped="yes"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
The problem is in these two lines of your Glade file:
<signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
<signal name="delete-event" handler="gtk_widget_show_all" object="menu" after="yes" swapped="yes"/>
Looking at the manual page of gtk_widget_hide_on_delete(), we see:
Utility function; intended to be connected to the “delete-event” signal on a GtkWindow. The function calls gtk_widget_hide() on its argument, then returns TRUE.
If we look at the delete-event signal, we see:
Returns
TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
This means that after the gtk_widget_hide_on_delete() is called, TRUE is returned, and this stops the GTK runtime from calling gtk_widget_show_all().
To achieve what you want, you can use a custom-defined handler, as shown below:
Remove that two lines and replace with the following:
<signal name="delete-event" handler="my_custom_func" object="menu" swapped="no"/>
Add your custom handler to the C file:
G_MODULE_EXPORT gboolean
my_custom_func(GtkWidget *w, GdkEvent *e, gpointer u) {
gtk_widget_hide(w);
gtk_widget_show_all(GTK_WIDGET(u));
return TRUE;
}
I notice that Glade only allows you to set an object to be passed within the user data part of a GTK callback.
Is there any way I can pass an integer value instead?
I have a set of menu items which I would like to point at the same callback function, however for a small section of the code I need to identify which menu item was the one which called the callback.
Note:
All my signals are setup automatically with glade_xml_signal_autoconnect, and I would perfer to keep it this way.
In my opinion with respect to glade and callbacks one have to say goodbye to the notion of passing just one single element to callbacks. And thus stop using glade in order to configure the user data that is passed to a specific callback.
I get used to pass a struct named App to all callbacks that contains a pointer to every ui element that is loaded from the ui definitions file and already instantiated by GtkBuilder. This also stops the tedious task in glade of clicking back and forth between widgets only to set up the user data of the callbacks. In addition to the ui elements, most of the time this struct contains further elements that are important at runtime on application level.
A benefit of this approach is that you are not tempted to think about how to implement an individual callback that should act upon more than one element which is often the case. Some people tackle this by grouping the widgets of interest into a container. In order to pass all widgets to the callback they just pass the container. Then in the callback they get the widgets by calling gtk_container_get_children or similar functions. This approach makes callbacks illegible and reduce the fun while editing the code.
If every callback has all elements available that should be manipulated at runtime you don't have to care about implementing a single callback since every callback shares the same structure.
Further i created a helper macro that defines a pointer to an already instantiated element with the name of its glade id. This way the elements definitions in the code are always in sync with those that are displayed in glade. That makes renaming of widgets very easy (substitute the name in all sources + the glade file).
To illustrate this approach i have appended files of a sample program. Don't be scared of the number of files. Dividing a program into logical units/modules makes programming much simpler. To get a quick view of the whole project i created a git repository on github:
https://github.com/o8i12398z12h9h/gtk-sample-app
Just compare my callbacks.c with your callback file(s). I would be interested to know how they compare with respect to legibility and structure and taking into account that you may have the element-ids from glade still in mind.
callbacks.c:
#include "app.h"
void
button1_clicked_cb (GtkButton * button, App * app)
{
GET_UI_ELEMENT (GtkEntry, entry1);
if (gtk_entry_get_text_length (entry1) == 0)
gtk_entry_set_text (entry1, "test");
else
gtk_entry_set_text (entry1, "");
}
void
button2_clicked_cb (GtkButton * button, App * app)
{
gboolean active;
GET_UI_ELEMENT (GtkSpinner, spinner1);
GET_UI_ELEMENT (GtkWidget, eventbox1);
g_object_get (G_OBJECT (spinner1), "active", &active,
NULL);
if (active) {
gtk_spinner_stop (spinner1);
gtk_widget_override_background_color (eventbox1,
GTK_STATE_FLAG_NORMAL,
app->
active_color);
}
else {
gtk_spinner_start (spinner1);
gtk_widget_override_background_color (eventbox1,
GTK_STATE_FLAG_NORMAL,
app->
inactive_color);
}
}
void
button3_clicked_cb (GtkButton * button, App * app)
{
GdkRGBA bg = { 0, 0, 1, 1 };
GET_UI_ELEMENT (GtkWidget, eventbox1);
gtk_widget_override_background_color (eventbox1,
GTK_STATE_FLAG_NORMAL,
&bg);
}
void
button4_clicked_cb (GtkButton * button, App * app)
{
const gchar *str;
GET_UI_ELEMENT (GtkLabel, label1);
str = gtk_label_get_text (label1);
if (strcmp (str, "label") == 0) {
gtk_label_set_text (label1, "NewText");
}
else {
gtk_label_set_text (label1, "label");
}
}
void
button5_clicked_cb (GtkButton * button, App * app)
{
GET_UI_ELEMENT (GtkWidget, button1);
GET_UI_ELEMENT (GtkWidget, button2);
GET_UI_ELEMENT (GtkWidget, button4);
g_signal_emit_by_name (button1, "clicked", app);
g_signal_emit_by_name (button2, "clicked", app);
g_signal_emit_by_name (button4, "clicked", app);
}
main.c:
#include "app.h"
int
main (int argc, char *argv[])
{
App *app;
app = (App *) g_new (App, 1);
gtk_init (&argc, &argv);
app_init (app);
GET_UI_ELEMENT (GtkWidget, window1);
gtk_widget_show_all (window1);
gtk_main ();
return 0;
}
app.c:
#include "app.h"
GObject *
app_get_ui_element (App * app, const gchar * name)
{
const gchar *s;
GSList *list;
list = app->objects;
do {
s = gtk_buildable_get_name (list->data);
if (strcmp (s, name) == 0) {
return list->data;
}
} while (list = g_slist_next (list));
return NULL;
}
void
app_init_colors (App * app)
{
GdkRGBA active_color = { 1, 0, 0, 1 };
GdkRGBA inactive_color = { 0, 1, 0, 1 };
app->active_color = g_new0 (GdkRGBA, 1);
app->inactive_color = g_new0 (GdkRGBA, 1);
app->active_color = gdk_rgba_copy (&active_color);
app->inactive_color = gdk_rgba_copy (&inactive_color);
}
void
app_init (App * app)
{
GError *err = NULL;
app->definitions = gtk_builder_new ();
gtk_builder_add_from_file (app->definitions,
UI_DEFINITIONS_FILE, &err);
if (err != NULL) {
g_printerr
("Error while loading app definitions file: %s\n",
err->message);
g_error_free (err);
gtk_main_quit ();
}
gtk_builder_connect_signals (app->definitions, app);
app->objects = gtk_builder_get_objects (app->definitions);
app_init_colors (app);
}
app.h:
#ifndef __APP__
#define __APP__
#include <gtk/gtk.h>
#define UI_DEFINITIONS_FILE "ui.glade"
#define GET_UI_ELEMENT(TYPE, ELEMENT) TYPE *ELEMENT = (TYPE *) \
app_get_ui_element(app, #ELEMENT);
typedef struct app_
{
GtkBuilder *definitions;
GSList *objects;
GdkRGBA *active_color;
GdkRGBA *inactive_color;
} App;
void app_init (App * );
GObject * app_get_ui_element (App * , const gchar * );
#endif
ui.glade:
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<property name="border_width">20</property>
<signal name="destroy" handler="gtk_main_quit" swapped="no"/>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">10</property>
<property name="column_spacing">20</property>
<child>
<object class="GtkSpinner" id="spinner1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkEventBox" id="eventbox1">
<property name="height_request">50</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="invisible_char_set">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">label</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="buttonbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">10</property>
<property name="layout_style">center</property>
<child>
<object class="GtkButton" id="button1">
<property name="label" translatable="yes">toggle entry</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="button1_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button2">
<property name="label" translatable="yes">toggle spinner + bg</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="button2_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button3">
<property name="label" translatable="yes">set bg</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="button3_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button4">
<property name="label" translatable="yes">toggle label</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="button4_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button5">
<property name="label" translatable="yes">toggle everything</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="button5_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
<property name="width">4</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="separator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
<property name="width">4</property>
<property name="height">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</interface>
I've been teaching myself how to code in C and utilize GTK for writing applications in Linux, using Glade to design the UI before implementing it. I've been trying to utilize a GtkTreeView coupled with a GtkListStore to display data, but I seem to be running into issues. I can get my application to launch fine and it shows the window and dialog correctly, but I cannot figure out how to add rows of data to the List Store programmatically and have them appear in the Tree View; most tutorials I find seem to prefer to make the rows in Glade, or to design the whole interface through the code. With the code as it is I receive no errors when running the program, but I cannot see anything being added in GtkTreeView. I don't see any blank rows being added either.
By writing some code to forcibly generate an error, I can see that the signal for postingButton is being connected (I think) so that should be fine. I'm just missing something in my actual implementation of GtkTreeView. If anyone can help me out, I'd greatly appreciate it!
Edit: The GtkEntries that are present will be implemented later; I'm just trying to get data to show up at all.
C Code:
/* I'd give objects better names normally, no worries!
* Just goofing around for now.*/
#include <gtk-3.0/gtk/gtk.h> // Needed for interface.
#include <gtk-3.0/gdk/gdk.h>
#include <glib-2.0/glib-object.h>
void on_mainWindow_destroy(GObject *object, gpointer user_data)
{
gtk_main_quit();
}
void on_stupidButton_clicked(GtkButton *button, gpointer *user_data)
{
gtk_dialog_run(GTK_DIALOG(user_data));
}
void on_postingButton_clicked(GtkButton *button, gpointer *user_data)
{
GtkListStore *liststore1;
GtkWidget *treeview1; // I've tried defining this as a GtkTreeView as well.
GtkTreeIter iter;
GtkBuilder *listStoreBuilder;
listStoreBuilder = gtk_builder_new();
gtk_builder_add_from_file(listStoreBuilder,
"../testing_interface.glade", 0);
treeview1 = GTK_WIDGET(gtk_builder_get_object(listStoreBuilder,
"treeview1")); // I'd change this to GTK_TREE_VIEW if the
// above is a GtkTreeView.
liststore1 = GTK_LIST_STORE(
gtk_tree_view_get_model((GtkTreeView *)treeview1));
gtk_list_store_append(liststore1, &iter);
gtk_list_store_set(liststore1, &iter, 0, "c", 1, "d", -1);
gtk_builder_connect_signals(listStoreBuilder, 0);
g_object_unref(G_OBJECT(listStoreBuilder));
g_object_unref(G_OBJECT(liststore1));
}
void on_cancelButton_clicked(GtkButton *button, gpointer *user_data)
{
gtk_widget_destroy((GtkWidget *)user_data);
}
int main(int argc, char *argv[])
{
GtkBuilder *builder;
GtkWidget *mainWindow, *dumbDialog;
GtkButton *stupidButton;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file(builder, "../testing_interface.glade",
0);
mainWindow = GTK_WIDGET(gtk_builder_get_object(builder, "mainWindow"));
stupidButton = GTK_BUTTON(gtk_builder_get_object(builder, "stupidButton"));
dumbDialog = GTK_WIDGET(gtk_builder_get_object(builder, "dumbDialog"));
gtk_builder_connect_signals(builder, 0);
g_object_unref(G_OBJECT(builder));
gtk_widget_show(mainWindow);
gtk_main();
return 0;
}
Glade File (testing_interface.glade):
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkDialog" id="dumbDialog">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="postingButton">
<property name="label" translatable="yes">Post</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_postingButton_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancelButton">
<property name="label" translatable="yes">Cancel</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_cancelButton_clicked" object="dumbDialog" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkEntry" id="entry1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="entry2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="0">postingButton</action-widget>
<action-widget response="0">cancelButton</action-widget>
</action-widgets>
</object>
<object class="GtkListStore" id="liststore1">
<columns>
<!-- column-name Testing -->
<column type="gchararray"/>
<!-- column-name Testing1 -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkWindow" id="mainWindow">
<property name="can_focus">False</property>
<signal name="destroy" handler="on_mainWindow_destroy" swapped="no"/>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkTreeView" id="treeview1">
<property name="height_request">183</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">liststore1</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="testing1Col">
<property name="title" translatable="yes">column</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="testing2Col">
<property name="title" translatable="yes">column</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext2"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="stupidButton">
<property name="label" translatable="yes">button</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_stupidButton_clicked" object="dumbDialog" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
Your code that appends rows to the tree view is basically okay, but several problems prevent it from working.
You are creating and populating a new builder on each entry to on_postingButton_clicked. This is obviously incorrect, because it means that you're creating a new tree and a new store on each click of the Post button. This is easily fixed by sending treeview1 as user_data to the on_postingButton_clicked callback. You do that already in dialog, so I simply modified <signal name="clicked" handler="on_postingButton_clicked" ...> in your XML to also include object="treeview1".
You are being too liberal with g_object_unref. gtk_tree_view_get_model doesn't increase the refcount of its model, so you shouldn't unref the liststore you get. Unrefing the builder in main is probably a bad idea if you want to pass it around and retrieve objects from it (and it's a singleton anyway).
You're supposed to #include <gtk/gtk.h>, without the intervening gtk-3.0 directory. If your compilation fails without that, you are probably not setting up pkg-flags correctly.
Here is a modified version of your code that works for me. It requires XML to be modified as described above.
/* compile with:
gcc -O2 -Wall glade-test.c $(pkg-config gtk+-3.0 gmodule-export-2.0 --cflags --libs) */
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib-object.h>
void on_mainWindow_destroy(GObject *object, gpointer user_data)
{
gtk_main_quit();
}
void on_stupidButton_clicked(GtkButton *button, gpointer *user_data)
{
gtk_dialog_run(GTK_DIALOG(user_data));
}
void on_postingButton_clicked(GtkButton *button, gpointer *user_data)
{
GtkTreeIter iter;
GtkTreeView *treeview1 = GTK_TREE_VIEW(user_data);
GtkListStore *liststore1 = GTK_LIST_STORE(gtk_tree_view_get_model(treeview1));
gtk_list_store_append(liststore1, &iter);
gtk_list_store_set(liststore1, &iter, 0, "c", 1, "d", -1);
}
void on_cancelButton_clicked(GtkButton *button, gpointer *user_data)
{
gtk_widget_destroy((GtkWidget *)user_data);
}
int main(int argc, char *argv[])
{
GtkBuilder *builder;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file(builder, "testing_interface.glade", 0);
gtk_builder_connect_signals(builder, 0);
gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "mainWindow")));
g_object_unref(G_OBJECT(builder));
gtk_main();
return 0;
}