Posts Tagged ‘gtk+’

2.1. Gtk::Button & Signals

June 26, 2011

Since buttons, signals and callbacks always go hand-in-hand so I am just going to put them into one chapter.

1. Signals & Callbacks:
Like many other GUI toolkits, such as wxWidget, gtkmm is event-driven, meaning that it relies on signals and callbacks to interact with the users. A signal is emitted when the users perform some actions with your widgets, such as pressing a mouse button. And in order to make the application react to those signals, we set up “signal handlers”, or callback functions, to do something after the signals are emitted. (For example, we can have a function (or “signal handler”) creating a notification dialog and showing it on the screen after the users click the button (or emit the “clicked” signals).

In order to preserve the object-oriented approach, gtkmm uses the libsigc++, a type-safe callback system for standard C++, to connect signals with callback functions (See the libsigc++ homepage). The following line is an example of connecting a Gtk::Button “clicked” signal with a callback function (“signal handler”) called “on_button_clicked” (You don’t have to remember or examine this code line. Just simply take a look at it. I will explain this statement for you later in this chapter):

button.signal_clicked().connect(sigc::mem_fun(*this, &HelloWorld::on_button_clicked));

2. An improved HelloWorld with Gtk::Button:
Like last time, we will start with the code of the program first and then examine it later. At present, as you already have some basic knowledge about Gtkmm, try to first read the following codes without looking at the explanation below it.

helloworld_2.1.cc

#include <iostream>
#include <gtkmm.h>

/* Create the HelloWorld class 
 * by inheritance from Gtk::Window 
 */
class HelloWorld : public Gtk::Window {
public:
  //Constructors & Destructors
  HelloWorld();
  virtual ~HelloWorld();
  
protected:
  //Signal Handler
  virtual void on_button_clicked();
  
  //Child widget
  Gtk::Button button;
};

/* The HelloWorld::HelloWorld() constructor */
HelloWorld::HelloWorld() : 
  //set the button's label
  button("Click me!")
{
  //set the window's title
  set_title("Hello World!");
  
  //connect the button's clicked signal to member 
  //function HelloWorld::on_button_clicked()
  button.signal_clicked().connect(sigc::mem_fun(*this, 
                   &HelloWorld::on_button_clicked));
  
  //add the button to the main window
  add(button);
  
  show_all_children();
}

/* The HelloWorld::~HelloWorld() destructor */
HelloWorld::~HelloWorld() {
}

/* Signal-Handler for Gtk::Button button */
void HelloWorld::on_button_clicked() {
  std::cout << "The button was clicked!!" << std::endl;
}

/* The main program */
int main(int argc, char *argv[]) {
  //Initialize Gtkmm
  Gtk::Main kit(argc, argv);
  
  //Create the helloworld object
  HelloWorld helloworld;
  
  //Display the helloworld window
  Gtk::Main::run(helloworld);

  return 0;
}

So did you see some new things in this code comparing to the one in chapter 1? Let’s examine it!

3. Create the Gtk::Button:
In the above example, I first create an empty button inside HelloWorld class by using:

Gtk::Button button;

and then set its label in the constructor’s initializer list.

HelloWorld::HelloWorld() : 
  button("Click me!")
{
  //the rest of the code...
}

As I have mentioned above, you can use the Gtk::Button::set_label() member function to set the label of button. This function accepts a Glib::ustring as its only argument.

void Gtk::Button::set_label(const Glib::ustring& label);

/* label: the button's label
 */

According to the Gtkmm reference, Gtk::Window is inherited from Gtk::Container. Therefore, we can use the Gtk::Container::add() method of the Gtk::Container class to add button to the helloworld window:

void Gtk::Container::add(Gtk::Widget& widget);

/* widget: the widget need to be packed inside the container
 */

Please notes that all the functions from the base class can be called directly inside the derived class. You don’t have to create a base object and then call out its method. For example, you just need to use .add() inside the HelloWorld class to directly add widget into the helloworld window, instead of creating a Gtk::Container and invoke its method like this:

/* ERROR: HelloWorld, derived from Gtk::Window, is already a 
 * container. Just need to invoke .add() inside HelloWorld 
 */
Gtk::Container container;
container.add(button);

4. Connect the signal to the callback:
Now here comes the fun part of this chapter.

Advertisements

1.1. Hello World!

June 26, 2011

Starting with a “Hello World!” example has been the traditional of most programming tutorials, and so is this one. Let’s begin our gtkmm tutorial by creating a 200 x 200 window with title “Hello World” (Source Code: HelloWorld.cc):

helloworld

#include <gtkmm.h>

class HelloWorld : public Gtk::Window {
public:
  HelloWorld();
  virtual ~HelloWorld();
};

HelloWorld::HelloWorld() {
  set_title("Hello World");
  set_size_request(200, 200);
}

HelloWorld::~HelloWorld() {
}

int main(int argc, char *argv[]) {
  Gtk::Main kit(argc, argv);
  
  HelloWorld helloworld;
  
  Gtk::Main::run(helloworld);

  return 0;
}

Look scary, huh? Thought this is a traditional Hello World? This was what I felt four months ago. Well yeah it is a HelloWorld but it is just very “C++-ish”. Let’s examine the code together to understand it.

1. Header:
The file <gtkmm.h> includes all the definitions, functions, classes, etc… of gtkmm and other libraries that are used by gtkmm source code, such as glibmm. Although sometimes you need further files inclusions most of the time #include <gtkmm.h> is enough.

2. Define a derived class:
After including all the necessary headers, the first thing you need to do is to define a class that will create a Gtk::Window. Well you can absolutely just create a Gtk::Window in main() by using Gtk::Window helloworld; , and then set the title and size request for the helloworld later. However, since you are using gtkmm, you should try to write codes that are as organized and as “object-oriented” as you can. This will help you a lot later in when you have to create a complex gtkmm GUI, with lots of child widgets, signals, etc… (which we will see later)

class HelloWorld : public Gtk::Window {
public:
  HelloWorld();
  virtual ~HelloWorld();
};

The above code creates a HelloWorld class that is derived from the Gtk::Window class. It has a constructor, HelloWorld::HelloWorld() and a destructor HelloWorld::~HelloWorld(). We use this HelloWorld class later on to create a helloworld window by calling:

HelloWorld helloworld;

3. Constructor & Destructor:
Now let’s take a look at the constructor HelloWorld::HelloWorld():

HelloWorld::HelloWorld() {
  set_title("Hello World");
  set_size_request(200, 200);
}

Since the HelloWorld class is derived from the Gtk::Window base class, we can use all the public member functions of the Gtk::Window class directly inside class HelloWorld. We first need to set the title by calling the Gtk::Window::set_title() method of the Gtk::Window base class, which takes a Glib::ustring as its only argument (for now you can just pretend that a Glib::ustring is merely equal to std::string):

//set the title of the Gtk::Window
void Gtk::Window::set_title(const Glib::ustring& title);

/* title: title of the window 
 */

The second function used inside the HelloWorld::HelloWorld() constructor (the set_size_request(200, 200) function) sets the request size for the newly created window. Unlike the Gtk::Window::set_title() function, Gtk::Widget::set_size_request() is a public member function of Gtk::Widget class, which is the base class of Gtk::Window (see the gtkmm reference for widget hierarchy). Since HelloWorld is derived from Gtk::Window, and Gtk::Window is derived from Gtk::Widget, we can use Gtk::Widget::set_size_request() inside our HelloWorld class:

//set the request size of the Gtk::Widget
void Gtk::Widget::set_size_request(int width = -1, 
                                   int height = -1);

/* width: the width of the widget in pixels
 * height: the height of the window in pixels. 
 */

4. Initialize gtkmm:
After declaring the HelloWorld class, in order for your window to be displayed, you have to first initialize gtkmm by calling:

Gtk::Main kit(argc, argv);

inside the main() function. The Gtk::Main kit(argc, argv) line helps setting up the gtkmm and GTK+ environment. To create the a HelloWorld window, you can just simply create a HelloWorld object:

HelloWorld helloworld;

After creating helloworld, the HelloWorld::HelloWorld() constructor will automatically set the title and the request size of the helloworld window for us. This is what I really like about gtkmm: Everything is nested inside the HelloWorld class and I do not need to use dozens of functions inside my main() to set properties or pack widgets into my main window.

Finally, in order for your window to be displayed (drawed) on the screen, you need to trigger the main gtkmm loop. By calling:

Gtk::Main::run(helloworld);

the computer will start your program and draw the helloworld object on the screen.

5. Compile the source code:
Compiling the above source code by calling:

g++ -g /home/phongcao/helloworld.cc -o /home/phongcao/helloworld `pkg-config --cflags --libs gtkmm-2.4`

gives us the desired “helloworld” program. The result has already been shown in the above screenshot.