1

Compile with g++ main.cc -Wall $(pkg-config gtkmm-3.0 --cflags --libs)

What it currently does:

Displays a window with a simple SpinButton

What I want to do:

Pass in a reference of spinbutton to the signal handler on_spinbutton_change so that I can getAdjustment and set the formatting (like here)

Question:

How do I pass in a reference of spinbutton and optionally additional data (like a simple integer)?

main.cc (compiles fine, does not pass reference):

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

class HelloWorld : public Gtk::Window {
public:
    HelloWorld();
    virtual ~HelloWorld();

protected:
    static gboolean on_spinbutton_change();
    Gtk::SpinButton spinbutton;
};

HelloWorld::HelloWorld() {
    spinbutton.signal_output().connect(sigc::ptr_fun(&HelloWorld::on_spinbutton_change));
    add(spinbutton);
    spinbutton.show();
}

HelloWorld::~HelloWorld() {}

gboolean HelloWorld::on_spinbutton_change() {
    std::cout << "Hello World" << std::endl;
    return true;
}

int main (int argc, char *argv[]) {
    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
    HelloWorld helloworld;
    return app->run(helloworld);
}

main.cc (does not compile, attempt to pass reference):

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

class HelloWorld : public Gtk::Window {
public:
    HelloWorld();
    virtual ~HelloWorld();

protected:
    static gboolean on_spinbutton_change(Gtk::SpinButton *spin);
    Gtk::SpinButton spinbutton;
};

HelloWorld::HelloWorld() {
    spinbutton.signal_output().connect(sigc::bind<Gtk::SpinButton*>(sigc::ptr_fun(&HelloWorld::on_spinbutton_change), spinbutton));
    add(spinbutton);
    spinbutton.show();
}

HelloWorld::~HelloWorld() {}

gboolean HelloWorld::on_spinbutton_change(Gtk::SpinButton *spin) {
    std::cout << "Hello World" << std::endl;
    return true;
}

int main (int argc, char *argv[]) {
    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
    HelloWorld helloworld;
    return app->run(helloworld);
}
jmpsabisb
  • 182
  • 1
  • 10
  • For this specific issue, sigc++ provides `sigc::ref()`. Please, try this: `spinbutton.signal_output().connect(sigc::bind(sigc::ptr_fun(&HelloWorld::on_spinbutton_change), sigc::ref(spinbutton)));`. Without `sigc::ref()`, you either get an (uninteded) copy of `spinbutton` or it does not compile (when Gtk::SpinButton's copy constructor is deleted). (It's a while ago that I switched to Qt and I used gtkmm 2.4 before but I believe this shouldn't have changed.) – Scheff's Cat Aug 19 '18 at 09:28
  • Using your suggested change, I get `error: no matching function for call to ‘bind(sigc::pointer_functor1, sigc::reference_wrapper)’`. – jmpsabisb Aug 20 '18 at 00:57
  • However, changing line 12 to `static gboolean on_spinbutton_change(const Gtk::SpinButton &spin);`, line 17 to `spinbutton.signal_output().connect(sigc::bind(sigc::ptr_fun(&HelloWorld::on_spinbutton_change), sigc::ref(spinbutton)));` and line 24 to `gboolean HelloWorld::on_spinbutton_change(const Gtk::SpinButton &spin) {`, I am able to compile and run without errors. This is apparently because without `const`, the copy constructor is called. With this, I am still not able to set the `SpinButton`'s value (because of `const`). – jmpsabisb Aug 20 '18 at 00:57
  • Any ideas on how to remove the `const` qualifier and still be able to run? – jmpsabisb Aug 20 '18 at 00:58
  • In your second sample code, the signature of callback is `static gboolean on_spinbutton_change(Gtk::SpinButton *spin);` i.e. the argument to be bound is a pointer. Binding a reference (or instance) should fail - type mismatch. In this case, you have to bind the address of `spinbutton`: `spinbutton.signal_output().connect(sigc::bind(sigc::ptr_fun(&HelloWorld::on_spinbutton_change), &spinbutton));`. To make the previous binding with `sigc::ref()` working, the signature of callback has to be changed: `static gboolean on_spinbutton_change(Gtk::SpinButton &spin);`. – Scheff's Cat Aug 20 '18 at 05:41
  • Please note, that unary `&` in _an expression_ is the address operator (inherited from C) but `&` in _a declaration_ is the _reference modifier_ (in C++ only). Same character, different meaning. (Confusing?) ;-) – Scheff's Cat Aug 20 '18 at 05:43

1 Answers1

1

I must admit that I switched from gtkmm to Qt some years ago. Out of curiosity, I installed gtkmm 3 in my cygwin (I'm on Windows 10) to prepare a sample (and find out how rusty I've become concerning gtkmm).

Considering the attempts of OP, I used multiple different signatures which can be used to achieve the same result.

  1. static gboolean Window::on_spinbtn_output_p(Gtk::SpinButton *pGtkSpinBtn);
    the corresponding connect():

    _gtkSpinBtn1.signal_output().connect(
      sigc::bind(
        sigc::ptr_fun(&Window::on_spinbtn_output_p),
        &_gtkSpinBtn1));
  2. static gboolean Window::on_spinbtn_output_r(Gtk::SpinButton &gtkSpinBtn);
    the corresponding connect():

    _gtkSpinBtn2.signal_output().connect(
      sigc::bind(
        sigc::ptr_fun(&Window::on_spinbtn_output_r),
        sigc::ref(_gtkSpinBtn2)));
  3. gboolean Window::on_spinbtn3_output() (non-static)
    the corresponding connect():

    _gtkSpinBtn3.signal_output().connect(
      sigc::mem_fun(this, &Window::on_spinbtn3_output));

The complete sample testGtkSpinBtnSig.cc:

#include <iostream>
#include <gtkmm/application.h>
#include <gtkmm/box.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/window.h>

class Window: public Gtk::Window {

  private:
    Gtk::VBox _gtkVBox;
    Gtk::SpinButton _gtkSpinBtn1;
    Gtk::SpinButton _gtkSpinBtn2;
    Gtk::SpinButton _gtkSpinBtn3;

  public:
    Window();
    virtual ~Window() = default;
    Window(const Window&) = delete;
    Window& operator=(const Window&) = delete;

  protected:
    static gboolean on_spinbtn_output_p(Gtk::SpinButton *pGtkSpinBtn);
    static gboolean on_spinbtn_output_r(Gtk::SpinButton &gtkSpinBtn);
    gboolean on_spinbtn3_output();
};

Window::Window(): Gtk::Window()
{
  _gtkSpinBtn1.set_range(0.0, 10.0); _gtkSpinBtn1.set_value(1.0);
  _gtkVBox.pack_start(_gtkSpinBtn1);
  _gtkSpinBtn2.set_range(0.0, 10.0); _gtkSpinBtn2.set_value(2.0);
  _gtkVBox.pack_start(_gtkSpinBtn2);
  _gtkSpinBtn3.set_range(0.0, 10.0); _gtkSpinBtn3.set_value(3.0);
  _gtkVBox.pack_start(_gtkSpinBtn3);
  add(_gtkVBox);
  _gtkVBox.show_all();
  // install signal handlers
  _gtkSpinBtn1.signal_output().connect(
    sigc::bind(
      sigc::ptr_fun(&Window::on_spinbtn_output_p),
      &_gtkSpinBtn1));
  _gtkSpinBtn2.signal_output().connect(
    sigc::bind(
      sigc::ptr_fun(&Window::on_spinbtn_output_r),
      sigc::ref(_gtkSpinBtn2)));
  _gtkSpinBtn3.signal_output().connect(
    sigc::mem_fun(this, &Window::on_spinbtn3_output));
}

gboolean Window::on_spinbtn_output_p(Gtk::SpinButton *pGtkSpinBtn)
{
  std::cout << "Window::on_spinbtn_output_p(): pGtkSpinBtn->get_value(): "
    << pGtkSpinBtn->get_value() << '\n';
  return true;
}

gboolean Window::on_spinbtn_output_r(Gtk::SpinButton &gtkSpinBtn)
{
  std::cout << "Window::on_spinbtn_output_r(): gtkSpinBtn.get_value(): "
    << gtkSpinBtn.get_value() << '\n';
  return true;
}

gboolean Window::on_spinbtn3_output()
{
  std::cout << "Window::on_spinbtn_output(): _gtkSpinBtn3.get_value(): "
    << _gtkSpinBtn3.get_value() << '\n';
  return true;
}

int main(int argc, char *argv[])
{
  auto app = Gtk::Application::create(argc, argv, "Test SpinButton Signals");
  Window gtkWin;
  return app->run(gtkWin);
}

Compiled and tested:

$ g++ testGtkSpinBtnSig.cc -Wall $(pkg-config gtkmm-3.0 --cflags --libs) -o testGtkSpinBtnSig

$ ./testGtkSpinBtnSig
Window::on_spinbtn_output_p(): pGtkSpinBtn->get_value(): 1
Window::on_spinbtn_output_r(): gtkSpinBtn.get_value(): 2
Window::on_spinbtn_output(): _gtkSpinBtn3.get_value(): 3

Snapshot of testGtkSpinBtnSig


Remembering, that OP actually wants to modify the formatting of the GtkSpinButton text another idea came into mind.

A notable extension of the gtkmm binding (in comparison to GTK+) is the fact that all the GTK+ widget class signals are provided as virtual methods. (I miss this feature very much in Qt where you have either virtual methods or signals but (IMHO) never both of them.) Practically, this means, in gtkmm you always have the option

  • to make a derived widget class with enhanced capabilities (overloading the virtual methods for certain signals) or
  • to modify the behavior of an individual instance (by connecting signal handlers).

Beside of this, it is, of course, also possible to derive a widget class which connects a method of its own to a signal inherited from the base class. (In gtkmm 2.4, I had to do this in the rare cases where the virtual methods were missing for GTK+ signals.)

So, I modified the above sample adding a derived SpinButton and changing the signal callbacks to format the spin button text.

testGtkSpinBtnSig.cc:

#include <sstream>
#include <iomanip>
#include <gtkmm/application.h>
#include <gtkmm/box.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/window.h>

std::string format(double value)
{
  std::ostringstream out;
  out << std::fixed << std::setw(4) << std::setprecision(1) << std::setfill('0')
    << value;
  return out.str();
}

class SpinButton: public Gtk::SpinButton {
  public:
    SpinButton (double climb_rate = 0.0, guint digits = 0):
      Gtk::SpinButton(climb_rate, digits)
    { }
    virtual ~SpinButton() = default;
    SpinButton(const SpinButton&) = delete;
    SpinButton& operator=(const SpinButton&) = delete;

  protected:
    virtual bool on_output() override;
};

bool SpinButton::on_output()
{
  const double value = get_value();
  set_text(format(value));
  return true;
}

class Window: public Gtk::Window {

  private:
    Gtk::VBox _gtkVBox;
    Gtk::SpinButton _gtkSpinBtn1;
    Gtk::SpinButton _gtkSpinBtn2;
    Gtk::SpinButton _gtkSpinBtn3;
    SpinButton _gtkSpinBtn4; // derived SpinButton

  public:
    Window();
    virtual ~Window() = default;
    Window(const Window&) = delete;
    Window& operator=(const Window&) = delete;

  protected:
    static gboolean on_spinbtn_output_p(Gtk::SpinButton *pGtkSpinBtn);
    static gboolean on_spinbtn_output_r(Gtk::SpinButton &gtkSpinBtn);
    gboolean on_spinbtn3_output();
};

Window::Window(): Gtk::Window()
{
  _gtkSpinBtn1.set_range(0.0, 10.0); _gtkSpinBtn1.set_value(1.0);
  _gtkVBox.pack_start(_gtkSpinBtn1);
  _gtkSpinBtn2.set_range(0.0, 10.0); _gtkSpinBtn2.set_value(2.0);
  _gtkVBox.pack_start(_gtkSpinBtn2);
  _gtkSpinBtn3.set_range(0.0, 10.0); _gtkSpinBtn3.set_value(3.0);
  _gtkVBox.pack_start(_gtkSpinBtn3);
  _gtkSpinBtn4.set_range(0.0, 10.0); _gtkSpinBtn4.set_value(4.0);
  _gtkVBox.pack_start(_gtkSpinBtn4);
  add(_gtkVBox);
  _gtkVBox.show_all();
  // install signal handlers
  _gtkSpinBtn1.signal_output().connect(
    sigc::bind(
      sigc::ptr_fun(&Window::on_spinbtn_output_p),
      &_gtkSpinBtn1));
  _gtkSpinBtn2.signal_output().connect(
    sigc::bind(
      sigc::ptr_fun(&Window::on_spinbtn_output_r),
      sigc::ref(_gtkSpinBtn2)));
  _gtkSpinBtn3.signal_output().connect(
    sigc::mem_fun(this, &Window::on_spinbtn3_output));
}

gboolean Window::on_spinbtn_output_p(Gtk::SpinButton *pGtkSpinBtn)
{
  pGtkSpinBtn->set_text(format(pGtkSpinBtn->get_value()));
  return true;
}

gboolean Window::on_spinbtn_output_r(Gtk::SpinButton &gtkSpinBtn)
{
  gtkSpinBtn.set_text(format(gtkSpinBtn.get_value()));
  return true;
}

gboolean Window::on_spinbtn3_output()
{
  _gtkSpinBtn3.set_text(format(_gtkSpinBtn3.get_value()));
  return true;
}

int main(int argc, char *argv[])
{
  auto app = Gtk::Application::create(argc, argv, "Test SpinButton Signals");
  Window gtkWin;
  return app->run(gtkWin);
}

Compiled and tested:

$ g++ testGtkSpinBtnSig.cc -Wall $(pkg-config gtkmm-3.0 --cflags --libs) -o testGtkSpinBtnSig

Snapshot of testGtkSpinBtnSig (2nd version)

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • You're the *Scheff*, Scheff. Excellent detail with working examples. With the examples, the plus/minus buttons did not work for me (in disabled state). I got around this by using `setting(Gtk::Adjustment::create(50.0, 0.0, 100.0, 1.0, 1.0, 0.0))` along with `spinbutton(setting)`. – jmpsabisb Aug 20 '18 at 20:50
  • @jmpsabisb Good point. I assume the [+], [-] didn't work as I left the constructor's `climb_rate` argument out - default is `0.0`. I forgot to mention that the `Gtk::SpinButton` provides some "formatting" methods on its own. So, may be, a proper configuration is even sufficient without signal handling/overloading. However, I feel you're "on track". ;-) – Scheff's Cat Aug 21 '18 at 05:35