Re: the right way to draw in a drawingarea



To use the Gtk::DrawingArea widget to display a custom image or pixmap I wrote a class that does this for me ( the code is attached).

The class is in Spanish (sorry no time to translate the code now), but the code is simple enough to understand it quite well.

To do the drawing class I inherint from objeto_drawingarea and overwrite the configurar (configure) function so this function writes what we need to the objeto_drawingarea::pixmap.

Then when we want to paint the widget we simply have to call repintar (repaint).

Basically It uses queue_draw() to paint the pixmap created by the configure function into the DrawingArea window.

It also controls some basic events  (on_expose,on_realize,on_size_alloc...).

The code is not perfect ( I'm not perfect and my code even less) but hope it helps a bit.


From: Paul Davis <pjdavis engineering uiowa edu>
To: "Foster, Gareth" <gareth foster siemens com>
CC: gtkmm-list gnome org
Subject: Re: the right way to draw in a drawingarea
Date: Tue, 11 Oct 2005 05:11:46 -0500

Foster, Gareth wrote:

the display flickers, even when drawing a simple square.




Doesn't this often mean you need to be double buffering?
_______________________________________________
gtkmm-list mailing list
gtkmm-list gnome org
http://mail.gnome.org/mailman/listinfo/gtkmm-list


This is a point I am completely unsure about. I haven't the foggiest on Gtk internals and the ramifications of a simple call such as draw_rectangle. I've used double buffering in both OpenGL and Java. I've just never really seen any real reference to it in gtk. Now that I've had a couple beers, I seem to rememeber some reference to getting an offscreen graphics context, drawing to that, and then switching the main gc with it back and forth. But I could be completely making that up. ( the beer )

Double buffering isn't really that hard of a concept. I'm just not sure of if its needed. Because its more than possible that gtk has its own buffering scheme burried somewhere. I've actually written a couple programs to animate image data using Gtkmm. I've never had a problem with flickering that wasn't because I was doing something silly like clearing and then redrawing. So the answer is I dunno. Its possible. If I get time I'll write a test program to try and play with the idea.

And if I had thought to take a moment to google something as silly as gtkmm double buffering, I would have noticed that the gtkmm docs specify that widgets are double buffered by default. In which case, no double buffering will not solve this problem cause its already being done. Which goes to show that yes, I was making up the graphics context thing. Must be something from java I was thinking of.
http://www.gtkmm.org/docs/gtkmm-2.4/docs/reference/html/classGtk_1_1Widget.html#a175

In other news, Unleashed wasn't half bad if anyone cares.

Cheers,
Paul
_______________________________________________
gtkmm-list mailing list
gtkmm-list gnome org
http://mail.gnome.org/mailman/listinfo/gtkmm-list

#include "objeto_drawingarea.hh"


/* ************************
   objeto_drawingarea
   ********************* */
objeto_drawingarea::objeto_drawingarea()
{
  // Lo primero es inicializar
  this->inicializar();

} // Fin de constructor

/* **************************
   ~objeto_drawingarea
   *********************** */
objeto_drawingarea::~objeto_drawingarea()
{
  //fprintf(stderr,"INICIO Destructor objeto_drawingarea\n");

  //fprintf(stderr,"FIN Destructor objeto_drawingarea\n");
} // Fin del constructor

/* ******************
   inicializar
   *************** */
// Funcion que inicializa los valores de la clase
void objeto_drawingarea::inicializar(void)
{
  // Inicializamos los colores minimos
  for(gint i=0;i<NUM_COLOR;i++)
    this->colores.push_back( Gdk::Color("white") );
  this->colores[0] = Gdk::Color("gray");
  this->colores[1] = Gdk::Color("black");
  this->colores[2] = Gdk::Color("aquamarine4");
  this->colores[3] = Gdk::Color("blue");

  // Ponemos los colores de fondo y de "frente"
  this->bg=0;this->fg=1;

  // Ponemos los signals
   
  this->signal_expose_event().connect(sigc::mem_fun(*this, &objeto_drawingarea::on_expose_event));
  this->signal_realize().connect(sigc::mem_fun(*this,&objeto_drawingarea::on_realize));
  this->signal_size_allocate().connect(sigc::mem_fun(*this, &objeto_drawingarea::on_size_alloc));
  

} // Fin de inicializar

/* *****************
   meter_color
   ************** */
// Funcion q permite meter un color nuevo al vector de colores
gint objeto_drawingarea::meter_color(Gdk::Color color)
{
  // Comprobamos si el color ya existe, ir pa na es tonteria
  int i=0;
  int size=this->colores.size();
  int ret=0;
  int r,g,b;
  r=color.get_red();
  g=color.get_green();
  b=color.get_blue();
  while((i<size)&&(ret==0))
  {
    if((r==this->colores[i].get_red())&&(g==this->colores[i].get_green())&&(b==this->colores[i].get_blue()))
      ret=1;
    else
      i++;
  }

  // Comprobamos si hemos encontrado el color
  if(ret!=0)
  {
    // El color ya existe
    return i; // Devolvemos la posicion del color dentro del vector
  }

  // No existe el color lo metemos
  this->colores.push_back(color);
  this->colormap->alloc_color(this->colores[i]);

  return i;

} // Fin de meter_color(Gdk::Color)
// Sobrecarga
gint objeto_drawingarea::meter_color(char *str)
{
  if(!str)
  {
    this->error("meter_color : Cadena vacia");
    return -1;
  }

  Gdk::Color color = Gdk::Color(str);

  return this->meter_color(color);
} // Fin de meter_color(char *)
// Sobrecarga
gint objeto_drawingarea::meter_color(int r,int g, int b)
{
  Gdk::Color color;
  color.set_red(r*256);
  color.set_green(g*256);
  color.set_blue(b*256);

  return this->meter_color(color);
} // Fin de meter_color(int,int,int)

/* *****************
   crea_pixmap
   ************** */
// Funcion que crea el pixmap para contener la imagen a representar
gint objeto_drawingarea::crea_pixmap(void)
{
  // Comprobamos q todo lo necesario existe y es correcto
  if(this->window==0)
  {
    this->error("crea_pixmap: ERROR no se puede crear pixmap, window no definido");
    return -1; // Window no definido
  }
  if((this->winw<=0)||(this->winh<=0))
  {
    this->error("crea_pixmap: ERROR no se puede crear pixmap, tamanyos incorrectos");
    return -2; // Tamaños del pixmap incorrectos
  }
  if(this->colormap==0)
  {
    this->error("crea_pixmap: ERROR no se puede crear pixmap, colormap no definido");
    return -3; // Colormap no definido
  }

  // Cogemos el tamanyo de la ventana
  this->window->get_geometry(this->winx,this->winy,this->winw,this->winh,this->wind);

  // Definimos la imagen
  // La imagen es del tamanyo de la ventana
  this->pixmap = Gdk::Pixmap::create(this->window,this->winw,this->winh,this->window->get_depth());

  // Le ponemos un colormap especifico
  this->pixmap->set_colormap(this->colormap);

  return 0; // Todo bien
} // Fin de crea_pixmap

/* **********************
   on_realize
   ******************* */
// Funcion que se llama cuando se muestra la ventana por primera vez
void objeto_drawingarea::on_realize()
{
  //fprintf(stderr,"INICIO on_realize\n");

  // Esta funcion se utiliza para inicializar el entorno grafico

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

  // Recogemos la ventana
  this->window = this->get_window ();
  // Guardamos la geometria de la ventana
  this->window->get_geometry (this->winx,this->winy,this->winw,this->winh,this->wind);
  // Creamos el contexto grafico
  this->gc_ = Gdk::GC::create (this->window);  
  // Recogemos el mapa de colores
  this->colormap = get_default_colormap ();

  // Metemos todos los colores de la paleta
  // OJO : La clase q herede debe inicializar la paleta al inicio
  for(gint i=0;i<NUM_COLOR;i++)
  {
    this->colormap->alloc_color(this->colores[i]);
  }

  // Ponemos los colores de fondo y de "frente"
  this->gc_->set_foreground(this->colores[this->fg]);
  this->gc_->set_background(this->colores[this->bg]);

  // Creamos el pixmap necesario para evitar el parpadeo
  this->crea_pixmap();


  //fprintf(stderr,"Informacion de pantalla :  %c x %d y %d w %d h %d d %d\n",((this->window==0)?('N'):('T')),winx,winy,winw,winh,wind);

  //fprintf(stderr,"FIN on_realize\n");
} // Fin de on_realize

/* **********************
   on_expose_event
   ******************* */
// Funcion que se llama cada vez que algo que estaba encima se quita
bool objeto_drawingarea::on_expose_event(GdkEventExpose * ev)
{
  //fprintf(stderr,"INICIO on_expose_event\n");

  // Mandamos repintar el pixmap
  this->window->draw_drawable(this->gc_,this->pixmap,0,0,0,0,this->winw,this->winh);

  //fprintf(stderr,"FIN on_expose_event\n");

  return true; // Permitimos que el evento suba al siguiente nivel
} // Fin de on_expose_event

/* *********************
   on_size_alloc
   ****************** */
// Funcion que se llama cuando se cambia el tamanyo de la ventana
void objeto_drawingarea::on_size_alloc(Gtk::Allocation &al)
{
  // Simplemente llamamos a cambio_ventana
  this->on_cambio_ventana();

} // Fin de on_size_alloc

/* *****************************
   on_cambio_ventana
   ************************** */
// Funcion q cambia el tamanyo de todos los elementos para ajustarse 
// al tamanyo de ventana q da el entorno grafico
void objeto_drawingarea::on_cambio_ventana()
{
  //fprintf(stderr,"INICIO on_cambio_ventana\n");

  // Volvemos a crear el pixmap, el tamanyo de la ventana a cambiado
  this->crea_pixmap();
  // Debemos repintar
  this->repintar();

  //fprintf(stderr,"Nuevo tamanyo : x %d y %d w %d h %d d %d\n",winx,winy,winw,winh,wind);

  //fprintf(stderr,"FIN on_cambio_ventana\n");
} // Fin de on_cambio_ventana

/* **********************
   configurar
   ******************* */
// Funcion que crea el pixmap ha mostrar
gint objeto_drawingarea::configurar(void)
{
  //fprintf(stderr,"INICIO configurar\n");

  // Ponemos una funcion por defecto q pone el pixmap a blanco

  // Comprobamos posibles errores
  if(this->window==0)
  {
    this->error("configurar : Window no definido");
    return -1;
  }
  if(this->pixmap==0)
  {
    this->error("configurar : Pixmap no definido");
    return -2;
  }
  if(this->gc_==0)
  {
    this->error("configurar : GC no definido");
    return -3;
  }
  if((this->winw<=0)||(this->winh<=0))
  {
    this->error("configurar : Tamanyos incorrectos");
    return -4;
  }

  // Todo bien podemos crear el pixmap

  // Limpiamos el pixmap
  this->gc_->set_foreground(this->colores[this->bg]);
  this->pixmap->draw_rectangle(this->gc_,true,0,0,this->winw,this->winh);


  //fprintf(stderr,"FIN configurar\n");
} // Fin de configurar


/* *****************
   repintar
   ************** */
// Funcion q repinta el pixmap en pantalla
gint objeto_drawingarea::repintar(void)
{
  //fprintf(stderr,"INICIO repintar\n");

  gint dev;

  // Reconstruimos el pixmap
  dev=this->configurar();

  // Dibujamos en el window el pixmap
  if(dev==0) // solo si la configuracion del pixmap ha ido bien
    this->window->draw_drawable(this->gc_,this->pixmap,0,0,0,0,this->winw,this->winh);

  // Obligamos a redibujar el window
  this->queue_draw();


  //fprintf(stderr,"FIN repintar\n");

  return dev; // Si es 0 todo bien, si es menor q 0 mal
} // Fin de repintar

/* **************
   error
   *********** */
// Funcion q sirve para controlar los errores q se han estado dando
void objeto_drawingarea::error(gchar *linea)
{
  // En esta funcion podemos decidir q tratamiento dar a los errores
  // sacarlos por pantalla a un fichero ...etc.

  std::cout<<"ERROR EN objeto_drawingarea.cc\n";
  std::cout<<linea<<"\n";
} // Fin de error



/* *******************************************
   objeto_drawingarea

   Clase para la realizacion rapida de
   drawingareas simples para dibujar
   en ellas
   **************************************** */

#ifndef _OBJETO_DRAWINGAREA_HH
#define _OBJETO_DRAWINGAREA_HH

// Includes standar
#include <iostream>

// Include propio de la gtkmm
#include <gtkmm/drawingarea.h>

// Defines propios

// Numero de colores de la paleta
#define NUM_COLOR 32

class objeto_drawingarea : public Gtk::DrawingArea
{
public:

  // Constructor
  objeto_drawingarea();
  // Destructor
  virtual ~objeto_drawingarea();

  // Variables para pintar
  Glib::RefPtr<Gdk::GC> gc_;
  gint fg,bg;
  Glib::RefPtr<Gdk::Window> window;
  Glib::RefPtr < Gdk::Colormap > colormap;

  // Paleta de colores a utilizar
  std::vector <Gdk::Color> colores;

  // Para guardar la imagen y evitar el parpadeo
  Glib::RefPtr<Gdk::Pixmap> pixmap;  

  // window geometry: x, y, width, height, depth
  gint winx,winy,winw,winh,wind;


  // Para repintar el pixmap
  gint repintar(void);

  // Funcion q permite meter un color
  gint meter_color(char *);
  gint meter_color(int,int,int);
  gint meter_color(Gdk::Color);

protected:


private:

  // Funcion que inicializa la clase
  void inicializar(void);

  // Funcion q crea el pixmap para contener la imagen a representar
  gint crea_pixmap(void);

  // Funcion q se llama cuando se muestra por primera vez el objeto
  void on_realize();

  // Funcion que se llama cuando algo se quita de encima y debemos repintar
  bool on_expose_event(GdkEventExpose * ev);

  // Estas funciones permiten reconstruir el pixmap cuando la ventana del
  // drawingarea cambia de tamanyo
  // Esta pensado para los paneles y para cuando la ventana cambia de tamanyo
  void on_size_alloc(Gtk::Allocation &);
  void on_cambio_ventana();

  // Funcion que debe implementar la clase q hereda
  // Aqui se debera meter todo el codigo q genera la imagen dentro del pixmap
  virtual gint configurar(void)=0;

  // Funcion para el tratamiento de errores
  void error(gchar *linea);

};
#endif



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