Posts Tagged ‘open source’

0.1. Why not C++/GTK+?

June 26, 2011

Well if you have ever tried to create some small open-source projects with graphical interface you probably have heard of things like Qt, GTK+, Gtkmm, wxWidgets, etc… These are all the toolkits used by developers around the world to create GUI (graphical user interface) for their applications.

About more than a year ago, I myself started learning C++. And then about 4 month later I improved pretty fast (although I still consider myself a beginner), since I was already taught C & Pascal and some fundamental algorithms like quick sort, insertion sort, recursion, etc… in high school. I solved a pretty good amount of problems in Project Euler as well as those in Introduction to Algorithms by Cormen.

However, it came the point where I really feel bored with non-graphical programming and want to actually create some medium-to-large projects, where I can practice the object-oriented paradigm and get the most out of it. That was how I started with my very first personal open source project – Dingo Music Manager.

Well about six months ago I started to find a graphical toolkit for my software. I finally came down to three: GTK+, Gtkmm and Qt. Lots of developers have recommended Qt to me. But I am kinda a minimalist person and really do not like the sleek look of Qt. I prefer the simplistic and uniqueness of GNOME and GTK. Therefore, I went with GTK.

I tried out Gtkmm for a week and then gave up. It was so lack of documentation. The only tutorial available is on the main website and it is too terse and hard to understand. I could not grab things like Gtk::VBox, Gtk::HBox, or know what Gtk::Adjustment is used for. All I did was trying to read the main tutorial and try to understand the core GTK theories behind it. Plus, I did not have an insanely strong OOP foundation (since Dingo is my very first software), which prevents me from fully understanding some problems. The inability to understand the core concepts and a weak OOP foundation made me turn away from Gtkmm and use GTK+ instead.

In my experience, using GTK+ with C++ is fine. You can just declare callback functions and variables in your classes as static and then connect the signals to those static functions. However, these were not very nice to me, because when my software grew larger and larger, I had to declare more and more static members, and ended up “static” my whole class. Well I could not live with that design.

Then I finally switched back to Gtkmm about a month ago. And guess what? I grabbed Gtkmm in a breeze. Part of this phenomenon was that I was able to understand the core GTK concepts, like GtkWidget, GtkVBox, GtkAdjustment, GtkTreeView, etc… and only have to use the tutorial to translate GTK+ codes into Gtkmm. My OOP foundation has been getting better too after 5 months of coding a music manager and trying to program in C++ with GTK+ (weird, is it?). That is why I decide to spend my free time this summer to write a tutorial for the Gtkmm toolkit.

I think that except for the large executable file after compiling, Gtkmm has no drawbacks or weaknesses when comparing to GTK+. Gtkmm even has some advantages over its older brother in terms of memory management and OOP. It is just because Gtkmm main tutorial is too terse and overly concise that makes it difficult for newbies to get the ideas. Lots of these newbies, including me six months ago, switch to another GUI toolkit. That was wasteful because in my opinion, using Gtkmm/C++ is way better than GTK+/C++ (I have never used Qt4 so I can not give my opinion here. But anyway…).

Using Gtkmm, I was able to create a powerful GUI for my Dingo Music Manager. I wrote less codes in Gtkmm/C++ than in GTK+/C++. However, the best part was that my class became less “static”. I am also integrating gstreamermm, SQLite3 and cURLpp into Dingo. I am planning on releasing it this October. Here is a screenshot of the application.

dingo-music
Dingo Music Manager

Those reasons above pushes me to start writing an easy-to-understand and very foundation tutorial for Gtkmm. I really hoped that by reading my tutorials, you guys as newbie Gtkmm developers will not have to waste several months on learning GTK+ just to switch to Gtkmm like I did, and fully understand the GTK concept to apply to other programming languages (Well most of them have better documentation than Gtkmm).

Lastly, feel free to leave comments and questions. I am willing correct the mistakes in this tutorial and answer your questions, although I am nowhere near a Gtkmm master. I strongly advise you to subscribe to the gtkmm mailing list to ask questions as well as keep-in-touch with gtkmm’s changes and development process.

Have fun hacking Gtkmm!!

Advertisements

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.

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.