2
votes

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>
1

1 Answers

2
votes

Your code that appends rows to the tree view is basically okay, but several problems prevent it from working.

  1. 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".

  2. 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).

  3. 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;
}