SpinButton e DrawingArea

Ho creato una GUI con Glade e ho ottenuto i puntatori ai vari widget.
Per far dialogare gli spinbutton con la drawingarea ho fatto così:

  • Ho derivato da Gtk::Window una nuova classe di finestra
    #include <iostream>
    #include <gtkmm/window.h>
    #include <gtkmm/builder.h>
    #include <gtkmm/spinbutton.h>
    #include "dadrawingarea.h"
    
    class DaWindow : public Gtk::Window
    {
    public:
    	DaWindow();
    	DaWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade);
    
    protected:
    	Glib::RefPtr<Gtk::Builder> builder;
    	Gtk::SpinButton *m_sbB, *m_sbH;
    	DaDrawingArea *m_da;
    	void on_spinbutton_digits_changed();
    };
    

    Aggiungo, come proprietà protette della classe, i puntatori ai widget SpinButton e DrawingArea, quest’ultima come classe derivata, per poter scrivere il suo metodo virtuale on_draw.

  • Implemento DaWindow:
    #include "dawindow.h"
    
    DaWindow::DaWindow(BaseObjectType* cobject,
                       const Glib::RefPtr<Gtk::Builder>& refGlade)
    :
    	Gtk::Window(cobject), builder(refGlade)
    {
    	m_sbB = m_sbH = 0;
    	builder->get_widget("spinbuttonB", m_sbB);
    	builder->get_widget("spinbuttonH", m_sbH);
    
    	m_sbB->set_range(0.0, 100.0);
    	m_sbB->set_value(7.0);
    	m_sbB->set_increments(0.5, 1.0);
    
    	m_sbH->set_range(0.0, 100.0);
    	m_sbH->set_value(13.0);
    	m_sbH->set_increments(0.5, 1.0);
    
    	m_sbB->signal_value_changed().connect(sigc::mem_fun(
    		*this, &DaWindow::on_spinbutton_digits_changed));
    	m_sbH->signal_value_changed()
    	.connect(sigc::mem_fun(
    		*this, &DaWindow::on_spinbutton_digits_changed));
    
    	builder->get_widget_derived("drawingarea", m_da);
    }
    
    DaWindow::DaWindow()
    {
    	return;
    }
    
    void DaWindow::on_spinbutton_digits_changed()
    {
    	m_da->set_rectangle(m_sbB->get_value(), m_sbH->get_value());
    	m_da->queue_draw();
    }
    

    Nel costruttore inizializzo i widget SpinButton, e ottengo anche il puntatore alla DaDrawingArea.

    Quando c’è un evento value_changed delle spinbutton viene chiamata DaWindow::on_spinbutton_digits_changed(): questa può chiamare i metodi della DaDrawingArea che è anche essa membro di DaWindow.

    In particolare si osservi il metodo Gtk::DrawingArea::queue_draw() : è questo che permette di ridisegnare l’ area.

  • Ho derivato la DaDrawingArea:
    #include <gtkmm/drawingarea.h>
    #include <cairomm/context.h>
    
    class DaDrawingArea : public Gtk::DrawingArea
    {
    public:
    	DaDrawingArea();
    	DaDrawingArea(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade);
    	void set_rectangle(double B, double H);
    
    protected:
    	double m_B, m_H;
    	virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
    };
    

    Ho solo aggiunto m_B e m_H.

  • Implementazione di DaDrawingArea:
    #include <iostream>
    #include <gtkmm/builder.h>
    #include "dadrawingarea.h"
    
    DaDrawingArea::DaDrawingArea(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade)
    :
    	Gtk::DrawingArea(cobject)
    {
    	m_B = m_H = 0.0;
    }
    
    void DaDrawingArea::set_rectangle(double B, double H)
    {
    	m_B = B;
    	m_H = H;
    }
    
    bool DaDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
    {
    	Gtk::Allocation allocation = get_allocation();
    	const int width = allocation.get_width();
    	const int height = allocation.get_height();
     
    	// coordinates for the center of the window
    	int xc, yc;
    	xc = width / 2;
    	yc = height / 2;
    
    	double x0 = xc - (m_B / 2);
    	double y0 = yc - (m_H / 2);
    
    	cr->rectangle(x0, y0, m_B, m_H);
    	cr->stroke();
    
    	return true;
    }
    

    Il metodo set_rectangle(double B, double H) viene usato dalle spinbutton per modificare le dimensioni.
    In on_draw si disegna il rettangolo.

  • il main
    #include <gtkmm/window.h>
    #include <gtkmm/application.h>
    #include <gtkmm/builder.h>
    #include <gtkmm/spinbutton.h>
    #include "dawindow.h"
    
    int main(int argc, char** argv)
    {
    	Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.example.altro");
    
    	Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("src/GUI.glade");
    
    	DaWindow *da = 0;
    	builder->get_widget_derived("window", da);
    
    	return app->run(*da);
    }
    

SpinButton GtkMM

ho creato una GUI con Glade.

poi ho derivato una Gtk::Window

#include <gtkmm/window.h>
#include <gtkmm/builder.h>
#include <gtkmm/spinbutton.h>
#include <iostream>

class DaWindow : public Gtk::Window
{
public:
	DaWindow();
	DaWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade);

protected:
	Glib::RefPtr<Gtk::Builder> builder;
	Gtk::SpinButton *m_sbB, *m_sbH;
	void on_spinbutton_digits_changed();
};

Per usare gli SpinButton bisogna inizializzarli: range, value, increments (accettano valori di tipo double).
L’ evento del cambio di valore è signal_value_changed().

#include "dawindow.h"

DaWindow::DaWindow(BaseObjectType* cobject,
                   const Glib::RefPtr<Gtk::Builder>& refGlade)
:
	Gtk::Window(cobject), builder(refGlade)
{
	m_sbB = m_sbH = 0;
	builder->get_widget("spinbuttonB", m_sbB);
	builder->get_widget("spinbuttonH", m_sbH);

	m_sbB->set_range(0.0, 100.0);
	m_sbB->set_value(7.0);
	m_sbB->set_increments(0.5, 1.0);

	m_sbH->set_range(0.0, 100.0);
	m_sbH->set_value(13.0);
	m_sbH->set_increments(0.5, 1.0);

	m_sbB->signal_value_changed().connect(sigc::mem_fun(
		*this, &DaWindow::on_spinbutton_digits_changed));
	m_sbH->signal_value_changed()
	.connect(sigc::mem_fun(
		*this, &DaWindow::on_spinbutton_digits_changed));
}

DaWindow::DaWindow()
{
	return;
}

void DaWindow::on_spinbutton_digits_changed()
{
	std::cout << "spin B: " << m_sbB->get_value() << std::endl;
	std::cout << "spin H: " << m_sbH->get_value() << std::endl;
}

Gtkmm: Entry, Label, segnali, ustring

ho creato una semplice GUI con Glade.
ho creato due signal handler per i bottoni: uno esegue l’ inversione della ustring, l’ altro cancella i campi.

  • i parametri passati con il binding sono i puntatori ai widget.
    Notare anche l’ uso di ptr_fun per indicare il puntatore ad una funzione, a differenza di mem_fun che serve con i puntatori a metodi.
  • notare come viene invertita la ustring: non ho trovato di meglio che usare il metodo assign che supporta gli iteratori. Usando i reverse iterators si ottiene la stringa invertita.
  • il testo di una label di imposta/modifica con set_text e get_text.
  • per una entry il testo si trova in un buffer a cui si accede con get_buffer, i cui metodi get_text() e delete_text() consentono la manipolazione.

ecco il listato:

#include <gtkmm/application.h>
#include <gtkmm/window.h>
#include <gtkmm/button.h>
#include <gtkmm/entry.h>
#include <gtkmm/label.h>
#include <gtkmm/builder.h>
#include <iostream>


void onOK_clicked(Gtk::Entry *pE, Gtk::Label *pL)
{
	Glib::ustring rev = pE->get_buffer()->get_text();
	rev.assign(rev.rbegin(), rev.rend());
	pL->set_text(rev);
	std::cout << "OK !" << std::endl;
}

void onCancel_clicked(Gtk::Entry *pE, Gtk::Label *pL)
{
	pE->get_buffer()->delete_text(0, -1);
	pL->set_text("inserisci nuovo testo.");
	std::cout << "Cancel :-(" << std::endl;
}

int main(int argc, char** argv)
{
	Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

	Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("src/GUI.glade");

	Gtk::Button *pOk_button, *pCancel_button;
	builder->get_widget("button_OK",pOk_button);
	builder->get_widget("button_CANCEL", pCancel_button);

	Gtk::Entry *pEntry;
	Gtk::Label *pLabel;
	builder->get_widget("entry", pEntry);
	builder->get_widget("label", pLabel);

	pOk_button->signal_clicked().connect(sigc::bind<Gtk::Entry*, Gtk::Label*>( sigc::ptr_fun(&onOK_clicked), pEntry, pLabel) );
	pCancel_button->signal_clicked().connect(sigc::bind<Gtk::Entry*, Gtk::Label*>( sigc::ptr_fun(&onCancel_clicked), pEntry, pLabel) );

	Gtk::Window *window;
	builder->get_widget("window", window);
	return app->run(*window);
}

Gtkmm Builder e Glade

Con Glade si possono creare interfacce grafiche in modo semplice.
Si ottiene un file *.glade che verrà usato da Gtk::Builder.

# creare un oggetto Builder
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create();
# caricare la GUI
builder->add_from_file("src/gui.glade");

ora si presentano due possibilità: o dobbiamo accedere ad una classe “base” di Gtkmm oppure ad una classe derivata. Ci sono 2 metodi di Gtk::Builder per questi 2 casi: get_widget e get_widget_derived. Bisogna fare attenzione al caso di classi derivate: bisogna avere un costruttore che accetta 2 parametri: il puntatore all’ oggetto container, e il puntatore al builder.
Ad esempio:

# da qualche parte:
class Helloworld : public Gtk::Window
{
	public:
		Helloworld();
		Helloworld(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade);
};

# nel main:
Helloworld *hw;

builder->get_widget_derived("window", hw);
return app->run(*hw);

ATTENZIONE:
il costruttore della classe derivata DEVE chiamare il costruttore della classe base nella lista di inizializzazione:

Helloworld::Helloworld(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade) :
    Gtk::Window(cobject), builder(refGlade){

    builder->get_widget("btnOk", btnOk);
    builder->get_widget("btnCancel", btnCancel);
    builder->get_widget("lblNotice",lblNotice);

    btnOk->signal_clicked().connect(sigc::mem_fun(*this, &FrmMain::on_ok_button_clicked));
    btnCancel->signal_clicked().connect(sigc::mem_fun(*this, &FrmMain::on_cancel_button_clicked));

}

vedere anche http://milindapro.blogspot.it/2012/10/create-gui-with-gtkmm-glade-with-gtkmm.html

Signal handler con gtkmm

Dopo aver dichiarato un widget e una funzione void in una classe Gtk::Window

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

class HelloWorld : public Gtk::Window
{

public:
  HelloWorld();
  virtual ~HelloWorld();

protected:
  //Signal handlers:
  void on_button_clicked();

  //Member widgets:
  Gtk::Button m_button;
};

bisogna “collegare” la funzione al widget in modo che venga chiamata ogni volta che un segnale viene lanciato:

#include "helloworld.h"
#include <iostream>

HelloWorld::HelloWorld()
: m_button("Hello World")   // creates a new button with label "Hello World".
{
  ...
  m_button.signal_clicked().connect(sigc::mem_fun(*this,
              &HelloWorld::on_button_clicked));
  ...
}

void HelloWorld::on_button_clicked()
{
  std::cout << "Hello World" << std::endl;
}

Notare che l’ handler non ha parametri.

A volte si può usare uno stesso handler per più widget simili.
Allora si rende necessario il passaggio di parametri o argomenti.
Per passare argomenti, bisogna fare il binding:

...
void on_button_clicked(Glib::ustring, int val);
...
// sigc::bind<Tipo1, Tipo2, ...>(HANDLER, param1, param2, ...);
m_button1.signal_clicked().connect( sigc::bind<Glib::ustring, int>( sigc::mem_fun(*this, &HelloWorld::on_button_clicked), "xyz", 3) );

Programmare con Cairomm e Gtkmm

Si deriva la classe Gtk::DrawingArea e la si personalizza con la funzione on_draw:

#include <gtkmm/drawingarea.h>

class MyArea : public Gtk::DrawingArea
{
public:
  MyArea();
  virtual ~MyArea();

protected:
  //Override default signal handler:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
};

Nell’ implementazione di on_draw si eseguono le operazioni di disegno:

#include "myarea.h"
#include <cairomm/context.h>

MyArea::MyArea()
{
}

MyArea::~MyArea()
{
}

bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
  Gtk::Allocation allocation = get_allocation();
  const int width = allocation.get_width();
  const int height = allocation.get_height();

  // coordinates for the center of the window
  int xc, yc;
  xc = width / 2;
  yc = height / 2;

  cr->set_line_width(10.0);

  // draw red lines out from the center of the window
  cr->set_source_rgb(0.8, 0.0, 0.0);
  cr->move_to(0, 0);
  cr->line_to(xc, yc);
  cr->line_to(0, height);
  cr->move_to(xc, yc);
  cr->line_to(width, yc);
  cr->stroke();

  return true;
}

Il main.cc crea la Gtk::Application (come tutte le app) che farà girare una Gtk::Window.
In quest’ ultima si crea una “area”, la si aggiunge e la si “mostra”: il metodo “on_draw” farà il resto.

#include "myarea.h"
#include <gtkmm/application.h>
#include <gtkmm/window.h>

int main(int argc, char** argv)
{
   Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

   Gtk::Window win;
   win.set_title("DrawingArea");

   MyArea area;
   win.add(area);
   area.show();

   return app->run(win);
}

Programmare gtkmm con C++

SU Debian bisogna installare il compilatore C++ con il pacchetto g++, e le librerie gtkmm con il pacchetto libgtkmm-3.0-dev.
Per compilare si usa il seguente comando:

g++ myprog.cc -o myprog `pkg-config gtkmm-3.0 --cflags --libs`

Si raccomanda però di sfruttare gli autotools: autoconf, automake, ecc… vedere guida.

Lo schema più semplice di un programma con gtkmm è del tipo:

//            file HelloWorld.h
// derivo una classe Gtk::Window e la estendo a piacere:
class HelloWorld : public Gtk::Window
{
public:
  HelloWorld();            //costruttore
  virtual ~HelloWorld();   //distrutture

protected:
  //Signal handlers:
  //Member widgets:
}

Implementazione:

//            file helloworld.cc
#include "helloworld.h"
#include <iostream>

HelloWorld::HelloWorld()
{
  ...
}

HelloWorld::~HelloWorld()
{
}

Il main.cc è del tipo:

#include "helloworld.h"
#include <gtkmm/application.h>

int main (int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  HelloWorld helloworld;

  //Shows the window and returns when it is closed.
  return app->run(helloworld);
}

La riga 6 dchiara una variabile di nome app, del tipo Glib::RefPtr e , e le assegna il valore restituito da Gtk::Application::create(): il terzo parmetro di questa funzione è un nome a piacere, nella forma reverse-DNS (notazione a dominio inversa).

Questa variabile app sarà usata con la sua funzione run() che lancerà un loop sull’ oggetto helloworld passato come parametro.