GTK Develop Notes
This won’t help in a long, long visible future.
Table of Contents
- GTK Develop Notes
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 | Gtk::RadioButton b1("b1"), b2("b2"), b3("b3"); |
And another:
1 | Gtk::RadioButton::Group g; |
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 | UpBox.pack_start(RadioButton1); |
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 | Gtk::TreeModel::Row row = *(refCompletionModel->append()); |
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 | class ModelColumns : public Gtk::TreeModel::ColumnRecord { |
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 asset_icon_from_pixbuf()
orset_icon_from_icon_name()
. An application can respond to the
user pressing the icon by handling thesignal_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
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 | Glib::RefPtr<Gtk::TreeStore> refTreeStore = |
To add child row to a existing row, using:
1 | Gtk::TreeModel::iterator iter_child = |
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.
Menu Bar and Tool Bar
Actions
Bars & XML
Popup Menu
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 | Glib::RefPtr<Gtk::Builder> builder = |
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()
.