2

I'm trying to make a simple software with Gtkmm3.

I want to have a window with a grid inside. At a click on a button inside that grid, a method of the window should be triggered to delete the current grid and replace it with another one.

I'm able to use a method of the grid like this :

button.signal_clicked().connect(sigc::mem_fun(*this, &MyGrid::someMethod));

"this" being MyGrid.

I would like to do something like this :

button.signal_clicked().connect(sigc::mem_fun(*this->get_parent(), &MyWindow::someMethod));

where this->get_parent() would be an instance of MyWindow

My .h:


#ifndef MINIPROJECT_GUI_H
#define MINIPROJECT_GUI_H

#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/grid.h>
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <gtkmm/label.h>


class WelcomeGrid: public Gtk::Grid
{
    Gtk::Label message;
    Gtk::Button nextButton; // This button should be connected to Fenetre::infoView()
public:
    WelcomeGrid();
    void display();

};


class InfoGrid : public Gtk::Grid
{
    Gtk::Button button2;// This button should be connected to Fenetre::welcomeView()
    Gtk::Label label2;
public:
    InfoGrid();
    void display();
};


class Fenetre : public Gtk::Window
{

public:
    Fenetre();
    virtual ~Fenetre(); // Setup window

    void welcomeView();

protected:
    //Member widgets:
    WelcomeGrid welcome;
    InfoGrid info;
    void infoView(); // Remove the current grid from the window and replace it by infoGrid
    void welcomeView(); // Remove the current grid from the window and replace it by WelcomeGrid

};


#endif //MINIPROJECT_GUI_H

My .cpp :

#include "GUI.h"

Fenetre::Fenetre()
{
    // Sets the border width of the window.
    set_border_width(10);
    this->add(welcome);
}

Fenetre::~Fenetre()
{
}

void Fenetre::welcomeView() {
    this->remove();
    this->add(welcome);
}

void Fenetre::infoView() {
    this->remove();
    this->add(info);
}


InfoGrid::InfoGrid() {
    button2.set_label("Hello.");
    button2.signal_clicked().connect(sigc::mem_fun(*this,
                                                   &InfoGrid::display));

    label2.set_label("Welcome on the Vampire creation interface.");
    this->attach(label2, 0, 0, 1, 1);
    this->attach(button2,1,1,1,1);
    button2.show();
    this->show_all();
}



WelcomeGrid::WelcomeGrid() {
    nextButton.set_label("Create new character.");
    auto a = this->get_parent();
    nextButton.signal_clicked().connect(sigc::mem_fun(*this,
                                                   &WelcomeGrid::display));

    message.set_label("Welcome on the Vampire creation interface.");
    this->attach(message, 0, 0, 1, 1);
    this->attach(nextButton,1,1,1,1);
    // This packs the button into the Window (a container);
    this->show_all();
}

void WelcomeGrid::display() {
    auto a = this->get_parent();
    std::cout << typeid(a).name();
}

void InfoGrid::display() {
    std::cout << "coucou";
}

dantarno
  • 92
  • 1
  • 6

1 Answers1

2

Without any code, it is hard to know what exactly you are looking for. Here is how I would do it: I would keep a reference to the parent Window inside the grid. For example:

#include <iostream>
#include <memory>
#include <sstream>

#include <gtkmm.h>

class MyWindow : public Gtk::Window
{

public:

    MyWindow()
    : m_grid{std::make_unique<MyGrid>(*this, m_count)}
    {
        add(*m_grid);
    }

    // This is called when the grid's button is pressed:
    void ReplaceGrid()
    {
        ++m_count; 
        
        // Remove the grid from the window:
        remove();

        // Destroy current grid:
        m_grid = nullptr;

        // Create a new grid:
        m_grid = std::make_unique<MyGrid>(*this, m_count);

        // Add it to the window:
        add(*m_grid);

        show_all();
    }

private:

    class MyGrid : public Gtk::Grid
    {
    
    public:
    
        MyGrid(MyWindow& p_parent, int p_count)
        : m_parent{p_parent}
        {
            // Create button:
            std::ostringstream ss;
            ss << "Replace me #" << p_count;
            m_replaceButton = Gtk::Button(ss.str());
    
            // Attach it to the grid:
            attach(m_replaceButton, 0, 0, 1, 1);
    
            // Connect replacement signal, using the parent window:
            m_replaceButton.signal_clicked().connect([this]()
                                                     {
                                                         // Call the parent (the window):
                                                         m_parent.ReplaceGrid();
                                                     });
        }

        ~MyGrid()
        {
            std::cout << "Grid destroyed" << std::endl;
        }
    
    private:
    
        Gtk::Button m_replaceButton;

        // Keep a reference to the parent window in the grid:
        MyWindow&   m_parent;
    };

    int                     m_count = 0;
    std::unique_ptr<MyGrid> m_grid;
};

int main(int argc, char *argv[]) 
{
    auto app = Gtk::Application::create(argc, argv, "so.question.q64594709");
    MyWindow w;

    w.show_all();
    
    return app->run(w);
}

If you run this code, you will see a window with a grid containing one button. Whenever you click the button, the window:

  1. updates a counter
  2. destroys the current grid
  3. creates a new grid with the updated counter value

You will see the counter value updated on the new grid's button label. In the terminal, the grids destructor will print a message, proving grids have really been switched.

Notice I have used lambdas here to clean up the syntax. I would suggest you do so as well. If you really want to use sigc::men_fun, you can encapsulate the lambda's content into the MyGrid::someMethod method that you mentioned in your question.

Notice also that the grid is a private nested class of the window (no one else needs to know...).

Compiled with GCC:

g++ main.cpp -o example.out `pkg-config gtkmm-3.0 --cflags --libs`
BobMorane
  • 3,870
  • 3
  • 20
  • 42
  • Thank you for your answer ! Is it good practice to have nested class like this ? If my way of changing content inside the window is not good, please tell me ! I will had my code tomorrow – dantarno Oct 29 '20 at 21:02
  • Since the grid is in your window and also using it, it is very unlikely it would be used in another context. In this case, hiding (nesting it privately) is a good practice because no one else that the window it was designed for can (mis)use it. – BobMorane Oct 29 '20 at 21:18
  • Not really, as I will have 5 different grid I don't want to have 5 nested class. – dantarno Nov 11 '20 at 11:30
  • If you do not want to have nested classes, you have to do two things. 1: Use a forward declaration of class MyWindow in the header file (MyGrid.h) of class MyGrid (add line ' class MyWindow; ' prior to ' class MyGrid { ... }; ') 2: Include the whole class MyWindow in the cpp-file (MyGrid.cpp) of class MyGrid (add line ' "include MyWindow.h" ' at top). It took me some time to find out about this - hope this helps others. – bejo Feb 15 '23 at 06:59