Gtk::Builder: autoconnecting signal handlers



*** Why we don't have Gtk::Builder::connect_signals() now:

With C, GtkBuilder (and libglade) allow you to name the signal handler
(a C callback function) for a widget's signal in your .glade file. Using
GModule, GtkBuilder then finds that function in your application and
connects it to the signal.

It's much harder to find C++ methods than C methods, plus we care more
about type safety and we have to deal with member methods (with an
associated instance) rather than just standalone functions.


*** An idea:

However, Stas Sergeev thought of an ingenious way to get this feature
with gtkmm. Here's the idea:

1. Modify gmmproc's _WRAP_SIGNAL() to add details of the signal to a
map, so that each gtkmm object can tell us at runtime what signals it
has. We could delay the filling of this map until
Gtk::Builder::connect_signals() is used, meaning there would be no
memory or speed cost if it was never used.

2. Fill a "SlotsContainer" object with named sigc::slots that will be
mentioned in the .glade file, and provide this to
Gtk::Builder::connect_signals().
Some macros mean that you don't need to write the method name as a
string and as a method name. I find the macros ugly, but I prefer them
to Qt's slots: keyword which does much the same thing via moc.

3. Use gtk_builder_connect_signals_full(), providing a callback that
lets us do the connection in our on way.

4. By making sigc::slot_base a polymorphic base class (by giving it a
virtual destructor), connect_signals() can use dynamic_cast<> to check
at runtime that the specified signal handler is of the correct type
(meaning it has the same parameters).

Here is the gtkmm patch, though it includes a hack to get around the
non-existence of the gmmproc modification, hard-coding this into
Gtk::Button for the sake of the examples: 
http://bugzilla.gnome.org/show_bug.cgi?id=573482#c64

I have attached a basic example (basic_main.cc) and the two files for
the derived widget example (deriveddialog.*), so you can see how it
might be used.

It seems that it can work. Thoughts?


*** Do we want this?

I don't personally feel the need to specify my signal handlers in
the .glade file. I like having code in code and UI in the XML file. But
I recognize that I've maybe just become used to it so my judgement may
be flawed.

Even if we want this feature, notice that this idea still requires us to
mention the signal handler in both the .glade file and the
SlotsContainer (and of course in the signal handler's declaration and
definition as before).

So it would replace 
  get_widget("something").something.connect(sigc::mem_fun(...))
  (with checks for null)
with
  mentioning it in the .glade file and adding it to the SlotContainer,
and calling connect_signals().
which doesn't seem like a big gain to me.

Thoughts?

-- 
murrayc murrayc com
www.murrayc.com
www.openismus.com
#include <gtkmm.h>
#include <iostream>

Gtk::Dialog* pDialog = 0;

class MySlots : public Glib::SlotsContainer
{
private:
  virtual void init_slots(void)
  {
    GLIBMM_SLOT(MySlots::on_button_clicked);
  }

void on_button_clicked()
{
  if(pDialog)
    pDialog->hide(); //hide() will cause main::run() to end.
}

};

int main (int argc, char **argv)
{
  MySlots slots;
  Gtk::Main kit(argc, argv);

  //Load the GtkBuilder file and instantiate its widgets:
  Glib::RefPtr<Gtk::Builder> refBuilder = Gtk::Builder::create();
  try
  {
    refBuilder->add_from_file("basic.ui");
  }
  catch(const Glib::FileError& ex)
  {
    std::cerr << "FileError: " << ex.what() << std::endl;
    return 1;
  }
  catch(const Gtk::BuilderError& ex)
  {
    std::cerr << "BuilderError: " << ex.what() << std::endl;
    return 1;
  }

  refBuilder->connect_signals(slots);

  //Get the GtkBuilder-instantiated Dialog:
  pDialog = refBuilder->get_widget<typeof(*pDialog)>("DialogBasic");
  if(pDialog)
    kit.run(*pDialog);

  return 0;
}
//$Id: deriveddialog.cc 588 2004-02-13 17:10:43Z murrayc $ -*- c++ -*-

/* libglademm example Copyright (C) 2003 libglademm development team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "deriveddialog.h"

DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade)
: Gtk::Dialog(cobject),
  m_refGlade(refGlade),
  m_pButton(0)
{
  m_refGlade->connect_signals(*this);
}

DerivedDialog::~DerivedDialog()
{
}

void DerivedDialog::on_button_quit()
{
  hide(); //hide() will cause main::run() to end.
}
//$Id: deriveddialog.h 691 2004-06-02 13:33:10Z mxpxpod $ -*- c++ -*-

/* libglademm example Copyright (C) 2003 libglademm development team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef GTKMM_EXAMPLE_DERIVED_DIALOG_H
#define GTKMM_EXAMPLE_DERIVED_DIALOG_H

#include <gtkmm.h>


class DerivedDialog : public Gtk::Dialog, public Glib::SlotsContainer
{
public:
  DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade);
  virtual ~DerivedDialog();

protected:
  //Signal handlers:
  virtual void on_button_quit();

  Glib::RefPtr<Gtk::Builder> m_refGlade;
  Gtk::Button* m_pButton;

private:
GLIBMM_DECLARE_SLOTS(
  GLIBMM_SLOT(DerivedDialog::on_button_quit)
);

};

#endif //GTKMM_EXAMPLE_DERIVED_WINDOW_H


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]