Matias Martini Matias Martini - 3 years ago 189
C++ Question

GTK Blinks when drawing

I'm really new with GTK, and im trying to learn how to handle it for a project i have in mind.

The thing is, i could manage to make an app for placing "Generators" on a window ... Each time i click on the screen, the app place a gen in the window, and prepare another one ready to be placed ...
The problem is that the program blinks when is drawing all the generators ...
Each gen has to redraw itself all the time, and this is i think the problem... Here is the code ... how can i make it faster? ... Thanks in advance!!

// gcc main.c -o main `pkg-config gtk+-3.0 --cflags --libs`
#include <gtk/gtk.h>
#include <stdlib.h>
#include <iostream>
#include <vector>

using namespace std;

class Gen{
public:
int x;
int y;
GdkPixbuf *pix;
GtkWidget *canvas;
bool placed;

Gen(GtkWidget *canvas){
this->canvas=canvas;
GError *err = NULL;
pix = gdk_pixbuf_new_from_file("./Img/gen.png", &err);
pix= gdk_pixbuf_scale_simple(pix,50,50, GDK_INTERP_BILINEAR);
x=10;y=10;
placed=0;
}

void draw(cairo_t *cr){
gdk_cairo_set_source_pixbuf(cr, pix, x, y);
cairo_paint(cr);
}

void updatePosition(int a, int b){
if(placed==0){
x=a-25;
y=b-25;
}
}

void place(){
placed=1;
}

};

class Map{
public:
vector<Gen *> Gens;
GtkWidget *window;
GtkWidget *canvas;
int xPointer,yPointer;

Map(GtkWidget *_window, GtkWidget *_canvas){
window=_window;
canvas=_canvas;
}

void draw(){
cairo_t *cr;
cr = gdk_cairo_create (gtk_widget_get_window(canvas));
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_rectangle(cr, xPointer-35, yPointer-35, 70, 70);
cairo_paint(cr);
for(vector<Gen *>::const_iterator i=Gens.begin();i!=Gens.end();i++){
(*i)->draw(cr);
}
cairo_destroy (cr);
}
void place(){
Gen *aux=Gens.back();
aux->place();
//Gens.push_back(new Gen(canvas));
}
void moving(int x,int y){
xPointer=x;yPointer=y;
if(Gens.size()==0){
Gens.push_back(new Gen(canvas));
}
else if (Gens.back()->placed==1){
Gens.push_back(new Gen(canvas));
}
Gen *aux=Gens.back();
aux->updatePosition(x,y);
this->draw();
cout<<"Elementos -> "<<Gens.size()<<"\n";
}
};





static gboolean
moving(GtkWidget *da, GdkEvent *event, gpointer data)
{

int x, y;
GdkModifierType state;
gdk_window_get_device_position (gdk_event_get_window ((GdkEvent *) event),
gdk_event_get_device ((GdkEvent *) event),
&x, &y, &state);
/*
(void)event; (void)data;
((Gen *)da)->draw();*/
Map *g=(Map *)data;
g->moving(x,y);
}
static gboolean
placing (GtkWidget *da, GdkEvent *event, gpointer data)
{

Map *g=(Map *)data;
g->place();

}

int main ( int argc, char **argv) {


GtkWidget *window;
GtkWidget *canvas;
gtk_init (&argc , &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request (window,
500, 500);

g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit) , NULL);
canvas = gtk_drawing_area_new ();
Map *g=new Map(window,canvas);
gtk_container_add (GTK_CONTAINER (window), canvas);
gtk_widget_set_events(window, GDK_POINTER_MOTION_MASK);

//g_signal_connect (canvas, "draw", G_CALLBACK (drawing), (gpointer *)g);
g_signal_connect (window, "motion-notify-event", G_CALLBACK (moving), (gpointer *)g);
g_signal_connect (window, "button-press-event", G_CALLBACK (placing), (gpointer *)g);

//g_signal_connect (canvas, "motion-notify-event", (GCallback) on_window_draw, NULL);

gtk_widget_set_app_paintable(canvas, TRUE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

Answer Source

Several remarks here: Gen::pix is allocated each time a Gen object is created. It's the same pixbuf, but you create one, then another one when using the scaling function (meaning you're leaking memory of the original pixbuf), and this for each Gen object. This is really unefficient, so using a static pix member, loading then scaling the pixbuf and fixing the memory leak would allow you to do this only once.

Then: you're calling gdk_cairo_create in the draw handler, but since GTK 3, you're supposed to get the cairo context as an input parameter in the draw signal callback. I see you're calling a custom draw method through the motion events, that's not how the GTK+ drawing stack works!

To do it right:

  • in the main, connect to the draw signal of the GtkDrawingArea
  • in your motion callbacks, just change positions of the Gen objects, and call gtk_widget_queue_draw for the drawing area. This will fire the draw signal for you.
  • in the callback connected to the draw signal, you then redraw your Gen objects in the cairo context you're given.
  • to improve performance, you can use the cairo clipping functions, or call gtk_widget_queue_draw_area or gtk_widget_queue_draw_region instead of gtk_widget_queue_draw. You'll get then a pre-computed clipping region in the cairo context you'll receive in your draw callback. With those hints, you can determine exactly what part of the image needs to be redrawn, and avoid unnecessary work.

Please read The GTK+ Drawing Model in the official documentation.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download