c++event-handlinggtkgtkmmgtkmm3

In C++, using gtkmm-3.0, how to connect a widget created in an XML file to a function


I am learning C++, and how to use gtkmm (I am using gtkmm version 3.24). I have created a simple window, with few widgets, but I am not able to connect a simple event with a simple function.

The minimal code I have is this:

myWindow.h

#pragma once

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

class MainWindow : public Gtk::Window
{
  public:

    MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder);

  protected:

    Glib::RefPtr<Gtk::Builder> m_builder;

    Glib::RefPtr<Gtk::Button> m_button;

      //Signal handler:
    void on_button_clicked();
};

myWindow.cpp

#include "myWindow.h"

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

#include <iostream>

MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder)
: Gtk::Window(cobject)
{
    this->m_builder = p_builder;

    Glib::RefPtr<Gtk::Button> p_button;

    p_builder->get_widget("button1", p_button);

    this->m_button = p_button;

    m_button->signal_clicked().connect( sigc::mem_fun(*this, &MainWindow::on_button_clicked) );
}

void MainWindow::on_button_clicked()
    {
        std::cout << "The Button was clicked." << std::endl;
    }

main.cpp

#include <iostream>
#include <cstring>

#include <gtkmm/application.h>
#include <gtkmm/builder.h>

#include "myWindow.h"

int main(int argc, char** argv)
{
    auto app = Gtk::Application::create(argc, argv);

    auto builder = Gtk::Builder::create_from_file("builder.ui");

    MainWindow* window = nullptr;

    builder->get_widget_derived("mainWindow", window);

    app->run(*window);

return 0;
}

builder.ui

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <object id="mainWindow" class="GtkWindow">
    <property name="title">Pippo</property>
    <property name="default_width">400</property>
    <property name="default_height">300</property>
    <child>
     <object class="GtkGrid" id="grid">
      <property name="visible">True</property>
        <child>
          <object id="button1" class="GtkButton">
            <property name="label">Button 1</property>
            <property name="visible">True</property>
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">0</property>
          </packing>
        </child>
     </object>
    </child>
  </object>
</interface>

This code gives the following error when building:

myWindow.cpp:18:26: error: no matching function for call to ‘Gtk::Builder::get_widget(const char [8], Glib::RefPtr<Gtk::Button>&)’
   18 |     p_builder->get_widget("button1", p_button);

What am I doing wrong? I guess I am calling get_widget() in the wrong way, or with the wrong arguments, but my knowledge is still too poor.


Solution

  • The problem in the code presented in the question is the datatype of the pointer to the button, or 'function signature', as said in comments. The pointer to the button needs to be of type Gtk::Button*, and not Glib::RefPtr<Gtk::Button>. Calling the get_widget() with the wrong signature (an input parameter with the wrong datatype), generated the 'no matching function' error. (A short digression for the C++ newbies like me: the error should be understood as 'no matching function with the given input parameters types (i.e. signature)', which due to the C++ polymorphism is a different function altogether, despite the same name).

    For clarity, I repeat the whole project, with the correct code

    myWindow.h

    #pragma once
    
    #include <gtkmm/window.h>
    #include <gtkmm/builder.h>
    #include <gtkmm/button.h>
    
    class MainWindow : public Gtk::Window
    {
      public:
        MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder);
    
      protected:
        Glib::RefPtr<Gtk::Builder> m_builder;
        Glib::RefPtr<Gtk::Button> m_button;
    
          //Signal handler:
        void on_button_clicked();
    };
    

    myWindow.cpp

    #include "myWindow.h"
    
    #include <gtkmm/builder.h>
    #include <gtkmm/button.h>
    
    #include <iostream>
    
    MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder)
    : Gtk::Window(cobject), m_builder{p_builder}
    {
        Gtk::Button* p_button = nullptr; // Create a raw pointer
    
        m_builder->get_widget("button1", p_button); // get the pointer value
    
        m_button = Glib::RefPtr<Gtk::Button>(p_button); // assign it to m_button
    
        m_button->signal_clicked().connect( sigc::mem_fun(*this, &MainWindow::on_button_clicked) );   // connect the signal to the function
    }
    
    void MainWindow::on_button_clicked()
        {
            std::cout << "The Button was clicked." << std::endl;
        }
    
    

    main.cpp

    #include <iostream>
    #include <cstring>
    
    #include <gtkmm/application.h>
    #include <gtkmm/builder.h>
    
    #include "myWindow.h"
    
    int main(int argc, char** argv)
    {
        auto app = Gtk::Application::create(argc, argv);
    
        auto builder = Gtk::Builder::create_from_file("builder.ui");
    
        MainWindow* window = nullptr;
    
        builder->get_widget_derived("mainWindow", window);
    
        app->run(*window);
    
    return 0;
    }
    

    builder.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <object id="mainWindow" class="GtkWindow">
        <property name="title">Pippo</property>
        <property name="default_width">400</property>
        <property name="default_height">300</property>
        <child>
         <object class="GtkGrid" id="grid">
          <property name="visible">True</property>
            <child>
              <object id="button1" class="GtkButton">
                <property name="label">Button 1</property>
                <property name="visible">True</property>
              </object>
              <packing>
                <property name="left-attach">0</property>
                <property name="top-attach">0</property>
              </packing>
            </child>
         </object>
        </child>
      </object>
    </interface>
    
    

    This code builds and runs correctly, and gives the requested behaviour.