Chapter 17. Timeouts, I/O and Idle Functions

Table of Contents

Timeouts
Monitoring I/O
Idle Functions

Timeouts

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::signal_timeout().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.

The following is an example of this technique, broken into three separate source files:

Source location: examples/timeout/timeout.h

#ifndef TIMEOUT_H
#define TIMEOUT_H

// Its more memory efficient to only include the header files that your 
// application will need rather than including <gtkmm.h>
#include <iostream>
#include <gtkmm/box.h>
#include <tkmm/button.h>
#include <gtkmm/window.h>
#include <map>

class TimerExample : public Gtk::Window
{
public:
    TimerExample();
    void add_timer_pressed();
    void del_timer_pressed();
    
    bool timer_callback(int timer_nr);

protected:    
    Gtk::HBox m_box;
    // Buttons for adding & deleting a timer and quitting the app
    Gtk::Button m_add_timer, m_del_timer, m_quit;
    // keep track of the timers being added
    int m_timer_number;
    // these two constants are initialized in the member initializer
    const int COUNT_VALUE;
    const int TIMEOUT_VALUE;
    // STL map for storing our connections
    std::map m_timers;
    // STL map for storing our timer values
    // each timer counts back from COUNT_VALUE to 0 and 
    // is removed when it reaches 0
    std::map m_counters;
};

#endif


Source location: examples/timeout/timeout.cpp

#include "timeout.h"

TimerExample::TimerExample() :
m_add_timer("add a new timer"),
m_del_timer("remove timer"),
m_quit("Quit"),
m_box(true,10),
m_timer_number(0),          // start numbering the timers at 0
COUNT_VALUE(5),             // each timer will count down 5 times before disconnecting
TIMEOUT_VALUE(1500)         // 1500 ms = 1.5 seconds
{
    // connect the three buttons
    m_quit.signal_pressed().connect(SigC::slot(*this, &Gtk::Widget::hide));
    m_add_timer.signal_pressed().connect(SigC::slot(*this,&TimerExample::add_timer_pressed));
    m_del_timer.signal_pressed().connect(SigC::slot(*this,&TimerExample::del_timer_pressed));
    
    m_box.pack_start(m_add_timer);
    m_box.pack_start(m_del_timer);
    m_box.pack_start(m_quit);
    
    set_border_width(10);
    add(m_box);
    show_all();            // show all the widgets, including the top level container
}

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 bool as a return value 
    // after calling bind.
    SigC::Slot0<bool> my_slot = bind(SigC::slot(*this,&TimerExample::timer_callback),
    m_timer_number);
    
    // This is where we connect the slot to the Glib::signal_timeout()
    SigC::Connection conn = Glib::signal_timeout().connect(my_slot,TIMEOUT_VALUE);
    
    // memorize the connection
    m_timers[m_timer_number] = conn;
    
    // initialize timer count
    m_counters[m_timer_number] = COUNT_VALUE + 1;
    
    // print some info to the console for the user
    std::cout << "added timeout " << m_timer_number++ << std::endl;
}

void TimerExample::del_timer_pressed()
{
    // any timers?
    if(m_timers.empty()) {
        // no timers left
        std::cout << "Sorry, there are no timers left!" << std::endl;
    } else {
        // get the nr of the first timer
        int timer_nr = m_timers.begin()->first;
        // give some info to the user
        std::cout << "manually disconnecting timer " << timer_nr << std::endl;
        // delete the entry in the counter values
        m_counters.erase(timer_nr);
        // destroy the connection!!
        // ***This is very important since the connection is NOT destroyed when
        // the corresponding 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.
        // (ie. since we are destroying the connection early, we must actually
        // call the disconnect() method on the connection object).
        // Try it and comment out the following line ....
        m_timers[timer_nr].disconnect();
        // destroy the connection
        m_timers.erase(timer_nr);
    }
}

bool TimerExample::timer_callback(int timer_nr)
{
    // print the timer
    std::cout << "This is timer " << timer_nr;
    // decrement and check counter value
    if (--m_counters[timer_nr] == 0) {
        std::cout << " being disconnected" << std::endl;
        // delete the counter entry in the STL MAP
        m_counters.erase(timer_nr);
        // delete the connection entry in the STL MAP
        m_timers.erase(timer_nr);
        // ***Note that we do not have to explicitly call disconnect() on the connection
        // since Gtk::Main does this for us when we return false.
        return false;
    }
    // print the timer value
    std::cout << " - " << m_counters[timer_nr] << "/" << COUNT_VALUE << std::endl;
    // keep going (ie. do not disconnect yet!)
    return true;
}


Source location: examples/timeout/main.cpp

#include <gtkmm/main.h>
#include "timeout.h"

int main(int argc, char *argv[])
{
    Gtk::Main kit(argc,argv);
    
    TimerExample example;
    
    Gtk::Main::run(example);
    
    return 0;
}