Presentation is loading. Please wait.

Presentation is loading. Please wait.

GTKmm Tutorial CS247 Spring 2018.

Similar presentations


Presentation on theme: "GTKmm Tutorial CS247 Spring 2018."— Presentation transcript:

1 GTKmm Tutorial CS247 Spring 2018

2 Topics Resources Environment Compiling & Linking Creating a Window
Window Layout Managers Frames & Panels Widgets Listeners Dialogs Menus & Toolbars Using glade Using valgrind

3 Resources How to use GTKmm: gtkmm.pdf Some general notes on using GTKmm: df GTKmm API documentation: GTKmm 3 reference manual: GTKmm 3 tutorial: GTKmm 3 examples mm-examples.zip mm-examples/MVC/MVC.zip

4 Resources (2) GNOME Human Interface guidelines GTK+/Glade
GTK+/Glade documentation/plain/examples/book/menus_and_toolbars/toolbar.glade?h=gtkmm

5 Environment Use linux.student.cs.uwaterloo.ca since has the GNU C++ compiler that is C++14 compliant (g++ –std=c++14) and the GTKmm 3.0 package installed. Enable X-forwarding by using command: ssh –X Can use dpkg -l "*gtkmm*" to see version of GTKmm package installed. Working from home: Windows: XMing + PuTTY + ssh -X Mac: XQuartz + ssh -X LINUX: ssh –X Can install on your home computer, but no guarantee that will be 100%- compatible with version in student environment. See installation.html.en Make sure you try your project out at least once in MC3022 since your project demos will be held there. MC2061 is a Ubuntu lab.

6 Compiling & linking When compiling and linking, must specifiy `pkg-config gtkmm cflags -- libs` as the last element of each command. In order to not forget, strongly encouraged to use provided sample Makefile. CXX = g++ -std=c++14 CXXFLAGS = -Wall -g -MMD GTKFLAGS = `pkg-config gtkmm cflags --libs` SOURCES = $(wildcard *.cc) OBJECTS = ${SOURCES:.cc=.o} DEPENDS = ${OBJECTS:.o=.d} EXEC=example $(EXEC): $(OBJECTS) $(CXX) $(CXXFLAGS) $(OBJECTS) -o $(EXEC) $(GTKFLAGS) %.o: %.cc $(CXX) -c -o $< $(CXXFLAGS) $(GTKFLAGS) -include ${DEPENDS} .PHONY: clean clean: rm -f $(OBJECTS) $(DEPENDS) $(EXEC)

7 Creating a window // ex1/main.cc #include <gtkmm/application.h> // Gtk::Application #include <gtkmm/window.h> // Gtk::Window #include <iostream> int main( int argc, char * argv[] ) { auto app = Gtk::Application::create( argc, argv, "GTKmm.Tutorial.Example1" ); Gtk::Window window; window.set_title( "Example 1" ); window.set_default_size( 500, 200 ); std::cout << "waiting for window to close" << std::endl; return app->run( window ); } // main Could include <gtkmm.h>, but that pulls in a *lot* of information. // Creates a Gtk::Application object initialized from argument list. Necessary in all gtkmm applications. // Passes in command-line arguments and application id. // application id must conform to rules listed in // For convenience, the restrictions on application identifiers are reproduced here: Application identifiers must contain only the ASCII characters "[A-Z][a-z][0-9]_-." and must not begin with a digit. Application identifiers must contain at least one '.' (period) character (and thus at least three elements). Application identifiers must not begin or end with a '.' (period) character. Application identifiers must not contain consecutive '.' (period) characters. Application identifiers must not exceed 255 characters. GTK::Application::create() // Creates a window, which is displayed via the run command. Gtk::Window window; // Set the default window size, width and height, in pixels. window.set_default_size( 500, 200 ); // Shows the window and enters the main gtkmm processing loop, which will finish when the window is closed. return app->run( window ); GTK::Application::run( int argc, char * argv[] ) Starts the application. The default implementation of this virtual function will simply run a main loop. It is an error to call this function if application is a proxy for a remote application. run( Window & ) If you call Gio::Application::quit() while a window is connected to the application, and then return from main() without removing the window from the application, the application's destructor will not be called. Parameters windowThe window to show. This method will return when the window is hidden. run(Window &, int argc, char * argv[] ) is also possible.

8 Creating your own window
// ex2/window.h #include <gtkmm/window.h> class MyWindow : public Gtk::Window { public: MyWindow(); }; // ex2/main.cc #include <gtkmm/application.h> #include "window.h" #include <iostream> int main( int argc, char * argv[] ) { auto app = Gtk::Application::create( argc, argv, "GTKmm.Tutorial.Example2" ); MyWindow window; std::cout << "waiting for window to close" << std::endl; return app->run( window ); } // main

9 Window managers Define a layout style for the window.
Java has a variety of layout managers, GTKmm doesn't. Instead, build by compositing existing "widgets". May find it useful to use Glade to generate the GUI, since GTKmm can import the .glade file to define the look of the interface, and then add the code to attach the listeners. GTKmm containers are defined to contain: single items e.g. Gtk::Frame, Gtk::Window, Gtk::Button multiple items e.g. Gtk::Paned, Gtk::Grid, Gtk::Box, Gtk::ButtonBox, Gtk::Notebook, Gtk::Assistant, Gtk::TreeView, Gtk::HeaderBar, Gtk::FlowBox, etc. Gtk::Frame can enclose one or a group of widgets within a box, optionally with a title. Gtk::Paned is divided into 2 halves, oriented either horizontally (default layout) or vertically (Gtk::Orientation::ORIENTATION_VERTICAL). The division between the two panes is adjustable by the user by dragging a handle. Child widgets are added to the panes of the widget with gtk_paned_pack1() and gtk_paned_pack2(). The division between the two children is set by default from the size requests of the children, but it can be adjusted by the user. A paned widget draws a separator between the two child widgets and a small handle that the user can drag to adjust the division. It does not draw any relief around the children or around the separator. (The space in which the separator is called the gutter.) Often, it is useful to put each child inside a GtkFrame with the shadow type set to GTK_SHADOW_IN so that the gutter appears as a ridge. No separator is drawn if one of the children is missing. Gtk::Grid arranges its child widgets in rows and columns. Use attach(), attach_next_to() and add() to insert child widgets. Gtk::Box arranges its child widgets vertically or horizontally. Use pack_start() and pack_end() to insert child widgets. Button boxes are a convenient way to quickly arrange a group of buttons. Their orientation can be either horizontal or vertical. ButtonBoxes help to make applications appear consistent because they use standard settings, such as inter-button spacing and packing. Buttons are added to a ButtonBox with the add() method. Button boxes support several layout styles. The style can be retrieved and changed using get_layout() and set_layout(). An Assistant splits a complex operation into steps. Each step is a page, containing a header, a child widget and an action area. The Assistant's action area has navigation buttons which update automatically depending on the type of the page, set with set_page_type(). The Gtk::TreeView widget can contain lists or trees of data, in columns.

10 Panes & frames // ex3/window.h #include <gtkmm/window.h> // Gtk::Window #include <gtkmm/frame.h> // Gtk::Frame #include <gtkmm/paned.h> // Gtk::Paned class MyWindow : public Gtk::Window { Gtk::Paned paned_; Gtk::Frame frame1_, frame2_; public: MyWindow(); ~MyWindow(); };

11 Panes & frames (2) // ex3/window.cc #include "window.h" MyWindow::MyWindow() : paned_{Gtk::Orientation::ORIENTATION_VERTICAL} { set_title( "Example 3" ); // Sets the window title. set_default_size( 500, 200 ); // Set default size, width and height, in pixels. set_border_width( 10 ); add( paned_ ); paned_.add1( frame1_ ); paned_.add2( frame2_ ); frame1_.set_label( "Gtk::Frame 1 Widget" ); // set the frames label frame2_.set_label( "Gtk::Frame 2 Widget" ); frame1_.set_label_align( 0.0, 0.25 ); frame2_.set_label_align( 1.0 ); frame1_.set_shadow_type( Gtk::ShadowType::SHADOW_ETCHED_OUT ); frame2_.set_shadow_type( Gtk::ShadowType::SHADOW_ETCHED_IN ); show_all_children(); } MyWindow::~MyWindow() {} // xalign (0.0 = left, 1.0 = right, defaults to 0.0) & // yalign (0.0 = under, 1.0 = over, default = 0.5; won't paint gap if exactly 0.0 or 1.0)

12 Widgets Layout: Separator, Paned, Box, Grid, ScrollBar
Menus and tool bars: MenuBar, ToolBar Text: Label, Entry, TextView Information: ToolTips, InfoBar, Dialog MessageDialog, FileChooserDialog, ColorChooserDialog, FontChooserDialog, AboutDialog Numeric values: SpinButton, Range, Scale Pictures: Image, PixBuf, DrawingArea Buttons: Button, ToggleButton, CheckButton, RadioButton Look at tutorial or GTKmm class API for details on what methods are available. Remember that a frame only holds one item, so will need a multi-widget container if want more than widget. Label: not editable, limited formating available, small to medium amount of text, set in constructor or later Entry: editable, small to medium amount of text in single line TextView: medium to large amount of text Tooltips are the little information windows that pop up when you leave your pointer over a widget for a few seconds. Use set_tooltip_text() to set a text string as a tooltip on any Widget. Gtk::ToolItems are not Widgets, but have the same method for convenience. Gtk::Tooltip is used for more advanced tooltip usage, such as showing an image as well as text. An InfoBar may show small items of information or ask brief questions. Unlike a Dialog, it appears at the top of the current window instead of opening a new window. Its API is very similar to the Gtk::Dialog API. A SpinButton allows the user to select a value from a range of numeric values. It has an Entry widget with increment and decrement buttons at the side. Clicking the buttons causes the value to 'spin' up and down across the range of possible values. Gtk::Scale and Gtk::Scrollbar both inherit from Gtk::Range and share much functionality. They contain a "trough" and a "slider" (sometimes called a "thumbwheel" in other GUI environments). Dragging the slider with the pointer moves it within the trough, while clicking in the trough advances the slider towards the location of the click, either completely, or by a designated amount, depending on which mouse button is used. This should be familiar scrollbar behaviour. Gtk::Button. Standard buttons, usually marked with a label or picture. Pushing one triggers an action. Gtk::ToggleButton. Unlike a normal Button, which springs back up, a ToggleButton stays down until you press it again. It might be useful as an on/off switch. Gtk::CheckButton. These act like ToggleButtons, but show their state in small squares, with their label at the side. They should be used in most situations which require an on/off setting. Gtk::RadioButton. Named after the station selectors on old car radios, these buttons are used in groups for options which are mutually exclusive. Pressing one causes all the others in its group to turn off. They are similar to CheckBoxes (a small widget with a label at the side), but usually look different

13 More complex window structure
// ex4/window.h #include <gtkmm.h> #include <vector> #include <string> class MyWindow : public Gtk::Window { Gtk::Paned paned_; Gtk::Frame frame1_, frame2_; Gtk::ButtonBox bbox_; Gtk::Box box_; Gtk::Label label_; Gtk::Button button1_, button2_; Gtk::Image image_; Gtk::Entry valueField_; static std::vector<std::string> imageNames_; static int index_; public: MyWindow(); ~MyWindow(); }; See the files in code/ex4 under the GTKmm tutorial folder. The Gtk::Paned divides a widget into two halves, separated by a moveable divider. It defaults to a horizontal orientation. Use Gtk::Frame to provide nice borders. Gtk::ButtonBox lets you hold a number of buttons in either a horizontal or vertical layout with a consistent spacing. Gtk::Box lets you hold multiple widgets, including other boxes. It defaults to a horizontal orientation Gtk::Label is used to hold non-editable text. It can span more than one line if you add newline characters to the string being displayed. Gtk::Button provides a button that the user can press to trigger an action. It can hold text and/or an image. Gtk::Image lets you display an image from a file. It can also contain an animation. The image can be a PNG or JPEG file. If the file fails to load, you'll get the broken image icon. Gtk::Entry is designed to hold 1 line of editable text. If want to hold more, use Gtk::TextView

14 More complex window structure (2)
// ex4/window.cc MyWindow::MyWindow() : paned_{Gtk::Orientation:: ORIENTATION_VERTICAL}, label_{"Enter text: "}, image_{imageNames_.at(index_)}, { set_title( "Example 4" ); set_default_size( 500, 300 ); set_border_width( 10 ); button1_.set_image( image_ ); button2_.set_label( "something" ); bbox_.add( button1_ ); bbox_.add( button2_ ); valueField_.set_text( "xxx" ); box_.add( label_ ); box_.add( valueField_ ); frame1_.add( bbox_ ); frame2_.add( box_ ); add( paned_ ); paned_.add1( frame1_ ); paned_.add2( frame2_ ); frame1_.set_label("..."); frame2_.set_label("..."); frame1_.set_label_align( 0.0, 0.25 ); frame2_.set_label_align( 1.0 ); frame1_.set_shadow_type( Gtk::ShadowType::SHADOW_ETCHED_OUT ); frame2_.set_shadow_type( Gtk:: ShadowType::SHADOW_ETCHED_IN ); show_all_children(); } Contents of most of the text string omitted. Gtkmm uses Glib::ustring in many classes/methods rather than std::string. Glib::ustring has much the same interface as std::string, but contains Unicode characters encoded as UTF-8. About UTF-8 and ASCII The standard character set ANSI_X  – more commonly known as ASCII – is a subset of UTF-8. So, if you want to, you can use Glib::ustring without even thinking about UTF-8. Whenever ASCII is mentioned in this manual, we mean the real ASCII (i.e. as defined in ANSI_X ), which contains only 7-bit characters. Glib::ustring can not be used with ASCII-compatible extended 8-bit charsets like ISO It's a good idea to avoid string literals containing non-ASCII characters (e.g. German umlauts) in source code, or at least you should use UTF-8 literals. You can find a detailed UTF-8 and Unicode FAQ here: Glib::ustring vs. std::string Glib::ustring has implicit type conversions to and from std::string. These conversions do not convert to/from the current locale (see Glib::locale_from_utf8() and Glib::locale_to_utf8() if you need that). You can always use std::string instead of Glib::ustring – however, using std::string with multi-byte characters is quite hard. For instance, std::string::operator[] might return a byte in the middle of a character, and std::string::length() returns the number of bytes rather than characters. So don't do that without a good reason. Many member functions and operators of Glib::ustring and Glib::ustring_Iterator assume that the string contains only valid UTF-8 data. If it does not, memory outside the bounds of the string can be accessed. In a perfect world the C++ Standard Library would contain a UTF-8 string class. Unfortunately, the C++98 standard doesn't mention UTF-8 at all. C++11 has UTF-8 literals but no UTF-8 string class. Note that std::wstring is not a UTF-8 string class because it contains only fixed-width characters (where width could be 32, 16, or even 8 bits). Glib::ustring and stream input/output The stream I/O operators, that is operator<<() and operator>>(), perform implicit charset conversion to/from the current locale. If that's not what you intended (e.g. when writing to a configuration file that should always be UTF-8 encoded) use ustring::raw() to override this behaviour. If you're using std::ostringstream to build strings for display in the user interface, you must convert the result back to UTF-8 as shown below: std::locale::global(std::locale("")); // Set the global locale to the user's preferred locale.

15 Listeners In order to be able to interact with the GUI, we need to associate actions to perform i.e. functions to call to GUI events. Events include: keyboard keys pressed/released, mouse motion, button presses, etc. Tie an event received by a widget to a function to call, as in: button.signal_clicked().connect( sigc::mem_fun(*this, &function) ); For keyboard events, need to tell window to override on_key_press_event and what sort of events it's interested in, combined via bit-wise operators: add_events( Gdk::EventMask ); bool on_key_press_event( GdkEventKey* ) override; Whenever you press or release a key, an event is emitted. You can connect a signal handler to handle such events. To receive the keyboard events, you must first call the Gtk::Widget::add_events() function with a bit mask of the events you're interested in. The event signal handler will receive an argument that depends on the type of event. For keyboard events it's a GdkEventKey*. As discribed in the appendix, the event signal handler returns a bool value, to indicate that the signal is fully handled (true) or allow event propagation (false). While there are Gdk::EventMask values, there are also Gdk::EventType values. Gdk::EventMask EXPOSURE_MASK  Receive expose events. POINTER_MOTION_MASK  Receive all pointer motion events. BUTTON_MOTION_MASK  Receive pointer motion events while any button is pressed. BUTTON1_MOTION_MASK  Receive pointer motion events while 1 button is pressed. BUTTON2_MOTION_MASK  Receive pointer motion events while 2 button is pressed. BUTTON3_MOTION_MASK  Receive pointer motion events while 3 button is pressed. BUTTON_PRESS_MASK  Receive button press events. BUTTON_RELEASE_MASK  Receive button release events. KEY_PRESS_MASK  Receive key press events. KEY_RELEASE_MASK  Receive key release events. ENTER_NOTIFY_MASK  Receive window enter events. LEAVE_NOTIFY_MASK  Receive window leave events. FOCUS_CHANGE_MASK  Receive focus change events. STRUCTURE_MASK  Receive events about window configuration change. PROPERTY_CHANGE_MASK  Receive property change events. VISIBILITY_NOTIFY_MASK  Receive visibility change events. PROXIMITY_IN_MASK  Receive proximity in events. PROXIMITY_OUT_MASK  Receive proximity out events. SUBSTRUCTURE_MASK  Receive events about window configuration changes of child windows. SCROLL_MASK  Receive scroll events. TOUCH_MASK  Receive touch events. Since gtkmm 3.4: SMOOTH_SCROLL_MASK  Receive smooth scrolling events. Since 3.4 GDK_TOUCHPAD_GESTURE_MASK: receive touchpad gesture events. Since gtkmm 3.18: TOUCHPAD_GESTURE_MASK  TABLET_PAD_MASK  Receive tablet pad events. Since gtkmm 3.22: ALL_EVENTS_MASK  The combination of all the above event masks. To determine which key was pressed or released, you read the value of GdkEventKey::keyval and compare it with a constant in the <gdk/gdkkeysyms.h> header file. The states of modifier keys (shift, ctrl, etc.) are available as bit-flags in GdkEventKey::state. GDK_KEY_Return

16 Adding listeners // ex4/window.h #include <gtkmm.h> #include <vector> #include <string> class MyWindow : public Gtk::Window { ... bool on_key_press_event( GdkEventKey* ) override; protected: // signal handlers void b1_clicked(); void b2_clicked(); public: MyWindow(); ~MyWindow(); }; Add our signal handler methods. Note that these could be calls to methods in another object.

17 Adding listeners (2) // ex4/window.cc MyWindow::MyWindow() ... { ... // set button listeners button1_.signal_clicked().connect( sigc::mem_fun(*this, &MyWindow::b1_clicked) ); button2_.signal_clicked().connect( &MyWindow::b2_clicked) ); add_events( Gdk::KEY_PRESS_MASK ); } bool MyWindow::on_key_press_event( GdkEventKey* keyEvent ) { static int numTimesReturnPressed = 0; if (keyEvent->keyval == GDK_KEY_Return) { numTimesReturnPressed++; string s = valueField_.get_text(); cout << "User entered: " << s << endl; label_.set_text( s ); valueField_.set_text( std::to_string( numTimesReturnPressed ) ); return true; } // if return Gtk::Window::on_key_press_event( keyEvent ); void MyWindow::b1_clicked() { index_++; index_ %= imageNames_.size(); image_.set( imageNames_.at( index_ ) ); void MyWindow::b2_clicked() { string s = valueField_.get_text(); cout << "old text = " << s << endl; valueField_.set_text( "yyy" ); Add our signal handler methods. Note that these could be calls to methods in another object. std::to_string( int ) is C++11 rather than using a string stream to convert a number to a string.

18 Dialogs Inherit from Gtk::Dialog
Gtk::MessageDialog: simple two lines of text plus "OK" button. Can add an image and other buttons that trigger a Gtk::Dialog::signal_response() signal plus an id. Gtk::FileChooserDialog: for "open" or "save" actions. Gtk::ColorChooserDialog: presents a colour palette from which the user can select a colour. Gtk::FontChooserDialog: presents a selection of fonts from which the user can select one. Gtk::AboutDialog: lets the program present some information without freezing the rest of the program (unlike the other dialogs) i.e. non-modal. Designed to display program name, credits, version, copyright, comments, authors, artists, website, documenters, translator, and logo. Can add an image and other buttons that trigger a Gtk::Dialog::signal_response() signal plus an id. "Dialogs are used as secondary windows, to provide specific information or to ask questions. Gtk::Dialog windows contain a few pre-packed widgets to ensure consistency, and a run() method which blocks until the user dismisses the dialog. There are several derived Dialog classes which you might find useful. Gtk::MessageDialog is used for most simple notifications. But at other times you might need to derive your own dialog class to provide more complex functionality. To pack widgets into a custom dialog, you should pack them into the Gtk::Box, available via get_content_area(). To just add a Button to the bottom of the Dialog, you could use the add_button() method. The run() method returns an int. This may be a value from the Gtk::ResponseType if the user closed the dialog by clicking a standard button, or it could be the custom response value that you specified when using add_button(). "

19 Menus & Tool bars GTKmm 3.0 uses an XML string to specify the layout and format of menus and tool bars. Need to add the Gtk::MenuBar/Gtk::Toolbar to a Gtk::Builder, which is then added to the container. Create actions, possibly associated with "key accelerators" such as Ctrl-C for copy, that are tied to the menu/tool bar selections. Would also be specified in the XML string. Can add a menu bar directly to a Gtk::ApplicationWindow, which is a subclass of both Gtk::Window and Gio::ActionMap. Populate the menu bar through the constructor, or by adding Gtk::MenuItem objects. Dealing with window closure gets tricky, though. Otherwise, the window needs to know about the application, and needs a Gtk::Builder to create the menu bar. Can create pop-up menus by using Gtk::Menu::popup. At this point is also a good idea to tell the application to respond to keyboard shortcuts, by using Gtk::Application::set_accel_for_action(). If your main window is derived from ApplicationWindow and you instantiate your menubar with Gtk::Application::set_menubar(), then you don't have to call set_accel_for_action(). change MyWindow to inherit from Gtk::ApplicationWindow but then quitting becomes a problem, as per See example: void ExampleApplication::on_menu_file_quit() { std::cout << G_STRFUNC << std::endl; quit(); // Not really necessary, when Gtk::Widget::hide() is called. // Gio::Application::quit() will make Gio::Application::run() return, // but it's a crude way of ending the program. The window is not removed // from the application. Neither the window's nor the application's // destructors will be called, because there will be remaining reference // counts in both of them. If we want the destructors to be called, we // must remove the window from the application. One way of doing this // is to hide the window. std::vector<Gtk::Window*> windows = get_windows(); if (windows.size() > 0) windows[0]->hide(); // In this simple case, we know there is only one window. } In XML This is where we specify the names of the menu items as they will be seen by users in the menu. Therefore, this is where you should make strings translatable, by adding translatable='yes'.

20 Menus & Tool bars (2) I would suggest having a "main box" with a vertical orientation, to which you'd add the menu bar (and tool bar if you want one) before adding the other widgets. There will likely be an overlap between the actions in the menu bar and the tool bar, so use that to your advantage. Can either build the menu incrementally by adding menu items or use XML, which is considerably easier once you understand the format. Note that can use defined accelerators, as Ctrl-[1-4] or by selecting sub-menu options.

21 Defining a menu // ex5/window.cc MyWindow::MyWindow(const Glib::RefPtr< Gtk::Application>& app) ... { ... add( mainBox_ ); setUpMenu(); structureGUI(); } void MyWindow::setUpMenu() { actionGroup_=Gio::SimpleActionGroup:: create(); builder_ = Gtk::Builder::create(); insert_action_group("example", actionGroup_); actionGroup_->add_action( "quit", sigc::mem_fun(*this, &MyWindow::sub_action1_quit)); const char* ui_info = "<interface>" " <menu id='menubar'>" " <submenu>" " <attribute name='label' translatable='yes'>_ActionGroup1 </attribute>" " <section>" " <item>" " <attribute name='label' translatable='yes'>_Quit</attribute>" " <attribute name='action'> example.quit</attribute>" " <attribute name='accel'> <Primary>q</attribute>" " </item>" See GTKmm tutorial code/ex5. Create an action group, which contains the handlers and what action names (text strings) they're associated with. The Gtk::Builder will be used to create the menu from the XML string. The string specifies what actions from the named action group to associate with the menu items, and any key accelerators to use.

22 Defining a menu (2) app_->set_accel_for_action( "example.quit", "<Primary>q"); ... try { builder_->add_from_string( ui_info ); } catch( const Glib::Error& ex) { std::cerr << "Building menu failed: " << ex.what(); } auto object = builder_->get_object( "menubar" ); auto gmenu = Glib::RefPtr<Gio::Menu> ::cast_dynamic( object ); // did the conversion fail? if ( !gmenu ) g_warning( "GMenu not found" ); else { auto menuBar = Gtk::manage( new Gtk::MenuBar( gmenu ) ); mainBox_.pack_start( *menuBar, Gtk::PACK_SHRINK ); } // if } // MyWindow::setUpMenu "When the menubar is a child of a Gtk::Window, keyboard accelerators are not automatically fetched from the Gio::Menu. See the examples/book/menus/main_menu example for an alternative way of adding the menubar when using Gtk::ApplicationWindow. Gtk::Application::set_accel_for_action() is new in gtkmm " Define all of the accelerators. Try to build the menu, but catch any exceptions. Turn the received object into a Gio::Menu, via cast_dynamic, which will result in a nullptr if the cast fails

23 Defining a menu (3) void MyWindow::sub_action1_quit() { Gtk::MessageDialog dialog( *this, "Termination Dialog", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL); dialog.set_secondary_text( "Are you *really* sure you want to Quit?" ); int result = dialog.run(); switch(result) { case( Gtk::RESPONSE_OK ): hide(); break; case( Gtk::RESPONSE_CANCEL ): std::cout << "Cancel clicked." << std::endl; default: std::cout << "Unexpected button clicked." << std::endl; } // switch } Gtk::ButtonsType Prebuilt sets of buttons for the dialog. If none of these choices are appropriate, simply use Gtk::BUTTONS_NONE then call Gtk::Dialog::add_buttons(). Please note that Gtk::BUTTONS_OK, Gtk::BUTTONS_YES_NO and Gtk::BUTTONS_OK_CANCEL are discouraged by the GNOME Human Interface Guidelines. "Give all alerts an affirmative button that dismisses the alert and performs the action suggested in the primary text. Provide a Cancel button for all alerts displayed in response to a user actions, such as Quit. If the alert warns of a technical problem or other situation that could result in data loss, provide a Help button that provides more information on the particular situation and explains the user's options. You may also provide buttons to perform alternate actions that provide another possible solution, fix potential problems, or launch related dialogs or programs. Button Phrasing Write button labels as imperative verbs, for example Save, Print. This allows users to select an action with less hesitation. An active phrase also fits best with the button's role in initiating actions, as contrasted with a more passive phrase. For example Find and Log In are better buttons than Yes and OK. Affirmative Button Place the affirmative button in the lower right corner of the alert. The affirmative button accepts the action proposed by the alert, or simply dismisses the alert if no action is suggested (as is the case with an information alert). Cancel Button If the alert was produced in response to a user's action, place a Cancel button immediately to the left of the affirmative button. This provides an escape route for users to stop an action in response to new information, or just if they clicked accidentally. Clicking the Cancel button reverts the application to its state prior to the user action. Help Button A Help button may be used to clarify alerts that present potentially destructive options. Place the Help button in the lower left corner of the alert. When clicked, launch a help window clarifying the situation, detailing the actions performed by the other buttons, and explaining any side-effects that each action may have. Alternate Buttons Extra buttons may be used to provide alternates to the primary action proposed by the alert text. Place these buttons to the left of the Cancel button, or the affirmative button if Cancel is not present. An example of a common alternate action would be a Quit without Saving button in a save confirmation alert. This is an alternative to the primary suggested action Save and the Cancel button." BUTTONS_NONE  No buttons at all. BUTTONS_OK  An OK button. BUTTONS_CLOSE  A Close button. BUTTONS_CANCEL  A Cancel button. BUTTONS_YES_NO  Yes and No buttons. BUTTONS_OK_CANCEL  OK and Cancel buttons. void MyWindow::sub_action1_quit() { ... hide(); //Closes the main window to stop the app->run(). }

24 Tool bars Gtk::Toolbar contains Gtk::ToolItem, which has subclasses Gtk::SeparatorToolItem, Gtk::ToolButton, Gtk::MenuToolButton, Gtk::ToggleToolButton, and Gtk::RadioToolButton. Can configure if a tool item is visible (or not) when the toolbar is laid out horizontally or vertically. Gtk::ToolItem can be decorated with an image and/or a text label. Simplest way to define the tool bar layout is to use XML in a .glade file, which can be used in combination with previously defined actions to specify what to do when the tool bar button is pressed. It's actually painful to find all of the necessary bits of information in the reference manual for tool bars; they've obviously moved to using GtK+ XML to define the look of the GUI where possible.

25 toolbar.glade <?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade > <interface> <requires lib="gtk+" version="3.8"/> <object class="GtkImage" id="image_1"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="pixbuf">img/icon1.png</property> </object> ... <object class="GtkToolbar" id="toolbar"> Glade file to define the toolbar structure Define the three images that make up the icons to use for the tool item widgets. Each has a name, and the path and filename of the image to use. Then define the toolbar...

26 toolbar.glade ... <child> <object class="GtkToolButton" id="show1"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Show image 1</property> <property name="action_name">example.show1</property> <property name="label_widget">image_1</property> </object> <packing> <property name="expand">False</property> <property name="homogeneous">True</property> </packing> </child> </interface> and each of its items, with names, tool tips (displayed when cursor hovers it), and the action to take (already defined in the action group).

27 window.cc void MyWindow::setUpMenu() { ... builder_->add_from_file( "toolbar.glade" ); Gtk::Toolbar * toolbar = nullptr; builder_->get_widget( "toolbar", toolbar ); if ( !toolbar ) g_warning( "toolbar not found" ); else mainBox_.pack_start( *toolbar, Gtk::PACK_SHRINK ); } void MyWindow::show_image_1() { image_.set( imageNames_.at( 0 ) ); } Window adds to the UI from the Glade XML file, then adds the defined toolbar (if successful) to the main box. The defined handler for each tool item displays the relevant associated image.

28 Glade It's pretty painful to create your own custom widgets that Glade will understand and let you use. Need to define custom Glade libraries in XML, and add to the Glade search path. Instead, it's probably easier to use Glade to create the GUI as if the default window is your custom window, and in your program, use the Gtk::Builder to find the window, and create it as your derived class window. You can then create your action group and connect the necessary signal bindings to your window's private/protected methods. See ex6/gui.glade. If creating custom widgets that you want to have available in Glade, e.g. your window inherits from Gtk::Window, need to create a "catalog" file that defines the class. The DTD that is shipped with Glade can be used to validate your catalog file. Note that properties must be entered in the same order as they are specified in the DTD for the validation to pass. To validate a file, do this: xmllint --dtdvalid glade-catalog.dtd --noout my-catalog.xml To install a widget plugin, the catalog XML file should be copied into the catalog directory, which can be retrieved as: pkg-config --variable=catalogdir gladeui-1.0 The plugin library should be installed into the modules directory: pkg-config --variable=moduledir gladeui-1.0 Widget icons if provided (recommended) need to be installed into the icon theme, this is described in the next chapter. You can also load your catalog from a user directory by specifying additional load path(s) in the environment, for instance: GLADE_CATALOG_SEARCH_PATH=~/mycatalogs:~/work/foo/glade Same goes for optional plugin libraries, for instance: GLADE_MODULE_SEARCH_PATH=~/work/foo/src Currently loading icons without installing them is unsupported. If create a Gtk::Box in a Gtk::Window, can't just get the box and add that to your window since it belongs to that window i.e. would have to remove it first. So, can either use move semantics to steal the window's contents, or use the builder to create the window as a derived object.

29 window.h // ex6/window.h class MyWindow : public Gtk::Window { Glib::RefPtr<Gtk::Application> & app_; Glib::RefPtr<Gtk::Builder> & builder_; Glib::RefPtr<Gio::SimpleActionGroup> actionGroup_; Gtk::Box * box_; Gtk::Image * image_; Gtk::Button * button1_, * button2_; Gtk::Label * label_; Gtk::Entry * valueField_; ... public: MyWindow( BaseObjectType *, Glib::RefPtr<Gtk::Builder> &, Glib::RefPtr<Gtk::Application> & ); ~MyWindow(); }; See GTKmm tutorial code/ex6. Modified window.h to now have pointers to the window elements that the handlers will need to modify. The constructor takes in the base object type that is used to initialize the superclass, the builder needed to find the pieces, the application needed for the menu bar action group, and initializes the pointers to nullptr. Need the BaseObjectType to ensure the underlying Gtk::Window is properly set. Want the builder so can find the widgets by name. Need the application so can add the action group to it. "This object [Gtk::Builder] represents an `instantiation' of an UI definition description. When one of these objects is created, the XML file is read, and the user interface is created. The Gtk::Builder object then provides an interface for accessing the widgets in the user interface by the names assigned to them inside the UI description. A Gtk::Builder holds a reference to all objects that it has constructed and drops these references when it is deleted. This deletion can cause the destruction of non-widget objects or widgets which are not contained in a toplevel window. For toplevel windows constructed by a builder, it is the responsibility of the user to get them with get_widget() or get_widget_derived() and delete them to get rid of them and all the widgets they contain. The methods get_widget() and get_widget_derived() can be used to access the widgets in the interface by the names assigned to them inside the UI description. Toplevel windows returned by these methods will stay around until the user explicitly deletes them. Other widgets will either be part of a larger hierarchy constructed by the builder (in which case you should not have to worry about their lifecycle), or without a parent, in which case they have to be added to some container before the builder is deleted to make use of them. Non-widget objects need to be fetched with get_object() or get_objects() to keep them beyond the lifespan of the builder."

30 window.cc MyWindow::MyWindow( BaseObjectType * cobject, Glib::RefPtr<Gtk::Builder> & builder, Glib::RefPtr<Gtk::Application> & app ) : Gtk::Window{cobject}, app_{app}, builder_{builder}, ... { set_title( "Example 6" ); set_default_size( 300, 400 ); set_border_width( 10 ); buildUI(); buildMenu(); add_events( Gdk::KEY_PRESS_MASK ); show_all_children(); } Note that the Gtk::Window constructor is passed the base object type parameter.

31 window.cc (2) void MyWindow::buildUI() { builder_->get_widget( "button1", button1_ ); ... if ( button1_ == nullptr || button2_ == nullptr || ... label_ == nullptr ) { g_warning("unable to extract window sub-components"); return; } // if image_->set( imageNames_.at( 0 ) ); button1_->signal_clicked().connect( sigc::mem_fun(*this, &MyWindow::b1_clicked) ); button2_->signal_clicked().connect( sigc::mem_fun(*this, &MyWindow::b2_clicked) ); } // MyWindow::buildUI Find each of the components we need to hook up to the handlers.

32 window.cc (3) void MyWindow::buildMenu() { actionGroup_ = Gio::SimpleActionGroup::create(); Gtk::Window::insert_action_group( "example", actionGroup_ ); actionGroup_->add_action( "sub_act_1.1", sigc::mem_fun( *this, &MyWindow::sub_action1_1 ) ); ... app_->set_accel_for_action("example.sub_act_1.1", "<Primary>1"); actionGroup_->add_action( "show1", sigc::mem_fun( *this, &MyWindow::show_image_1 ) ); } // MyWindow::buildMenu Set up the menu handlers. Set up the accelerator keys to the defined actions. Set up the toolbar actions.

33 main.cc int main( int argc, char * argv[] ) { try { auto app = Gtk::Application::create( argc, argv, "gtkmm.tutorial.example6" ); auto builder = Gtk::Builder::create_from_file( "gui.glade" ); MyWindow *window_ = nullptr; builder->get_widget_derived( "window", window_, app ); if ( window_ == nullptr ) { g_warning("unable to extract window"); return -1; } // if app->run( *window_ ); delete window_; } // main First create the application to run. Create the builder from the glade file. Find the window in the glade build, which builds the MyWindow instance, derived from Gtk::Window. Automatically gets the BaseObjectType and Builder reference passed in, so only need to add the app parameter. The pointer will be nullptr if failed, in which case produce an error message and exit. Otherwise, tell the app to run the window, and delete it when done.

34 Valgrind GTKmm doesn't play well with valgrind.
Libraries don't free memory allocated once, apparently relying upon OS to clean it up. Developers claim appropriate suppression files and exporting variables such as G_SLICE and G_DEBUG will make it workable. Resources: world-program Spent a few days going down the rabbit hole trying to get valgrind to play well with gtkmm. Even the most basic example (create an application with a window that contains nothing) leaks a huge amount of memory since there's no way to create a Gtk::Application on the stack (constructors are protected). Attempting to remove the window from the application, call app->quit(), delete the wrapped object, or calling reset() on the Glib::RefPtr still resulted in memory leaks despite using the recommended suppression files. Suggests running valgrind with suppression files from 1. Clone the git repository: git clone 2. Open a terminal and cd into GNOME.supp. 3. make GNU Make will generate suppression (SUPP) files in the build folder. To use them with Valgrind, pass a --suppressions=SUPP_FILE option for each suppression file that you would like to use. For example, to use the suppressions in glib.supp, you would pass --suppressions=/path/to/GNOME.supp/build/glib.supp to Valgrind. See the Valgrind page on the GNOME wiki ( for tips on using Valgrind to detect memory leaks in GNOME software. valgrind --tool=memcheck --leak-check=full --leak-resolution=high --num-callers=20 --log-file=vgdump your-program If you want or need to validate the files with "make test" then it is necessary to install valgrind-suppressions-util. --- suggested suppression file brings the number down, but still many leaks in the application creation and run code $ export G_SLICE=always-malloc $ export G_DEBUG=gc-friendly $ valgrind --tool=memcheck --leak-check=full --leak-resolution=high \ --num-callers=50 --show-reachable=yes \ --suppressions=gtk.suppression2 \ Refers to: and


Download ppt "GTKmm Tutorial CS247 Spring 2018."

Similar presentations


Ads by Google