Chapter 17. Timeouts, I/O and Idle Functions

Table of Contents

Timeouts
Monitoring I/O
Idle Functions

Timeouts


Google

You may be wondering how to make gtkmm do useful work while it's idling along (well, sleeping actually) in Gtk::Main::run(). Happily, you have several options. Using the following methods you can create a timeout method that will be called every few milliseconds.

SigC::Connection Glib::SignalTimeout::connect(const SigC::Slot0<bool>& slot, unsigned int interval, int priority = Glib::PRIORITY_DEFAULT);

The first argument is a slot you wish to have called when the timeout occurs. The second argument is the number of milliseconds between calls to your method. You get back a SigC::Connection object that can be used to destroy the connection. Use

MyConnection.disconnect();

to destroy the connection. Another way of destroying the Connection is your signal handler. It has to be of the type SigC::Slot0<bool>. As you see from the definition your signal handler has to return a value of the type bool. A definition of a sample method might look like this:

bool MyCallback() { std::cout << "Hello World!\n"; return true; }

You can stop the timeout method by returning false from your signal handler. Therefore, if you want your method to be called repeatedly, it should return true.

Here's an example of this technique:

Source location: examples/timeout/timeout.cc

#include <iostream>
#include <gtkmm/button.h>
#include <gtkmm/box.h>
#include <gtkmm/main.h>
#include <gtkmm/window.h>
#include <map>


class TimerExample : public Gtk::Window
{
  // the usual stuff - nothing exciting
  Gtk::HBox   m_box;
  Gtk::Button m_add_timer, m_del_timer, m_quit;
  int m_t_nr;

  // the start value for our timer
  static const int COUNT_VALUE;

  // the timeout value for the timers in [ms]
  static const int TIMEOUT_VALUE;

  // we need this to store our connections
  std::map<int,SigC::Connection> m_timers;

  // this is for storing our timer values
  // each timer countsa back from COUNT_VALUE to 0 and
  // if removed when it reaches 0
  std::map<int,int> m_counters;

public:
  TimerExample();

  // the signal handlers for add & remove button
  void add_timer_pressed();
  void del_timer_pressed();

  // the signal handler for the timer
  // note that is not of the type int callback()
  // since we use bind() to add a data value of type int to it
  int timer_callback(int timer_nr);
};

const int TimerExample::COUNT_VALUE = 5;
const int TimerExample::TIMEOUT_VALUE = 1500;


TimerExample::TimerExample() :
  m_add_timer("add a new timer"),
  m_del_timer("remove timer"),
  m_quit("Quit"),
  m_box(true,10),
  m_t_nr(0)
{
  // connect the signal handlers
  m_quit.pressed.connect(&Gtk::Main::quit);
  m_add_timer.pressed.connect(slot(this,&TimerExample::add_timer_pressed));
  m_del_timer.pressed.connect(slot(this,&TimerExample::del_timer_pressed));

  // put buttons into container
  m_box.pack_start(m_add_timer);
  m_box.pack_start(m_del_timer);
  m_box.pack_start(m_quit);

  // set border and display all
  set_border_width(10);
  add(m_box);
  show_all();
}


void TimerExample::add_timer_pressed()
{
  // creation of a new object prevents long lines and
  // shows us a little how slots work
  // we have 0 parameters and int as return value after calling bind
  SigC::Slot0<int> my_slot = bind(slot(this,&TimerExample::timer_callback),m_t_nr);

  // now connect the slot to Gtk::Main::timeout
  Gtk::Connection conn = Gtk::Main::timeout.connect(my_slot,TIMEOUT_VALUE);

  // memorize connection
  m_timers[m_t_nr] = conn;

  // initialize timer count
  m_counters[m_t_nr] = COUNT_VALUE + 1;

  // print some information on the console
  cout << "added timeout " << m_t_nr++ << endl;
}


void TimerExample::del_timer_pressed()
{
  // are there any timers ?
  if(m_timers.empty()) {
    // nope
    cout << "sorry, there are no timers left" << endl;
  } else {
    // get the nr of the first timer
    int timer_nr = m_timers.begin()->first;
    // give a little information to the user
    cout << "removing timer " << timer_nr << endl;
    // delete the entry in the counter values
    m_counters.erase(timer_nr);
    // destroy the connection !!!!!
    // this is important since the connection is NOT destroyed when
    // the according Connection-Object is deleted
    // The purpose of the connection object is to give you the
    // possibility to destroy a connection without having to destroy
    // either the sender or the receiver
    // Try it and comment out the following line ....
    m_timers[timer_nr].disconnect();
    // destroy the connection
    m_timers.erase(timer_nr);
  }
}


int TimerExample::timer_callback(int timer_nr)
{
  // print the timernr
  cout << "This is timer " << timer_nr;
  // decrement & check counter value
  if(--m_counters[timer_nr] == 0) {
    cout << " boom" << endl;
    // delete the counter entry
    m_counters.erase(timer_nr);
    // delete the connection entry
    m_timers.erase(timer_nr);
    // note that we do not need to call disconnect on the connection
    // since we Gtk::Main does this for us when we return 0
    return 0;
  }

  // print the timer value
  cout << " - " << m_counters[timer_nr] << "/" << COUNT_VALUE << endl;
  return 1;
}


// the intresting stuff ends here


int main (int argc, char *argv[])
{
  Gtk::Main app(argc, argv);

  TimerExample example;

  app.run();
  return 0;
}