Create GUI with Gtkmm - Glade with gtkmm

Previous post introduced the Gtkmm library and eclipse IDE for gtkmm development. As I mentioned at there, there are two possible ways to design gtkmm GUI either from code or Glade UI designer. I believe (and most of people believe) that Glade UI designer is the easiest way to create GUI for gtkmm development. Glade has a very user friendly environment to design and create GUI and finally the created GUI can be export as a xml file with two different formats as GtkBuilder and libglade. For this and future examples I am willing to use GtkBuilder format.
Now let's create a simple GUI from Glade. First you have to install Glade on your system, you may use synaptic package manager or Ubuntu software center to install it.

When you run Glade you may see the following window as the main window of Glade.




As you can see, left hand side of the window includes the palette which has all the gtkmm widget that glade support, and right hand side includes the properties of the selected widget and all the widgets list as a hierarchical list.

Every gtkmm GUI needs a top level window. Therefore first of all add a 'Window' from the palette, then you can add more widgets to the window. Also there is a widget type called 'Containers', containers can have one or more widgets on it. Therefore you should add proper containers to the empty window to include more widgets. For this example first add 'Vertical box' to window with 2 items. Then I added 'Label' to first position and 'Horizontal box' with 2 items to second position of vertical box. Likewise I have created the following GUI and saved it as GtkBuilder file format with the file name “gui.glade”. Also I have changed names of some widgets to make design file more understandable. The xml file for Glade GUI can be found at the end of this article for downloads.




Now let's write a C++ code for this GUI. As we all know C++ is a OOP language, but still we did not use the advantage of the OOP behavior for our gtkmm codes. Now let's see how to encapsulate all the things which are related to specific gui into one class.

First add new class to your project by right click on the eclipse project and selecting new class from the pop-up menu. It will create two file on the project's src folder as header file and source file. Copy following code to header (.h) file.

#include <gtkmm.h>

class FrmMain : public Gtk::Window{
protected:
    Glib::RefPtr<Gtk::Builder> builder;
    Gtk::Button *btnOk;
    Gtk::Button *btnCancel;
    Gtk::Label *lblNotice;

public:
    FrmMain(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade);//constructor

protected:
    //signal handlers
    void on_ok_button_clicked();
    void on_cancel_button_clicked();
};

Code explained

class FrmMain : public Gtk::Window{
FrmMain is the class name and this class was inherited Gtk::Window class which enable to use FrmMain class as a Gtk window.

Glib::RefPtr<Gtk::Builder> builder;
Gtk::Button *btnOk;
Gtk::Button *btnCancel;
Gtk::Label *lblNotice;
These are the widget variables which enable you to execute some operations on that. Here, builder variable is essential to import and load xml file to our code.

FrmMain(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade);//constructor
This is the constructor of the class. The parameters defined in here are essential to initiate the Gtk::Window and Gtk::Builder.

void on_ok_button_clicked();
void on_cancel_button_clicked();
These are the declarations for signal handlers for the signal which are emitted by widgets of the GUI  (Such as event handlers in Java or C#).

The source file for the above header file will be,

#include "FrmMain.h"

using namespace Gtk;

FrmMain::FrmMain(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade) :
    Gtk::Window(cobject), builder(refGlade){

    builder->get_widget("btnOk", btnOk);
    builder->get_widget("btnCancel", btnCancel);
    builder->get_widget("lblNotice",lblNotice);

    btnOk->signal_clicked().connect(sigc::mem_fun(*this, &FrmMain::on_ok_button_clicked));
    btnCancel->signal_clicked().connect(sigc::mem_fun(*this, &FrmMain::on_cancel_button_clicked));

}


void FrmMain::on_ok_button_clicked(){
    lblNotice->set_text("OK clicked");
}

void FrmMain::on_cancel_button_clicked(){
    lblNotice->set_text("Cancel clicked");
}

Code explained

FrmMain::FrmMain(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade) :
    Gtk::Window(cobject), builder(refGlade){
The constructor, initiate by calling super class constructor as Gtk::Window(cobject) and builder as builder(refGlade).


builder->get_widget("btnOk", btnOk);
builder->get_widget("btnCancel", btnCancel);
builder->get_widget("lblNotice",lblNotice);
These code lines will assign widgets from xml to variables which are declared in header file. (btnOk, btnCancel and lblNotice are the names which I assigned for widgets from Glade).  get_widget function will allows you to get widgets from xml file to your variables. So you can use these variable to manipulate GUI's widgets as your wish.


btnOk->signal_clicked().connect(sigc::mem_fun(*this, &FrmMain::on_ok_button_clicked));
btnCancel->signal_clicked().connect(sigc::mem_fun(*this, &FrmMain::on_cancel_button_clicked));
These code lines will connect signal handlers from xml file to your functions. That's mean when you click on btnOk button it will call the function  on_ok_button_clicked on your source code. So you can include your codes to execute when click on the button. But you can not change the the return type and parameter list of these functions as your wish. There are predefined formats for each signals. You can refer the format from the source of the signal. To navigate to the source of the signal you can click on the 'signal_clicked' while pressing on ctrl key on eclipse IDE. You may see the code like this,

  Glib::SignalProxy0< void > signal_clicked();

Here zero mean no parameters and void means return type is void. Let's see another example,

  Glib::SignalProxy1< bool,GdkEventButton* > signal_button_press_event();


Here 1 mean one parameter with the type of GdkEventButton* and bool return type. Therefore your function will be,

bool on_my_button_press_event(GdkEventButton* event){
}


void FrmMain::on_ok_button_clicked(){
    lblNotice->set_text("OK clicked");
}

void FrmMain::on_cancel_button_clicked(){
    lblNotice->set_text("Cancel clicked");
}

Now these are the code for signal handlers. Each one will set a specific text to lblNotice label.

So our encapsulated class is ready for execution. But where is the place to import or load xml file to the project by giving the file name of the xml file? This task perform on the main function of the project as,

Main kit(argc,argv);
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("gui.glade");

FrmMain *frm = 0;
builder->get_widget_derived("frmMain", frm);
kit.run(*frm);

Code explained

Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("gui.glade");
This will load all xml (glade) file to builder variable and allow to use the file as a gtk builder.


FrmMain *frm = 0;
builder->get_widget_derived("frmMain", frm);
kit.run(*frm);
get_widget_derived function allow to load a gtk widget from xml file to the derived class which we already implemented. Then the derived object can be use to run the main loop of the gtk.

If you run the code, you will see the result as,

when you click on OK and Cancel button the text of the label will change accordingly.

So now you can encapsulate all the variables, functions and structs, etc... to a class through this way. Also this will enable re-usability of the code.

The complete source code can be download here (with the glade xml file).


2 comments:

  1. Hi. I'm trying to give row headers to my gtk:TableView. Any idea of how can I do that? the documentation only seems to cover column headers

    ReplyDelete