通过引用传递给信号处理程序

Passing by reference to signal handler

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

编译

目前的工作:

Displays a window with a simple SpinButton

我想做的事情:

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

问题:

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

main.cc(编译正常,未通过引用):

#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(不编译,尝试传递引用):

#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);
}

我必须承认,几年前我从 gtkmm 转向了 Qt。出于好奇,我在我的 cygwin(我在 Windows 10)中安装了 gtkmm 3 来准备一个示例(并找出我对 gtkmm 的生疏程度)。

考虑到OP的尝试,我使用了多个不同的签名可以达到同样的效果

  1. static gboolean Window::on_spinbtn_output_p(Gtk::SpinButton *pGtkSpinBtn);
    对应的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);
    对应的connect():

    _gtkSpinBtn2.signal_output().connect(
      sigc::bind(
        sigc::ptr_fun(&Window::on_spinbtn_output_r),
        sigc::ref(_gtkSpinBtn2)));
    
  3. gboolean Window::on_spinbtn3_output()(非静态)
    对应的connect():

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

完整样本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);
}

编译测试:

$ 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


请记住,OP 实际上想要修改 GtkSpinButton 文本的格式,我想到了另一个想法。

gtkmm 绑定的一个显着扩展(与 GTK+ 相比)是所有 GTK+ 小部件 class 信号都是作为虚拟方法提供的。 (我非常想念 Qt 中的这个功能,你有 either 虚拟方法 信号,但(恕我直言)从来没有。)实际上,这意思是,在 gtkmm 中你总是有选项

  • 制作具有增强功能的派生小部件class(为某些信号重载虚拟方法)或
  • 修改单个实例的行为(通过连接信号处理程序)。

除此之外,当然也可以派生一个小部件 class,它将自己的方法连接到从基础 class 继承的信号。 (在 gtkmm 2.4 中,我不得不在 GTK+ 信号缺少虚拟方法的极少数情况下这样做。)

因此,我修改了上面的示例,添加了派生的 SpinButton 并更改了信号回调以格式化微调按钮文本。

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);
}

编译测试:

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