Next Previous Contents

15. Timeouts, I/O and Idle Functions

15.1 Timeouts

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

Connection Gtk::Main::timeout.connect(const SlotType &sd, guint32 interval);

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 function. You get back a 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 callback function. It has to be of the type Slot0<gint>. As you see from the definition your callback function has to return a value of the type gint. A definition of a sample function might look like this:

gint MyCallback() { cout << "Hello World!"; return true; }

You can stop the timeout function by returning zero or false from your callback function. Therefore, if you want your function to be called repeatedly, it should return a non-zero value, or true.

Here's an example of this technique:

Source location: examples/timeout/timeout.cc

#include <iostream>
#include <gtk--/button.h>
#include <gtk--/box.h>
#include <gtk--/main.h>
#include <gtk--/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;
  gint m_t_nr;

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

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

  // we need this to store our connections
  map<gint,Gtk::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
  map<gint,gint> m_counters;

public:
  TimerExample();
  
  // the callback functions for add & remove button
  void add_timer_pressed();
  void del_timer_pressed();

  // the callback for the timer
  // note that is not of the type gint callback(void)
  // since we use bind() to add a data value of type gint to it
  gint timer_callback(gint timer_nr);
  
  // this will end the application when its window is closed
  gint delete_event_impl(GdkEventAny*);
};

const gint TimerExample::COUNT_VALUE = 5;
const gint 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 callbacks
  m_quit.pressed.connect(Gtk::Main::quit.slot());
  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 gint as return value after calling bind 
  SigC::Slot0<gint> 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
    gint 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);
  }
}


gint TimerExample::timer_callback(gint 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


gint TimerExample::delete_event_impl(GdkEventAny*)
{ 
  Gtk::Main::quit(); return 0;
}



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

  TimerExample example;

  app.run();
  return 0;
}

15.2 Monitoring I/O

A nifty feature of GDK (one of the libraries that underlying GTK--) is the ability to have it check for data on a file descriptor for you. This is especially useful for networking applications. The following function is used to do this:

Connection Gtk::Main::input.connect(const SlotType& sd, gint source,
                                    GdkInputCondition condition);

The first argument is a slot you wish to have called when then the specified event (see argument 3) occurs on the file descriptor you specify using argument two. Argument three may be one or a combination (using |) of:

The return value is a Connection that may be used to stop monitoring this file descriptor using the disconnect following function. The callback function should be declared as follows:

void input_callback(gint source, GdkInputCondition condition);

where source and condition are as specified above. As usual the slot is created with slot() and can be a member function of an object.

A little (and somewhat dirty) example follows as usual. To use the example just execute it from a terminal; it doesn't create a window. It will create a pipe named testpipe in the current directory. Then start another shell and execute cat >testpipe. The example will print each line you enter until you type quit.

Source location: examples/input/input.cc

#include <gtk--/main.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fstream>
#include <iostream>
#include <memory> 

using std::istream;

using std::auto_ptr;

using SigC::slot;

auto_ptr<istream> input;


// this will be our callback for read operations
// there is not much to say. just read a string,
// print it and quit the application if the string was quit
void MyCallback(int, GdkInputCondition) {
  Gtk::string dummy;
  do {
    (*input) >> dummy;
    cout << dummy << endl;
    if(dummy == "quit") Gtk::Main::quit();
  } while(input->fail());
}


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

  // create a fifo for testing purposes
  if (mkfifo("testfifo",0666) != 0) { 
    cerr << "error creating fifo" << endl;
    return -1;
  }
  
  // open the fifo
  input=new ifstream("testfifo");
  
//    int fd = open("testfifo", 0);
//    if (fd == -1) {
//      cerr << "error opening fifo" << endl;
//      return -1;
//    }

  // assign the fifo's filedescriptor to our ifstream object
  //This sucks; it will only ever work with libstdc++-v3, as
  //  both istream::__filebuf_type and the basic_filebuf contructor
  //  that takes an fd are libstdc++-v3 specific.
  //input=new istream(new ifstream::__filebuf_type(fd,"testfifo"));
  
  // connect the callback function
  app.input.connect(slot(MyCallback), fd, GDK_INPUT_READ);

  // and last but not least - run the application main loop
  app.run();

  // now remove the temporary fifo
  if(unlink("testfifo")) 
    cerr << "error removing fifo" << endl;

  return 0;
}

15.3 Idle Functions

What if you have a function you want called when nothing else is happening? Hook it up using the following:

Connection Gtk::Main::idle.connect(Slot0<gint> idlefunc, gint priority);

This causes GTK-- to call the specified function whenever nothing else is happening. You can add a priority (lower numbers are higher priorities). If you don't supply a priority value, then GTK_PRIORITY_DEFAULT will be used. There are two ways to remove the callback: calling disconnect on the Connection object, or returning false (or 0) in the callback function, which should be declared as follows:

int idleFunc();

Since this is very similar to the functions above this explanation should be sufficient to understand what's going on. However, here's a little example:

Source location: examples/idle/idle.cc

#include <iostream>
#include <gtk--/adjustment.h>
#include <gtk--/button.h>
#include <gtk--/box.h>
#include <gtk--/label.h>
#include <gtk--/progressbar.h>
#include <gtk--/main.h>
#include <gtk--/window.h>
#include <map>


class IdleExample : public Gtk::Window
{
  // the usual stuff - nothing exciting
  Gtk::Button m_quit;
  Gtk::Adjustment  m_percentage_c;
  Gtk::ProgressBar m_progressbar_c;
  Gtk::Adjustment  m_percentage_d;
  Gtk::ProgressBar m_progressbar_d;

public:
  IdleExample();
  
  // a timer-function
  gint timer_callback();
  // a idle-function
  gint idle_callback();
  
  gint delete_event_impl(GdkEventAny*);
};


IdleExample::IdleExample() :
  m_quit("Quit"),
  m_percentage_c(0,0,100,0.5),
  m_progressbar_c(m_percentage_c),
  m_percentage_d(0,0,5000,0.5),
  m_progressbar_d(m_percentage_d)
{
  // connect the callbacks
  m_quit.pressed.connect(Gtk::Main::quit.slot());

  // put buttons into container
  Gtk::VBox *vbox = manage( new Gtk::VBox(false,5));

  // adding a few widgets
  vbox->pack_start(* manage(new Gtk::Label("Formatting windows drive C:")));
  vbox->pack_start(* manage(new Gtk::Label("100 MB")));
  vbox->pack_start(m_progressbar_c);
  m_progressbar_c.set_show_text(true);

  vbox->pack_start(* manage(new Gtk::Label("")));

  vbox->pack_start(* manage(new Gtk::Label("Formatting windows drive D:")));
  vbox->pack_start(* manage(new Gtk::Label("5000 MB")));
  vbox->pack_start(m_progressbar_d);
  m_progressbar_d.set_show_text(true);

  Gtk::HBox *hbox = manage( new Gtk::HBox(false,10));
  hbox->pack_start(m_quit, true, false);
  vbox->pack_start(*hbox);

  // set border and display all
  set_border_width(5);
  add(*vbox);
  show_all();
   
  // formattinf drive c in timeout callback ;-)
  Gtk::Main::timeout.connect(slot(this,&IdleExample::timer_callback), 50);

  // formatting drive d in idle callback ;-)
  Gtk::Main::idle.connect(slot(this,&IdleExample::idle_callback));
}


// increase the progressbar's value and remove callback when done
gint IdleExample::timer_callback()
{
  float value = m_percentage_c.get_value();
  m_percentage_c.set_value(value + 0.5);
  return value < 99.99;
}


// increase the progressbar's value and remove callback when done
// note the diffrence in speed and also the impact of system load
// try to increase system load and watch the drive d value
gint IdleExample::idle_callback()
{
  float value = m_percentage_d.get_value();
  m_percentage_d.set_value(value + 0.5);
  return value < 4999.99;
}


gint IdleExample::delete_event_impl(GdkEventAny*)
{ 
  Gtk::Main::quit(); return 0;
}


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

  IdleExample example;

  app.run();
  return 0;
}

This example points out the difference of idle and timeout functions a little. If you need functions that are called periodically, and speed is not very important, then you want timeout functions. If you want functions that are called as often as possible (like calculating a fractal in background), then use idle functions.

Try executing the example and increasing the system load. The upper progress bar will increase steadily; the lower one will slow down.


Next Previous Contents