This won’t help in a long, long visible future.

Table of Contents

  1. GTK Develop Notes
    1. Hello World
    2. Build
    3. Basic
    4. Buttons
      1. Gtk::Button
      2. Gtk::ToggleButton
      3. Gtk::CheckButton
      4. Gtk::RadioButton
    5. Range Widgets
    6. Miscellaneous Widgets
      1. Gtk::Label
      2. Gtk::Frame
      3. Gtk::Entry
        1. Simple Entry
        2. Entry Completion
        3. Entry Icon
        4. Entry Progress Bar
      4. Gtk::SpinButton
      5. Gtk::ProgressBar
      6. Gtk::InfoBar
      7. Tooltips
    7. Container Widgets
      1. Single-Item Widgets (or fixed number Items)
        1. Gtk::Frame
        2. Gtk::Paned
        3. Gtk::ScrolledWindow
        4. Gtk::AspectFrame
        5. Gtk::Alignment
      2. Multi-Item Widgets (or dynamic Items)
        1. Packing
        2. Gtk::Box
          1. Per child adding options
          2. Pre-container adding options
        3. Gtk::ButtonBox
        4. Gtk::Grid
        5. Gtk::Notebook
        6. Gtk::Assistant
    8. The TreeView widget
      1. Model
        1. ListStore
        2. TreeStore
      2. View
      3. Selection
      4. Sorting
      5. Drag and Drop
      6. Right-click Popup Menu
    9. Combo Box
    10. Text View
    11. Menu Bar and Tool Bar
      1. Actions
      2. Bars & XML
      3. Popup Menu
    12. Gio::Resource and glib-compile-resources
    13. Tool Palette
    14. Adjustment
    15. Widgets without X-Windows
    16. Dialog
    17. Drawing Area
      1. Cairo API
    18. Using Glade
      1. Initializing
      2. Accessing widgets
      3. Derived widgets

GTK Develop Notes

Hello World

Today I began to learn a bit of GTK+ Development with C++, this is a note while learning.

Build

Install libgtkmm-3.0-dev.

Learn to use make.

Basic

First declare a basic application body of type Glib::RefPtr<Gtk::Application>, a smart-pointer, or auto instead:), and with constructor Gtk::Application::create(argc, argv, "org.gtkmm.example"). Notice that argv must not be const.

  • [ ] To Do: Get to know about the third parameter.

Then declare a window MyWindow, maybe other containers in the future. As for now, we often define a sub class of Gtk::Window to deal the work.

Return app->run(Window) at the end of main function.

Buttons

Gtk::Button

Set a button’s label with constructor like MyButton = Gtk::Button("Label") or later with MyButton.set_label("Label"). To add label with pictures, use MyButton.add_pixlabel("name.xpm", "textlabel"), there will be a label made of an icon followed with text.

Define an accelerator key for keyboard navigation with a underline before label, and a true second parameter, like: MyButton = Gtk::Button("_Label", true).Though I haven’t known about how to set key bindings yet.

  • [ ] To Do: Get to know about accelerator key.

In an window, use add(Gtk::Button MyButton)to add the button to it. And call MyButton.show() to show the button. To display all elements in the window, call show_all_children().

And to deal the events, we should know the concept of signal, it isn’t strange to C++ programmers. The Gtk::Button provides five signals but only clicked is recommended now.

  • [x] To Do: Get to know about Gtk::Widget::signal_button_press_event() and others.

To connect the signal emitted by the button and our handler, we should use MyButton.signal_clicked().connect(sigc::mem_fun(*this, &Buttons::ButtonClicked)). In this example, we have a class Buttons : Gtk::Window, and the handler ButtonClicked is a member function, of course MyButton is a member of this class with type Gtk::Button .

mem_fun means the handler is a member of the first parameter. If the handler is an independent function, use sigc::ptr_fun(&Func).

To deal other signals, like press, first we should let the window to report this signal. That is Gdk::Window::set_events(EventMask mask). For instance, in MyWindow, execute set_events(Gdk::BUTTON_PRESS_MASK) will let the window report all press signals. Then the button pressed will be able to emit signals. And the Slot Prototype is a boolean function with a parameter GdkEventButton, see the official document.

Another question: the relationship between Gtk::Window and Gdk::Window. Seems that a Gtk::Window can register with a Gdk::Window and then receive events like signal_button_press_event(). The usage of this signal might be the same as clicked? And we should know that other 4 signals can be only detected in the X Window System.

Sometimes we may just press enter to activate a button, that’s a default button, use: MyButton.set_can_default(bool = true) to ensure it can be a default button, and use MyButton.grab_default() to make it default. focus is another property like this.

We can set_sensitive(boolean) to decide whether the button will response to our click, and it’s appearance will follow the sensibility(valid / invalid).

Gtk::ToggleButton

To get the status of a toggle button use get_active() which returns a boolean value. If want to force the state of button, use set_active(), however it is not recommended for a clicked signal might be emitted. To change the button’s state, just use toggled(), which emitted toggled signal instead of clicked.

It is more like a base class for other types of buttons.

Gtk::CheckButton

Usage is the same as Toggle Button, with difference in appearance.

Gtk::RadioButton

Some Radio Button gather together as a group, and at the same time only one in the group will be activated. There are 2 ways to set the group.

1
2
3
Gtk::RadioButton b1("b1"), b2("b2"), b3("b3");
b2.join_group(b1);
b3.join_group(b1);

And another:

1
2
3
Gtk::RadioButton::Group g;
Gtk::RadioButton* b1 = Gtk::make_managed<Gtk::RadioButton>(g, "b1");
//other two are the same.

All Radio Buttons are off at first, so we should call Button1.set_active(), to make one activated.

To align several Radio Buttons, use Gtk::Box. Like:

1
2
3
UpBox.pack_start(RadioButton1);
UpBox.pack_start(RadioButton2);
UpBox.pack_start(RadioButton3);

Box can do many other things.

Range Widgets

There are 2 main kind of range widgets, scale and scrollbar. Both widgets need an adjustment to make effect. To construct a scale. use MyScale(MyAdjustment, Gtk::ORIENTATION_VERTICAL), scrollbar doesn’t need the second parameter, and for scale, the second can be replaced by horizontal. Once they(adjustments) are changed, they emit a signal_value_changed() signal, and we can get their current value using get_value(), which returns a double, to deal other things.

Let’s check some properties of the them.

set_digits(int): range from(possibly) 0 to 5, set the number of decimals after the dot.

set_value_pos(Gtk::PositionType): set where to show the value of scales.

set_draw_value(bool): set whether to show the value or not.

And now take a look at constructor of an adjustment:

Value: the initial value of the scale.

lower, upper: the range of scale’s value. upper exclusive, lower inclusive.

step_increment: the $\Delta$, as we often use, when we press up or down.

page_size: page is a abstract concept, we should know that the actual upper is upper - page_size.

page_increment: the $\Delta$ when press Pg-up or Pg-Dn, usually the same as page_size.

Thus there’re 2 more method to adjust properties: set_page_size(double) and set_page_increment(double).

Notice again that the changed signals are emitted by adjustments rather than scales.

We often use Box in these sections, a box can be generated by setting orientation(vertical or horizontal) and the padding. To put things(child boxes, buttons, etc) into it, use MyBox.pack_start(Things), they will be aligned by the way we set.

Miscellaneous Widgets

Gtk::Label

Label is used to show non-editable text. It support \n to start a new line, and it is self-justed.

We can set the way it just, or set simple format, using set_text(), set_mark_up(), set_justify(), set_line_wrap() and so on.

Gtk::Frame

A frame can be filled with label(s?) (and other?), with a title. We can set its title with constructor, and use Frame1.add(Label1) to add label to it.

Gtk::Entry

Entry is like a Text box(?) in other GUI languages, where you can type(maybe not if it is set as non-editable).

Simple Entry

It has max_length, text, editable, visibility(used for password), and they can be set and get.

We can also set select_region(int lower, int upper), to make text in the entry from lower to upper selected.

get_text_length() can counts how many characters there are now.

Once the text is changed, the entry will emit a changed signal. And when a entry is chosen, it emits an activate signal. Also, there’s a focus_out_event signal when focus leaves.

Entry Completion

To use a drop down menu and complete the entry, we shall define an Gtk::EntryCompletion, using Gtk::EntryCompletion::create(), may be a smart-pointer. Then set the completion for out entry by MyEntry.set_completion(completion).

To build a drop menu, we may use a Gtk::TreeModel.

In brief, we should define a class ModelColumns : public Gtk::TreeModel::ColumnRecord. And define an instance of the class, MyColumn. Then create a list to fill the completion, by auto refCompletionModel = Gtk::ListStore::create(MyColumn), and completion->set_model(refCompletionModel).

If we want to use our own matching method instead of a default one, call completion->set_match_func(sigc::mem_fun(*this, &exampleWindow::CompletionMatching)). Just like set a signal handler.

To add record to the list, using:

1
2
3
Gtk::TreeModel::Row row = *(refCompletionModel->append());
row[MyColumn.ColumnId] = 1; // 2, 3, 4, ...
row[MyColumn.ColumnName] = "Name Name" // Alice, Bob, ...

Seems it overrides the operator[] to reach the records at the end of list just using members of that instance.

If we don’t set a matching function, we should set a default column to look for a match, like: completion->set_text_column(MyColumn.ColumnName). It can even represent the kind of record columns!

For further use, we have a way to insert actions at the bottom of completion. That’s completion->insert_action_text(title, positon), when the title action is activated, it emits a signal with an integer to handle, which is just the position. To check the validity of the action, we can use a map to store them. It can be replaced by other data structures I guess.

In the end let’s see how to implement the sub class:

1
2
3
4
5
6
7
8
9
10
11
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
public:
ModelColumns() {
add(ColumnId);
add(ColumnName);
}
Gtk::TreeModelColumn<unsigned int> ColumnId;
Gtk::TreeModelColumn<Glib::ustring> ColumnName;
};

ModelColumns MyColumn;

Entry Icon

An Entry widget can show an icon at the start or
end of the text area. The icon can be specifed by methods such as
set_icon_from_pixbuf() or
set_icon_from_icon_name(). An application can respond to the
user pressing the icon by handling the
signal_icon_press signal.

From developer.gnome.org

Entry Progress Bar

A progress bar can be shown under the text and in the entry area, by calling set_progress_fraction(double). To make it update periodically, we can handle the signal Glib::signal_timeout(). Notice that besides the usual sigc parameter, there’s a period parameter count in ms.

Gtk::SpinButton

Like a scale, it works with an adjustment with value, lower, upper, step_increment, and page_increment, but no page_size.

Other properties are like entry and scale.

  • [ ] To Do: Get to know about the acceleration.

Gtk::ProgressBar

It can be update like the progress bar in the entry, however another method pulse() that the entry doesn’t have is provided to display the process of work that cannot be calculated as value. Of course we can set_pulse_step(). They work under activity mode.

And the orientation and text above the bar can both be set, using set_halign() and set_valign().

Gtk::InfoBar

Info bar can jump out and hide so it needs a dynamic container. We can let the info bar apply for a content area, and define a container, then add a Label to it. It can emit a signal_response() (when widgets in it emit another signal?). Usually we hide it, and it can be activated by show(); We can also set the kind of the info bar, for example, Gtk::MESSAGE_INFO by MyInfoBar.set_message_type().

Tooltips

It’s a small frame with texts, sometimes with images besides, which will appear for a few seconds over current widget to show information or help.

Check the example at developer.gnome.org to know how to write a Tool-tips, which can catch the position of cursor and appear at the appropriate position.

Container Widgets

Such as Gtk::Grid or Gtk::Frame.

Single-Item Widgets (or fixed number Items)

Some kinds, some usages. Like frame, paned, scrolled-window, etc.

Gtk::Frame

A frame is like a rectangle and there’s one and only one item in it. To set the title of frame, using MyFrame_set_label("string"). And the title (or label)’s position can be set through set_label_align, the orientation notations is in API Reference. There’s also set_shadow_type() to control the style of frame. About adding widget to a frame, we have talked before.

Gtk::Paned

A paned has two parts and we can drag the middle space between them to adjust the size of each. It has add1() and add2() to insert widgets.

Gtk::ScrolledWindow

An important property of scroll window is whether the scroll bar is always shown.

set_policy() controls the behaviors of bars, with two parameters for horizontal and vertical. And the only valid value of parameter is Gtk::POLICY_AUTOMATIC and Gtk::POLICY_ALWAYS.

Gtk::AspectFrame

A Frame with fixed ratio of length and width. Check Reference for more information.

Gtk::Alignment

A abstract container which can set the orientation of widget in it, like Gtk::ProgressBar.

Multi-Item Widgets (or dynamic Items)

Some kinds, some usages. Like Grid, Notebook, Assistant, Box, etc.

Packing

Packing is not a container, it’s a way to arrange widgets in a container, like 响应式开发 in other GUI designing and developing progress.

Gtk::Box

Per child adding options

The position(from the beginning or the end), ways of adjustment(no spare spaces, spaces between child widgets and adjusting the size of child widgets) and extra border area of a child widget can all be set, by MyBox.pack_atart(Widget&, PackOptions, padding), or pack_end() instead.

Pre-container adding options

The orientation(horizontal and vertical), spaces between widgets and whether all children are of the same size can be set, with constructor, set_spacing(int) and set_homogeneous(bool).

What’s the difference between spacing (set when the box is created)
and padding (set when elements are packed)? Spacing is added between
objects, and padding is added on either side of a widget.

From developer.gnome.org

Gtk::ButtonBox

Using add() to add buttons to it and using set_layout(ButtonBoxStyle) and set_spacing(int) to change its appearances.

Gtk::Grid

Grid has simple method add(), or we can let a widget span multiple rows and columns using attach() and attach_next_to().

To let all rows or columns be of the same height or width, call set_row_homogeneous() and set_column_homogeneous().

You can set the margin and expand properties of the
child Widgets to control their spacing and their behavior when the Grid is resized.

From developer.gnome.org

sigc::bind is now often appears in signal handling, it is use to bind our signal handler(mem_fun() or ptr_fun()) with more information as a parameter to the real handler. The usage is signal.connect(sigc::bind(sigc::mem_fun(object, &func), typename data)).

Gtk::Notebook

Notebook is a window with multiple tabs. Using MyNoteBook.append_page(Widget Content, ustring tab) to add a new page. Method prepend_page() and insert_page() are also provided.

When current page is switched, the notebook emits a signal_switch_page(), with the page contents and page id.

We can reach the contents using get_current_page() and get_nth_page(), and force the selected page by set_current_page().

Gtk::Assistant

Assistant divides complicated work into several steps, we can set pages like Gtk::Notebook. It can emit cancel signal and apply signal when corresponding button is clicked. Closed signal is emitted when the assistant is closed. And the prepare signal is emitted before each page will be shown.

For more usage check developer.gnome.org.

The TreeView widget

Model

Model stores the data which is displayed by View. ListStore and TreeStore are normally used for Model.

ListStore

A structure without child row, so we will focus on tree store which is beyond the list.

TreeStore

Derive from Gtk::TreeModelColumnRecord, a class ModelColumns is used to specify data types of a row. In the constructor of the class, we call add() to insert a instance of the Model data type(Gtk::TreeModelColumn<typename> data), which is also a member of the class. Then create a instance of our class(ModelColumns MyColumns).

When declare a TreeStore, pass our instance to the Gtk::TreeStore::create(). Then we can conveniently reach the contents, through the override operator[]. The specific way is:

1
2
3
4
5
6
7
Glib::RefPtr<Gtk::TreeStore> refTreeStore = 
Gtk::TreeStore::create(MyColumn);
// do something
Gtk::TreeModel::iterator iter = refTreeStore->append();
Gtk::TreeModel::Row MyRow = *iter;
MyRow[MyColumns.data] = 1;
typename MyData = MyRow[MyColumns.data];

To add child row to a existing row, using:

1
2
Gtk::TreeModel::iterator iter_child = 
refTreeStore->append(MyRow.children());

If we do not want some of the data to be shown, just don’t add it to the View.

View

To set which model to be shown, using MyTreeView.set_model(refTreeStore). Or specify it in constructor.

To set which column of the model to be shown, using append_column("Label", instance.data_instance) in order.

For usual types like string, number and boolean, they will be convert to entries and check buttons to display. As for others, we should wither connect set_cell_data_func() with our callback to convert them into string representation, or derive our own CellRenderer. See Reference for more information.

The View supports more than one Model to be shown, and the cells are also editable, with automatically-saved or our own logic. Besides, the Model can be iterated over, like a set or vector in C++.

Selection

Single, multiple selection. Get current selected rows. Changed signal. Rows cannot be selected. Force selected rows.

Sorting

Sorted by specific column. Multiple views of the same model sorted by different columns.

Drag and Drop

Reorder-able rows and special situations.

Override virtual method to control more complex situations.

Right-click Popup Menu

handling button_press_event signal to deal with popup context menu.

About the menu, there’e more details in menus chapter.

Combo Box

Drop down menu box.

Text View

large text box with a model-view structure.

Actions

Bars & XML

Gio::Resource and glib-compile-resources

Store the .glade, xml, images as file but convert them to binary data as parts of the application when linking.

Tool Palette

Like a tool bar, but contains tool items categorized into groups.

It supports drag and drops.

Adjustment

Most adjustable widgets have an adjustment to react to the value changes easily.

Widgets without X-Windows

They are used to align other elements.

Using event box including them to deal the event.

Dialog

Some types of dialogs and design own dialogs.

Drawing Area

Cairo API

Using Glade

Initializing

1
2
Glib::RefPtr<Gtk::Builder> builder = 
Gtk::Builder::create_from_file("basic.glade")

We can also pass a second parameter to instantiate just one window.

Accessing widgets

Define a pointer with the type we want, then call builder->get_widget("name_in_glade", pointer). If the widget doesn’t exist in the glade file, the pointer will be set to nullptr.

Derived widgets

Derive class from base class and call get_widget_derived().