[gtkmm] display image and draw from non-gui thread



Following suggestions from the list, I came up with the following code
(attached), in my quest to build a gui for a certain application.
Basically, an image is loaded from the disk and displayed into a drawing
area. The main window has also a toggle button which starts/stops a
non-gui thread. At fixed intervals the non-gui thread must draw something
into the drawing area (random points for now). I managed to do this now
with a Dispatcher. I followed the dispatcher.cc example, but I simplified
it (I don't have the signal_finished() stuff). Could somebody please give
me some feedback on the attached code? I want to make sure that I don't
have memory leaks, that widgets are distroyed when they should, etc.
In other words I want to make sure that everything is clean.

Thanks!

// draw.C - example of loading an image from file and drawing from a 
//          non-gui thread via a Dispatcher.
// 
// Silviu Minut - June 16 2003.
// 
// g++ -o draw draw.C `pkg-config --cflags --libs gtkmm-2.0 gthread-2.0`
//
//
// Main window composed of a drawing area and a toggle button.
// An image is loaded from a file and displayed in the drawing area.
// When the toggle button is toggled, a separate thread is started/stopped.
// The thread draws (random points for now) into the drawing area, via a 
// Dispatcher.
// This is the recommended approach for drawing from a non-gui thread.

#include <gtkmm/box.h>
#include <gdkmm/colormap.h>
#include <gtkmm/togglebutton.h>
#include <gtkmm/drawingarea.h>
#include <gtkmm/window.h>
#include <gtkmm/image.h>
#include <gtkmm/main.h>

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

#include <iostream>
#include <cstdlib>
#include <cmath>

using namespace std;


///////////////////////////////////////////////////////////////////////////////
//
// Drawing area with a picture in the background.
// 
///////////////////////////////////////////////////////////////////////////////

class ImgViewer : public Gtk::DrawingArea
{
public:
    ImgViewer();
    ~ImgViewer() {};

    void load(char * file);
    void draw_random_points();

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

    // A GC, some colors, and a pixbuf to display an image in the background
    Glib::RefPtr<Gdk::GC> gc_;
    Gdk::Color green_, black_;
    Glib::RefPtr<Gdk::Pixbuf> pixbuf_;
};


ImgViewer::ImgViewer()
{
    // Get the colors only. We don't have a window until realized().
    Glib::RefPtr<Gdk::Colormap> colormap = get_default_colormap();

    green_ = Gdk::Color("green");
    black_ = Gdk::Color("black");
    colormap->alloc_color(green_);
    colormap->alloc_color(black_);
    add_events(Gdk::EXPOSURE_MASK);    
}

void ImgViewer::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();

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

    gc_ = Gdk::GC::create(window);
    gc_->set_foreground(green_);
}

void ImgViewer::load(char * file)
{
    u_int rows=0, cols=0;

    pixbuf_=Gdk::Pixbuf::create_from_file(file);

    rows=pixbuf_->get_height();
    cols=pixbuf_->get_width();
    cout << rows << "\t" << cols << endl;
    
    set_size_request(cols, rows);
}

bool ImgViewer::on_expose_event(GdkEventExpose * event)
{
    // On expose event only draw the image in the background
    int winx, winy, winw, winh, wind;
    
    Glib::RefPtr<Gdk::Window> window = get_window();
    window->get_geometry(winx, winy, winw, winh, wind);

    pixbuf_->render_to_drawable(window, gc_, 0,0, 0,0, winw, winh,
				Gdk::RGB_DITHER_NORMAL, 0, 0);
}


void ImgViewer::draw_random_points()
{
    int x, y;
    int winx, winy, winw, winh, wind;
    
    Glib::RefPtr<Gdk::Window> window = get_window();
    window->get_geometry(winx, winy, winw, winh, wind);
//     pixbuf_->render_to_drawable(window, gc_, 0,0, 0,0, winw, winh,
// 				Gdk::RGB_DITHER_NORMAL, 0, 0);
    x=rand()%winw;
    y=rand()%winh;
    window->draw_rectangle(gc_, 1, x,y,5,5);
}


///////////////////////////////////////////////////////////////////////////////
//
// Main window
//
///////////////////////////////////////////////////////////////////////////////

class AppWindow : public Gtk::Window
{
public:
    AppWindow();
    virtual ~AppWindow();
    void load(char * file) { iv.load(file); };

protected:

    // GUI stuff
    Gtk::VBox m_box;
    Gtk::ToggleButton m_button;
    ImgViewer iv;

    void on_toggle_button_clicked();

    // Thread stuff
    void thread_function();
    Glib::Dispatcher draw_;
    u_int iteration;
    bool m_run;
    void launch();
};

AppWindow::AppWindow() 
    : m_box(false, 5), m_button("Run"), iteration(0), m_run(0)
{
    m_button.signal_clicked().connect( 
	SigC::slot(*this, &AppWindow::on_toggle_button_clicked) );

    // Connect the dispatcher
    draw_.connect(SigC::slot(iv, &ImgViewer::draw_random_points));

    add_events(Gdk::EXPOSURE_MASK);

    m_box.pack_start(iv, Gtk::PACK_EXPAND_WIDGET, 5);
    m_box.pack_start(m_button, Gtk::PACK_SHRINK, 5);
    add(Gtk::manage(m_box));
    show_all();
}

AppWindow::~AppWindow()
{

}


void AppWindow::on_toggle_button_clicked()
{
    bool state=m_button.get_active();
    if(state){
	m_button.set_label("Running");
	m_run=1;
	launch();
	std::cout << "active" << "\t" << m_run << std::endl;
    }
    else{
	m_button.set_label("Stopped");
	m_run=0;	
	std::cout << "inactive" << "\t" << m_run 
		  << "\t" << iteration << std::endl;
    }
}

void AppWindow::thread_function()
{
    while(m_run){
	if(iteration%10000==0)
	    draw_();  // tell the GUI to draw
	iteration++;
    }
}

void AppWindow::launch()
{
    // Create a non-joinable thread -- deleted automatically on thread exit.
    Glib::Thread::create(SigC::slot_class(*this, &AppWindow::thread_function), 
			 false);
}


void usage(char * prog)
{
    cout << endl << "\e[1;31mUsage: \e[0m" 
	 << prog << " [Options] image_file" << endl
	 << endl << endl
	 << "\e[1;33mOptions: \e[0m" << endl
	 << endl;
    exit(-1);
}


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

    Gtk::Main mmain(argc, argv);
    AppWindow w;
    
    if(argc<2)
	usage(argv[0]);

    w.load(argv[1]);

    Gtk::Main::run(w);

    return 0;
}


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