Glade-constructed TreeView with gtkmm

2019-07-04 13:58发布

I've got a glade-constructed TreeView/ListStore that I'm trying to load into an application and manipulate via gtkmm.

Here's the manager's class:

typedef struct
{
  Gtk::ListStore *liststore_info;
  Gtk::TreeModelColumn<string> *treeview_info_column_time;
  Gtk::TreeModelColumn<string> *treeview_info_column_message;
}UiElements;

class GuiManager
{
  Glib::RefPtr<Gtk::Builder> builder;
  UiElements elements;

  public:
    GuiManager();
    ~GuiManager();

    void info_handler(string msg);
}

And the implementation:

GuiManager::GuiManager()
{
  builder = Gtk::Builder::create();
  builder->add_from_file("GUI.glade");

  builder->get_widget("liststore_info", elements.liststore_info);
  builder->get_widget("treeview_info_column_time", elements.treeview_info_column_time);
  builder->get_widget("treeview_info_column_message", elements.treeview_info_column_message);
}

Here's the function I'm trying to call to manipulate the TreeView:

void GuiManager::info_handler(string msg)
{
  Gtk::TreeModel::Row row = *(elements.liststore_info->append());
  row[*(elements.treeview_info_column_time)] = "Now";
  row[*(elements.treeview_info_column_message)] = msg;
}

And finally, the relevant Glade XML:

<object class="GtkListStore" id="liststore_info">
  <columns>
    <!-- column-name Time -->
    <column type="string"/>
    <!-- column-name Message -->
    <column type="string"/>
  </columns>
</object>
<object class="GtkTreeView" id="treeview_info">
  <property name="visible">True</property>
  <property name="can_focus">True</property>
  <property name="model">liststore_info</property>
  <property name="enable_search">False</property>
  <property name="enable_grid_lines">both</property>
  <child internal-child="selection">
    <object class="GtkTreeSelection" id="treeview-selection2"/>
  </child>
  <child>
    <object class="GtkTreeViewColumn" id="treeview_info_column_time">
      <property name="resizable">True</property>
      <property name="sizing">autosize</property>
      <property name="min_width">100</property>
      <property name="title" translatable="yes">Time</property>
      <property name="clickable">True</property>
    </object>
  </child>
  <child>
    <object class="GtkTreeViewColumn" id="treeview_info_column_message">
      <property name="resizable">True</property>
      <property name="sizing">autosize</property>
      <property name="min_width">300</property>
      <property name="title" translatable="yes">Message</property>
      <property name="clickable">True</property>
    </object>
  </child>
</object>

The compilation fails, however, with the following:

In file included from /usr/include/gtkmm-3.0/gtkmm.h:119:0,
                 from GUI3_gui_manager.h:8,
                 from GUI3_gui_manager.cpp:1:
/usr/include/gtkmm-3.0/gtkmm/builder.h: In instantiation of ‘void Gtk::Builder::get_widget(const Glib::ustring&, T_Widget*&) [with T_Widget = Gtk::TreeModelColumn<std::basic_string<char> >]’:
GUI3_gui_manager.cpp:64:86:   required from here
/usr/include/gtkmm-3.0/gtkmm/builder.h:628:93: error: ‘get_base_type’ is     not a member of ‘Gtk::TreeModelColumn<std::basic_string<char> >’
     widget = dynamic_cast<T_Widget*>(this->get_widget_checked(name, T_Widget::get_base_type()));
                                                                                             ^

I'm obviously misusing TreeModelColumn, but my source tutorial for this method (which as thus far proven reliable) does things in a similar fashion, so I'm at a loss for the correct method here.

Any help is appreciated. =)

2条回答
劫难
2楼-- · 2019-07-04 14:22

You generally can't define the TreeModel (ListStore, TreeStore) in Glade when using gtkmm. The C (as used in Glade) and C++ types are just too different and the statically-typed C++ API can't know about your glade file's TreeModel definition at compile time.

Also, in your code, your treeview_info_column_time in your C++ code is a Gtk::TreeModelColumn<>. Your treeview_info_column_time in your Glade file is a GtkTreeViewColumn. Model columns and View columns are very different things. You can just use get_widget() to turn one into the other.

You are also trying to use get_widget() to get your liststore_info TreeModel (ListStore). But a TreeModel is not a widget. get_object() might work, but I just wouldn't bother trying to define the model in Glade. The tutorial that you link to doesn't do this either.

查看更多
趁早两清
3楼-- · 2019-07-04 14:37

I've just worked out how to do this. It's slightly dodgy as you have to create a C++ class that matches the types in your .glade file, but providing they do match, you get the same type-safe functions as you do when creating the TreeView manually.

class Cols: public Gtk::TreeModel::ColumnRecord {
    public:
        Cols() {
            // This order must match the column order in the .glade file
            this->add(this->colA);
            this->add(this->colB);
            this->add(this->colC);
        }

        // These types must match those for the model in the .glade file
        Gtk::TreeModelColumn<Glib::ustring> colA;
        Gtk::TreeModelColumn<Glib::ustring> colB;
        Gtk::TreeModelColumn<Glib::ustring> colC;
};

...

// Get hold of the TreeView from the .glade file
Gtk::TreeView* tv = nullptr;
refBuilder->get_widget("treeview1", tv); // refBuilder is Gtk::Builder instance
assert(tv);

// Get hold of the ListStore model from the .glade file
auto items = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(
    refBuilder->get_object("listmodel1")
);
assert(items);

Cols mycols;

// Populate tree view with items
auto row = *(items->append());
row[mycols.colA] = "Row 1 Col 1";
row[mycols.colB] = "Row 1 Col 2";

row = *(items->append());
row[mycols.colA] = "Row 2 Col 1";

This works by Cols::Cols() adding the type-safe columns in the same order as the .glade file, so the index stored in each of the colX variables matches the data type in the corresponding index in the model. Just don't forget to update the code if you ever reorder the columns! You can even add some items to the TreeView within Glade, then append more items in code and they will all show up together at runtime!

From reading the docs it looks like you could also use the type-unsafe set_value() function if you wanted to avoid creating the Cols class entirely, like this: (untested)

row.set_value(0, "value for first column");
row.set_value(1, "value for second column");

The docs warn that a crash is likely to result if the data type doesn't match the expected column format, so going to the effort of creating a class like Cols is probably worth it.

查看更多
登录 后发表回答