Introduzione a Gtkmm

Autore: Fabio Di Matteo
Ultima revisione: 17/02/2018 - 17:01

26/04/2023 - 13:40 Aggiornamento.
Ho scritto uno script che crea lo scheletro dell'applicazione Un script bash che crea lo scheletro di un'applicazione gtkmm con meson



Gtkmm è l'interfaccia C++ ufficiale del popolare toolkit grafico GTK+. Include le typesafe callback e un completo set di widget facilmente estendibili grazie all'ereditarietà (tratto da wikipedia).

In questo tutorial andremo a vedere come realizzare una finestra con un bottone , che al click incrementa il contenuto di una label. Come da figura. l'interfaccia grafica realizzata con Glade verra' caricata dal file gui.ui .

main.cc

#include <gtkmm.h>
#include "mainwindow.hpp"
 
int main(int argc, char *argv[])
{
	auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
	mainWindow mainw;
	return app->run(*mainw.window1);
}

mainwindow.hpp

#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
 
#include <gtkmm.h>
 
class mainWindow  
{
	public:
		Glib::RefPtr<Gtk::Builder> builder;
		Gtk::Window *window1;
		Gtk::Button *button1;
		Gtk::Label *label1;
 
		mainWindow();
 
	protected:	
		void on_button_clicked();
 
	private:
		/* add your private declarations */
};
 
#endif /* MAINWINDOW_HPP */

mainwindow.cpp

#include "mainwindow.hpp"
#include <iostream>
 
 
mainWindow::mainWindow()
{
	auto builder = Gtk::Builder::create();
 
	try
	{
		builder->add_from_file("gui.ui");
	}
	catch(const Glib::FileError& ex)
	{
		std::cerr << "FileError: " << ex.what() << std::endl;
	}
	catch(const Glib::MarkupError& ex)
	{
		std::cerr << "MarkupError: " << ex.what() << std::endl;
	}
	catch(const Gtk::BuilderError& ex)
	{
		std::cerr << "BuilderError: " << ex.what() << std::endl;
	}
	builder->get_widget("window1", window1);
	builder->get_widget("button1", button1);
	builder->get_widget("label1", label1);
 
	button1->signal_clicked().connect( sigc::mem_fun(*this, &mainWindow::on_button_clicked) );
 
}
 
void mainWindow::on_button_clicked()
{
	static int c;
	c++;
	label1->set_text("You have clicked "+std::to_string(c)+" times!");
}

makefile

all:
	g++ mainwindow.cpp main.cc `pkg-config gtkmm-3.0 --cflags --libs` -o simple

gui.ui

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="window1">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">You have clicked 0 times</property>
            <attributes>
              <attribute name="scale" value="5"/>
            </attributes>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label" translatable="yes">Cliccami</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
    <child type="titlebar">
      <placeholder/>
    </child>
  </object>
</interface>

Supporto a Meson e alle risorse (GNU/Linux - Windows)

La struttura del progetto:

.
├── compile_resources.sh
├── main.cc
├── mainwindow.cpp
├── mainwindow.hpp
├── meson.build
├── res
│   ├── gui.ui
│   └── resources.xml
└── resources.cpp

meson.build

project('GTKmm', 'cpp')
gtkmm = dependency('gtkmm-3.0')



os=build_machine.system()
if os == 'linux'
	executable('simple-meson', 'main.cc','resources.cpp','mainwindow.cpp', dependencies : gtkmm)
endif
if os == 'windows'
  executable('simple-meson', 'main.cc','resources.cpp','mainwindow.cpp', dependencies : gtkmm, link_args : ['-mwindows'])
endif

main.cpp

#include <gtkmm.h>
#include "mainwindow.hpp"
 
int main(int argc, char *argv[])
{
	auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
	mainWindow mainw;
	return app->run(*mainw.window1);
}

mainwindow.hpp

#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
 
#include <gtkmm.h>
 
class mainWindow  
{
	public:
		Glib::RefPtr<Gtk::Builder> builder;
		Gtk::Window *window1;
		Gtk::Button *button1;
		Gtk::Label *label1;
 
		mainWindow();
 
	protected:	
		void on_button_clicked();
 
	private:
		/* add your private declarations */
};
 
#endif /* MAINWINDOW_HPP */

mainwindow.cpp

#include "mainwindow.hpp"
#include <iostream>
 
 
mainWindow::mainWindow()
{
	auto builder = Gtk::Builder::create();
 
	try
	{
		builder->add_from_resource("/app/res/gui.ui");
	}
	catch(const Glib::FileError& ex)
	{
		std::cerr << "FileError: " << ex.what() << std::endl;
	}
	catch(const Glib::MarkupError& ex)
	{
		std::cerr << "MarkupError: " << ex.what() << std::endl;
	}
	catch(const Gtk::BuilderError& ex)
	{
		std::cerr << "BuilderError: " << ex.what() << std::endl;
	}
 
 
 
 
	builder->get_widget("window1", window1);
	builder->get_widget("button1", button1);
	builder->get_widget("label1", label1);
 
	button1->signal_clicked().connect( sigc::mem_fun(*this, &mainWindow::on_button_clicked) );
 
}
 
void mainWindow::on_button_clicked()
{
	static int c;
	c++;
	label1->set_text("You have clicked "+std::to_string(c)+" times!");
}

compile_resources.sh

#!/bin/bash
 
cd res
echo "Building resources.cpp ..."
glib-compile-resources --generate-source resources.xml
mv resources.c ../resources.cpp

gui.ui

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">You have clicked 0 times</property>
            <attributes>
              <attribute name="scale" value="5"/>
            </attributes>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label" translatable="yes">Cliccami</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>