Search code examples
c++gtkmmgladegtkmm3

Progress Bar gtkmm Glade


Working with the buttons and handling its events is easy but I couldn't interact with progress bars. How do I get a Gtk::ProgressBar from a Glade file and set its fraction?

I use Gtkmm 3.24 and I want to update a progress bar when I click a button. here is my glade file:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkApplicationWindow" id="MainWindow">
    <property name="can-focus">False</property>
    <property name="title" translatable="yes">My Main Window</property>
    <property name="default-width">200</property>
    <property name="default-height">150</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can-focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkButton" id="LittleButton">
            <property name="label" translatable="yes">button</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">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkProgressBar" id="MyProgressBar">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

My main.cpp file

#include <memory>
#include <gtkmm.h>

class MainWindow : public Gtk::ApplicationWindow 
{
public:
    MainWindow(BaseObjectType* obj, Glib::RefPtr<Gtk::Builder> const& builder)
        : Gtk::ApplicationWindow(obj)
        , builder{builder}
    {
        
    }
    virtual ~MainWindow() = default;
private:
    Glib::RefPtr<Gtk::Builder> builder;
};

int main(int argc, char* argv[]) 
{
   auto app = Gtk::Application::create(argc, argv, "de.engelmarkus.example");
   auto builder = Gtk::Builder::create_from_file("MyGladeGUI.glade");
   MainWindow* mwindow = nullptr;
   builder->get_widget_derived("MainWindow", mwindow);
   
   auto littleButton = builder->get_object("LittleButton");
   
   auto r = app->run(*mwindow);
   delete mwindow;

   return r;
}

I build my application from the command line with:

g++ -rdynamic main.cpp -o hello $(pkg-config gtkmm-3.0 --libs --cflags)

Solution

  • Here is code that does what you want:

    #include <memory>
    #include <gtkmm.h>
    
    constexpr int ERROR = 1;
    
    int main(int argc, char* argv[]) 
    {
        auto app = Gtk::Application::create(argc, argv, "de.engelmarkus.example");
        auto builder = Gtk::Builder::create_from_file("MyGladeGUI.glade");
    
        // Get the window. First a `Glib::Object` handle to it and then cast it
        // to the `Gtk::ApplicationWindow` type:
        Glib::RefPtr<Glib::Object> mainWindowAsObject = builder->get_object("MainWindow");
        Glib::RefPtr<Gtk::ApplicationWindow> mainWindow = Glib::RefPtr<Gtk::ApplicationWindow>::cast_dynamic(mainWindowAsObject);
        if(!mainWindow)
        {
            return ERROR;
        }
        
        // At this point, the main window is valid, and as bonus, wrapped in a smart
        // pointer, which means you do not have to manage its memory yourself. Now get
        // the button. First get a `Glib::Object` handle to it and then cast it to the
        // `Gtk::Button` type:
        Glib::RefPtr<Glib::Object> buttonAsObject = builder->get_object("LittleButton");
        Glib::RefPtr<Gtk::Button> button = Glib::RefPtr<Gtk::Button>::cast_dynamic(buttonAsObject);
        if(!button)
        {
            return ERROR;
        }
    
        // At this point, the button is valid. We have to get the progress bar. You get
        // the idea now ;):
        Glib::RefPtr<Glib::Object> pBarAsObject = builder->get_object("MyProgressBar");
        Glib::RefPtr<Gtk::ProgressBar> pBar = Glib::RefPtr<Gtk::ProgressBar>::cast_dynamic(pBarAsObject);
        if(!pBar)
        {
            return ERROR;
        }
    
        // At this point, both the button and the progress bar have been extracted
        // from the builder and are valid. We can now connect the button's 'clicked'
        // signal handler:
        button->signal_clicked().connect([&pBar](){
            const double currentFraction = pBar->get_fraction();
            pBar->set_fraction(currentFraction + 0.10);
        });
    
        // The syntax is ugly the basically:
        //  1. Call `get()` to retreive the stored pointer
        //  2. Call operator* on this pointer to get a reference
        return app->run(*(mainWindow.get()));
    
    } // mainWindow automatically deleted here by its RefPtr container!
    
    

    Output:

    enter image description here

    I have taken the liberty to simplify it a bit. Most importantly, I reviewed your memory management model and completely removed the usage of delete, through the use of Glib::RefPtr which can do that for you.

    You can build with:

    g++ -rdynamic main.cpp -o hello `pkg-config gtkmm-3.0 --libs --cflags`
    

    Note: I wrote this using Gtkmm 3.22.