Gtkmm/Glibmm Thread Example

After dealing with threads in Qt and seeing how nice and simple it was, I decided to try again with the Gtkmm/Glibmm threading system. I think the crucial piece for me with the Qt threads was the excellent documentation, so I’m going to show you my full example here and document it in detail. As a quick disclaimer, I do not know if this is the 100% “right” way to do threads, but it matches up with everything I’ve read and, more importantly, it works.

Where to start…

I’m going to start with a brief description of what is going to happen. We will develop a threaded worker class, a trivial Gtk::Window class and a trivial main function. Let’s get the first two out of the way early, since they are the easy ones.

int main ()

82
83
84
85
86
87
88
89
90
int main (int argc , char ** argv) {
 
  if(!Glib::thread_supported()) Glib::thread_init();
 
  Gtk::Main kit (argc, argv);
  MyWindow win;
  Gtk::Main::run (win);
  return 0;
}

This is a pretty standard main function, with one notable nod to Glibmm, and that is line 84.

84
if(!Glib::thread_supported()) Glib::thread_init();

You only ever call Glib::thread_init(); once, otherwise it will abort with an error. So what we are doing is asking Glib “Are you already handling threads?” and if it isn’t we initialize the threading system. This has the additional perk of aborting the program if threads are not supported at all.

If the rest of this function doesn’t make sense to you, walk away from this tutorial and go read the Gtkmm one.

MyWindow

Next we are going to implement that Gtk::Window class that we use in main, ever so imaginatively called MyWindow. There is nothing much special going on, so I’ll show you the full listing then pick out the important bits.

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class MyWindow : public Gtk::Window {
 
  public:
    MyWindow () : thrashHive("Go!") {
      bee = NULL;
      thrashHive.signal_clicked().connect(sigc::mem_fun(*this, &MyWindow::go));
      add(thrashHive);
      show_all();
    }
 
    void go () {
      if(bee != NULL)
        return;
 
      bee = new Worker();
      bee->sig_done.connect(sigc::mem_fun(*this, &MyWindow::beeDone));
      bee->start();
 
      thrashHive.set_sensitive(false);
      thrashHive.set_label("Working");
    }
 
    void beeDone () {
      delete bee;
      bee = NULL;
      thrashHive.set_sensitive(true);
      thrashHive.set_label("Go!");
    }
 
  private:
    Gtk::Button thrashHive;
    Worker * bee;
 
};

As you can tell, this is a simple Gtk::Window with just a single button (thrashHive) in it. In the constructor we connect this button to a method go() at line 52. go() is important to us because it is where we create and launch our thread, which is represented as the class Worker of which we have a pointer declared at line 78 called bee. Let’s run through go() line by line.

58
59
      if(bee != NULL)
        return;

In lines 58 and 59 we are protecting our memory from trying to create multiple instances of a Worker thread, a good idea to stay safe in an asynchronous environment.

61
62
63
      bee = new Worker();
      bee->sig_done.connect(sigc::mem_fun(*this, &MyWindow::beeDone));
      bee->start();

At line 61 we instantiate a new Worker class. Next at line 62 we connect the signal sig_done to our beeDone() method, which essentially just undoes everything we do in go(). We do this because this is a non-blocking, asynchronous working thread and we need to know when it is finished running. When we get to the implementation of the Worker class, you’ll see that this “signal” is not a given, but rather something we implement ourselves, and we can have as many “signals” for events as needed. Lastly we start our thread at line 63.

65
66
      thrashHive.set_sensitive(false);
      thrashHive.set_label("Working");

Here we disable our button and change it’s text. Why do this here and not before thread creation? In most cases that would be a good idea, but here we want to demonstrate that the main thread is still working, so we do updates to the GUI after we start the worker thread.

Worker

The Worker class is actually fairly simple. It has a basic set of steps:

  1. Create and wait to be started.
  2. Get started, create thread and run.
  3. Do work, emit signals, check for being aborted.
  4. Return when done, then wait around until cleaned up.

Just breeze over the full listing then we’ll go over each of those points one at a time.

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Worker {
  public:
 
    Worker() : thread(0), stop(false) {}
 
    // Called to start the processing on the thread
    void start () {
      thread = Glib::Thread::create(sigc::mem_fun(*this, &WgetWorker::run), true);
    }
 
    // When shutting down, we need to stop the thread
    ~Worker() {
      {
        Glib::Mutex::Lock lock (mutex);
        stop = true;
      }
      if (thread)
        thread->join(); // Here we block to truly wait for the thread to complete
    }
 
    Glib::Dispatcher sig_done;
 
  protected:
    // This is where the real work happens
    void run () {
 
      while(true) {
        {
          Glib::Mutex::Lock lock (mutex);
          if (stop) break;
        }
        sleep(5);
        std::cout << "Thread write!" << std::endl;
        sig_done();
        break;
      }
    }
 
    Glib::Thread * thread;
    Glib::Mutex mutex;
    bool stop;
};

We don’t have anything to deal with for the first point. In the constructor we just initialize the Glib::Thread to 0 and our sentinel boolean, stop to false.

For the second point, we need to look at start() and run().

start() is our public method, and it creates and runs the actual Glib::Thread. Doing that is fairly straight forward:

10
11
12
    void start () {
      thread = Glib::Thread::create(sigc::mem_fun(*this, &WgetWorker::run), true);
    }

The run() method is where our blocking operations go so that we don’t have to worry about gumming up the UI, this is where the work actually happens (i.e. step 3).

27
28
29
30
31
32
33
34
35
36
37
38
39
    void run () {
 
      while(true) {
        {
          Glib::Mutex::Lock lock (mutex);
          if (stop) break;
        }
        sleep(5);
        std::cout << "Thread write!" << std::endl;
        sig_done();
        break;
      }
    }

In my run() I just wait five seconds and write to stdout, but if you are doing a lengthy process, you should occasionally check the stop variable for an abort condition by using lines 30 - 33. Always put your Glib::Mutex::Lock inside of their own block, it is easier to handle them that way as the destructor does the cleanup work for you.

On line 37 we see a “signal” being emitted. This is actually a Glib::Dispatcher, which uses pipes for asynchronous communication between threads. In practical application just treat it like a sigc::signal . If you need to communicate data, emit the Glib::Dispatcher and have the receiving thread do method calls to get the information from stored values in the sending thread.

One easy to miss caveat when using gthreads, you have to throw in some extra libraries with pkg-config –libs gthread-2.0, here is the command line to compile this demo application.

g++ -Wall -o bee gthread.cpp `pkg-config gtkmm-2.4 --cflags --libs` `pkg-config --libs gthread-2.0`

That is the guts of a simple Gtkmm/Glibmm worker thread, I hope that I was clear if not concise. Here is the complete listing of the program for your reading/borrowing/compiling pleasure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <iostream>
#include <gtkmm.h>
 
class Worker {
  public:
 
    Worker() : thread(0), stop(false) {}
 
    // Called to start the processing on the thread
    void start () {
      thread = Glib::Thread::create(sigc::mem_fun(*this, &WgetWorker::run), true);
    }
 
    // When shutting down, we need to stop the thread
    ~Worker() {
      {
        Glib::Mutex::Lock lock (mutex);
        stop = true;
      }
      if (thread)
        thread->join(); // Here we block to truly wait for the thread to complete
    }
 
    Glib::Dispatcher sig_done;
 
  protected:
    // This is where the real work happens
    void run () {
 
      while(true) {
        {
          Glib::Mutex::Lock lock (mutex);
          if (stop) break;
        }
        sleep(5);
        std::cout << "Thread write!" << std::endl;
        sig_done();
        break;
      }
    }
 
    Glib::Thread * thread;
    Glib::Mutex mutex;
    bool stop;
};
 
class MyWindow : public Gtk::Window {
 
  public:
    MyWindow () : thrashHive("Go!") {
      bee = NULL;
      thrashHive.signal_clicked().connect(sigc::mem_fun(*this, &MyWindow::go));
      add(thrashHive);
      show_all();
    }
 
    void go () {
      if(bee != NULL)
        return;
 
      bee = new Worker();
      bee->sig_done.connect(sigc::mem_fun(*this, &MyWindow::beeDone));
      bee->start();
 
      thrashHive.set_sensitive(false);
      thrashHive.set_label("Working");
    }
 
    void beeDone () {
      delete bee;
      bee = NULL;
      thrashHive.set_sensitive(true);
      thrashHive.set_label("Go!");
    }
 
  private:
    Gtk::Button thrashHive;
    Worker * bee;
 
};
 
int main (int argc , char ** argv) {
 
  if(!Glib::thread_supported()) Glib::thread_init();
 
  Gtk::Main kit (argc, argv);
  MyWindow win;
  Gtk::Main::run (win);
  return 0;
}

Posted September 30th, 2008 - Permalink
Categories: C++ - GTK+ - Programming - Qt - Work
No Comments »
 
KickTweet or Creating a KDE3 Panel Applet

I started using Twitter again last week and wanted a Linux GUI client. I looked at the standard bunch, but none of them did it for me. I wanted something small and easy. Some had system tray modes, but I decided to make my own that would reside as a panel applet.

Doing the API interaction is easy, it’s just web services and I’ve used libcurl enough before to have that down. It’s the Qt/KDE piece that I wasn’t so sure about. I got started with my good friend Google, and found a few resources. If you are interested in creating a panel applet for KDE 3.5 or so, ignore the Google results they will only serve to lead you in the wrong direction!

Getting Started
I got started by firing up KDevelop and selecting “Project -> New Project” then choosing the “C++ -> KDE -> Kicker Applet” project. This will generate a nice framework for you to start developing your application on. It’s just like creating any other Qt application, but in a smaller space.

Caveats
There are a few small things that you won’t get unless you poke around a bit. The biggest problems I had were getting the applet to expand properly and getting my input text box to accept focus. Both of these problems were solved by reading the code from “mathapplet.cpp” by Andrew Coles in the KDE source tree.

I’ll start with the expanding issue. The first fix is to change the constructor to pass KPanelApplet::Stretch instead of the default KPanelApplet::Normal, as shown below.

1
2
3
4
5
6
extern "C" {
  KPanelApplet* init( QWidget *parent, const QString& configFile) {
    KGlobal::locale()->insertCatalogue("KickTweet");
    return new KickTweet(configFile, KPanelApplet::Stretch, KPanelApplet::About, parent, "KickTweet");
  }
}

This only fixes part of the problem though. If you load it into the panel as it is now, it will keep resizing until it has pressed all the other applets aside. This is because we need to fix the default implementation of widthForHeight(int height) and heightForWidth(int width). My fix here was to just feed back a standard value regardless of the current size requests. I copied mathapplet in sending back 110 for width and 22 for height. It doesn’t seem to be causing any problems yet.

The next issue is the input box focus. Again, this is taken from the mathapplet code and it is a simple if non-obvious fix. Just add watchForFocus(tweetText); to your constructor, where tweetText is your QLineEdit or derived. This is of course only relevant if you are using a text entry widget.

Testing
You can test your applet the easy way or the hard way. I tried it the hard way for quite a while, then when I got fed up I found the easy way.

the hard way is to do “make install” then add the applet to your panel. You will not notice changes if you do this version, because even if you add and remove the applet from the panel, it is not unload from memory. To get it unloaded you must restart the panel, easily done with a call to “dcop kicker default restart” This is slow an unwieldy however, and I recommend you only use it for testing the actual integration into the panel.

The easier way to test your applet is to still do “make install” but then to run your associated desktop file with appletproxy, like so “appletproxy /opt/kde3/share/apps/kicker/applets/kicktweet.desktop“. This lets you do quick and easy debugging and will send your cout and cerr to the terminal if you run it from one.

Conclusion
Creating a kicker applet is not nearly as hard as I thought it might be. After several missteps I now have “KickTweet” which readily scratches my Twitter itch.

KickTweet in action

Posted September 18th, 2008 - Permalink
Categories: C++ - Internet - KickTweet - Programming - Projects - Qt
No Comments »
 
CameraBooth Monitor

I’ve been working on CameraBooth some more and I separated out a launcher/monitor window for the application. You can load configurations then run them without having to use the command line. In that same vein you “should” be able to use it on multiple monitors now, so you can have your launcher on one screen and the booth window on another. I haven’t been able to test that for lack of having multiple monitors.

This is just a “preview” of what’s to come, right now it is super unstable. I don’t thread correctly, I just kind of hijack the glib main loop, so it’s a bit prone to crashes. I’ll fix that soon though. Until then here are some preview images of the new monitor screen.


Showing a failed booth window launch (didn’t plug in camera)


Showing the “live” monitor view. You can see what’s going on in the booth as well as monitor events like image captures.

Posted July 14th, 2008 - Permalink
Categories: C++ - CameraBooth - GTK+ - Programming - Projects
No Comments »
 
libvcvideo Working

I started yet another project a few weeks ago. This one, called libvcvideo, is intended to be a super simple way to access video devices, especially web cams. I’ve been writing it to replace the more feature rich, but heavier OpenCV in my other projects. Yesterday in class I got the first working product, so I made a flurry of refinements, added some documentation, and I’m now showing it off.

It’s nothing amazing yet, it only works in Linux on V4L devices that are web cams and use the RGB24 data format. That said, it’s still a lot of devices, especially from the spca5xx and gspca drivers.

I’ll be adding more to it over time, but here is some proof of concept items.

Here is a fullscreen shot of the library running my gtkmm test program. The library was compiled with it’s debug option which provides all that information in the terminal window.

One cool thing I added is a sigc++2 signal for measuring the progress of operations. Here I have it hooked up to my gtkmm test program showing where it is in the initialization process.

Here is the first image I captured with the library. If it looks a little off color, it is because I hadn’t corrected the byte ordering on the format yet, so blue and red are swapped. Yes, it is a boring first image, but the camera was under my desk and I didn’t want to offend the teacher by openly working on this during a macroeconomics lecture.

Here is about the most simple example possible. It safely creates, opens, initializes, and grabs a frame from the device “/dev/video0″.

#include <iostream >
using std::cerr;
using std::endl;
#include <string >
using std::string;
 
#include "lib/videoDevice.h"
 
int main (int argc, char ** argv) {
  vc::videoDevice device ("/dev/video0");
  vc::vdFrame frame;
 
  try {
    device.init();
  }
  catch(string s) {
    cerr << "Device initialization failed: " << s << endl;
    exit(1);
  }
 
  try {
    device.getFrame(frame);
  }
  catch(string s) {
    cout << "Failed to get frame: " << s << endl;
    exit(1);
  }
}

You can get more details on the project page.

Posted June 18th, 2008 - Permalink
Categories: C++ - Linux - Programming - libvcvideo
No Comments »
 
The Mana World Buddy

Wrote a little buddy list application to The Mana World RPG. Nothing fancy, but I got to try out libcurl.
Go To Project Page

Posted April 7th, 2008 - Permalink
Categories: C++ - GTK+ - Programming - Projects - tmwbuddy
No Comments »
 
More Posts
« Older
Newer »