[gtkmm] drawing from a non-gui thread.



I'm writing an application, which has a GUI thread and a work thread. The
work thread runs in a loop and must draw something on the gui every 10000
iterations, say (as opposed to using a timer, as in the radar.cc example).
Please see the attached file.

Since I haven't seen any example doing precisely that (drawing from a
non-gui thread), I'm thinking to contribute this to the examples, but I'd
like to check its correctness with the list. Does anybody see any
obvious flaws?

Thanks!
// Simple thread example: gui thread and work thread. 
// After some number of iterations, the work thread draws something on the gui.
//
// The work thread is started/stopped by the "Run" button.
//
// Inspired from radar.cc and thread.cc.
// 
// g++ -o draw `pkg-config --cflags --libs gtkmm-2.0 gthread-2.0` draw.C
//

#include <gtkmm/drawingarea.h>
#include <gdkmm/colormap.h>
#include <gdkmm/window.h>


#include <gtkmm/button.h>
#include <gtkmm/togglebutton.h>
#include <gtkmm/box.h>
#include <gtkmm/statusbar.h>

#include <gtkmm/window.h>
#include <gtkmm/main.h>

#include <cmath>
#include <sstream>

#include <iostream>
#include <vector>

#include <glibmm/thread.h>
#include <sigc++/class_slot.h>

#include <gdkmm/general.h>



class m_DrawingArea : public Gtk::DrawingArea
{
public:
    m_DrawingArea(const u_int MaxIter=10000);
    virtual ~m_DrawingArea() {};

    void thread_function();

    bool m_run;
protected:
    virtual void on_realize();
    virtual bool on_expose_event(GdkEventExpose* event);

    Glib::RefPtr<Gdk::GC> gc_;
    Gdk::Color blue_, red_, green_, black_, white_, grey_, yellow_;

    Glib::Mutex mutex_;
    u_int loop_variable;
    u_int max_iterations;

};



m_DrawingArea::m_DrawingArea(const u_int MaxIter)
    : m_run(0), loop_variable(0), max_iterations(MaxIter)
{
    // get_window() would return 0 because the Gdk::Window has not yet been realized
    // So we can only allocate the colors here - the rest will happen in on_realize().
    Glib::RefPtr<Gdk::Colormap> colormap = get_default_colormap();

    blue_ = Gdk::Color("blue");
    red_ = Gdk::Color("red");
    green_ = Gdk::Color("green");

    black_ = Gdk::Color("black");
    white_ = Gdk::Color("white");
    grey_ = Gdk::Color("grey");

    yellow_ = Gdk::Color("yellow");

    colormap->alloc_color(blue_);
    colormap->alloc_color(red_);
    colormap->alloc_color(green_);
    colormap->alloc_color(black_);
    colormap->alloc_color(white_);
    colormap->alloc_color(grey_);
    colormap->alloc_color(yellow_);

    add_events(Gdk::EXPOSURE_MASK);
}


void m_DrawingArea::on_realize()
{
    // We need to call the base on_realize()
    Gtk::DrawingArea::on_realize();

    // Now we can allocate any additional resources we need
    Glib::RefPtr<Gdk::Window> window = get_window();

    gc_ = Gdk::GC::create(window);

    window->set_background(black_);
    window->clear();

    gc_->set_foreground(green_);
}


bool m_DrawingArea::on_expose_event(GdkEventExpose * event)
{
    Glib::RefPtr<Gdk::Window> window = get_window();

    // window geometry: x, y, width, height, depth
    int winx, winy, winw, winh, wind;
    window->get_geometry(winx, winy, winw, winh, wind);
    window->clear();

    // More stuff here
    
    return true;
}


void m_DrawingArea::thread_function()
{
    u_int i, j;
    int winx, winy, winw, winh, wind;

    Glib::RefPtr<Gdk::Window> window = get_window();

    // window geometry: x, y, width, height, depth
    window->get_geometry(winx, winy, winw, winh, wind);
    gc_->set_foreground(green_);

//     while(m_run && loop_variable<max_iterations){
    while(m_run){
	if(loop_variable%10000==0){
	    i=rand()%400;
	    j=rand()%600;

	    Glib::Mutex::Lock lock(mutex_);
	    window->draw_point(gc_, j, i);
// 	    Gdk::flush();
	}

	loop_variable++;
    }
}


class AppWindow : public Gtk::Window
{
public:
    AppWindow(const u_int MaxIter=10000);
    virtual ~AppWindow();
  
protected:
    // Child widgets
    Gtk::VBox m_box;
    Gtk::ToggleButton m_button;
    m_DrawingArea m_darea;

    void on_toggle_button_clicked();

};

AppWindow::AppWindow(const u_int MaxIter) 
    : m_box(false, 5), m_button("Run"), m_darea(MaxIter)
{
    m_button.signal_clicked().connect( 
	SigC::slot(*this, &AppWindow::on_toggle_button_clicked) );

    add_events(Gdk::EXPOSURE_MASK);

    m_darea.set_size_request(600,400);
    m_box.pack_start(m_darea, Gtk::PACK_EXPAND_WIDGET, 5);
    m_box.pack_start(m_button, Gtk::PACK_SHRINK, 5);
    add(m_box);
    show_all();
}

AppWindow::~AppWindow()
{

}


void AppWindow::on_toggle_button_clicked()
{
    bool state=m_button.get_active();
    if(state){
	std::cout << "active" << std::endl;
	m_button.set_label("Running");
	m_darea.m_run=1;
	Glib::Thread * const Work = Glib::Thread::create(
	    SigC::slot_class(m_darea, &m_DrawingArea::thread_function), true);
// 	    SigC::slot(&AppWindow::thread_function), true);
    }
    else{
	std::cout << "inactive" << std::endl;
	m_button.set_label("Stopped");
	m_darea.m_run=0;	
    }
}


int main(int argc, char** argv)
{
    Glib::thread_init();

    Gtk::Main main_instance (argc, argv);

    AppWindow draw(600*400);
    Gtk::Main::run(draw);

    return 0;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]