Создание виджетов

Создание классов виджетов.

Все классы виджетов UniWidgets создается в соответствующем пространстве имен и наследуются от класса Gtkmm виджета(в данном примере Gtk::EventBox) или от уже реализованного виджета UniWidgets. Необходимо определить констукторы класса и деструктор,а также закрытый метод NameWidget::constructor(), который будет вызываться в констукторе. Макрос DISALLOW_COPY_AND_ASSIGN делает закрытыми членами конструктор копирования и оператор присваивания(это уменьшает набор допустимых значений при инициализации). Макрос ADD_PROPERTY создает свойство для виджета и определяет методы доступа к свойству(подробнее по макросу смотреть в include/global_macros.h).
Пример h файл класса NameWidget:
#ifndef _NAMEWIDGET_H
#define _NAMEWIDGET_H
// -------------------------------------------------------------------------
#include <gtkmm.h>
#include <global_macros.h>
// -------------------------------------------------------------------------
namespace UniWidgets
{
class NameWidget : public Gtk::EventBox
{
public:
    NameWidget();
    explicit NameWidget(Gtk::EventBox::BaseObjectType* gobject);
    virtual ~NameWidget();

protected:
    // Variables
    ...

    // Methods
    ...

private:
    // Methods
    void constructor();

    ...

    DISALLOW_COPY_AND_ASSIGN(NameWidget);
    ADD_PROPERTY(PropertyName,PropertyType)
};

}

#endif

сс файл класса NameWidget.
#include <iostream>
#include "NameWidget.h"
// -------------------------------------------------------------------------
using namespace std;
using namespace UniWidgets;
using namespace Glib;
// -------------------------------------------------------------------------
void NameWidget::constructor()
{

    ...

}
// -------------------------------------------------------------------------
NameWidget::NameWidget() :
    Glib::ObjectBase("NameWidget")
{
    constructor();
}
// -------------------------------------------------------------------------
NameWidget::NameWidget(Gtk::EventBox::BaseObjectType* gobject) :
    Gtk::EventBox(gobject)
{
    constructor();
}
// -------------------------------------------------------------------------
NameWidget::~NameWidget()
{
}
// -------------------------------------------------------------------------
Glib::ObjectBase("NameWidget") задает имя виджета, необходимое при создании из glade файла.

Допустим это класс какой-то компоненты(картинка, текст и т.п.) т.е. то что рисуется на экране. Тогда файлы класса мы помещаем в директорию "components" и далее нужно добавить следующие изменения в файлы этой директории:

Использование плагина libglade для создания виджета.

После того как новый виджет был создан, нужно добавить его в плагин, чтобы он создавался из glade файла и был доступен в glade-3 редакторе.
Плагин находится в папке plugins/ и нужно в этой папке добавить новый виджет в widgets_list, чтобы для него сгенерировались нужные функции:
В секцию components добавляем строку в конец списка.
NameWidget,GLADE_DEFAULT_FUNCS,Obj_Get_Type, glade_new<NameWidget>, NULL;
,где

Далее переходим в каталог plugins/libglade, где после компиляции проекта в файле libglade.cc должна появиться строка в функции glade_module_register_widgets() примерно такая:

glade_register_widget(Obj_Get_Type<NameWidget>(), glade_new<NameWidget>, NULL,NULL);
Это функция, которая регистрирует конструктор для нового виджета.
Для работы с glade-3 необходимо внести следующие дополнения в файл plugins/glade/uniwidgets.xml
В секцию <glade-widget-classes> добавить:
<glade-widget-class name="gtkmm__CustomObject_namewidget" generic-name="namewidget" title="NameWidget" >
    <get-type-function>namewidget_get_type</get-type-function>
    <post-create-function>namewidget_postcreate</post-create-function>
    <action-activate-function>glade_action_activate</action-activate-function>
    <actions>
        ...
    </actions>
    <properties>
        ...
    </properties>
</glade-widget-class>

В секцию <glade-widget-group name="Components" title="Components"> добавить:
<glade-widget-class-ref name="gtkmm__CustomObject_namewidget"/>

После внесения всех изменений можно запускать glade-3 редактор, но только скрипт(utilities/scripts/start_glade.sh) так как там указан дополнительно путь к плагину.

# Path to uniwidgets directory
export UWPATH=`pwd`/../..
 # $(dirname $0)/../..
export GLADE_CATALOG_PATH=$UWPATH/plugins/glade
export GLADE_MODULE_PATH=$GLADE_CATALOG_PATH/.libs
После установки пакета libuniwidgets в систему можно просто запускать glade-3 и все виджеты будут содержаться в палитре.

Использование gtk::builder для создания виджета.

Для загрузки виджетов из glade файлов можно использовать механизм Gtk::Builder. Gtk::Builder в отличие от libglade не является отдельной библиотекой,а входит в состав GTK+ и является рекомендуемым подходом для создания и использования glade файлов.
Для того чтобы виджеты UniWidgets можно было загружать из glade файла нужно зарегистрировать типы виджетов(точно также как и для стандартных виджетов Gtkmm):
int main (int argc, char **argv)
{
   ...
Gtk::Main Kit(argc, (char**&)argv);
UniWidgets::init(argc, (char**&)argv);
   ...
}
Здесь Gtk::Main Kit(argc, (char**&)argv) - регистрирует типы для виджетов Gtkmm,а UniWidgets::init(argc, (char**&)argv) - регистрирует типы для виджетов UniWidgets. Функция init(...) содержит вызовы функций для каждого виджета, которые выполняют регистрацию, например:
void wrap_init_components()
{
    // Map gtypes to gtkmm wrapper-creation functions:
    Glib::wrap_register(IMAGEBLINKGETTYPE, &Obj_Wrap_New<ImageBlink>);
    Glib::wrap_register(IMAGEGETTYPE, &Obj_Wrap_New<Image>);
    ...

    // Register the gtkmm gtypes:
    Obj_Get_Type<ImageBlink>();
    Obj_Get_Type<Image>();
    ...
} // wrap_init()
Следовательно для каждого виджета должны быть определены функции Obj_Wrap_New и Obj_Get_Type. Шаблоны этих функций объявлены в файле include/plugins.h. Здесь объявлено два типа функиций UObj_Wrap_New и UObj_Get_Type для виджетов наследуемых от UDefaultFunction(SimpleObject,...) и, Obj_Wrap_New и Obj_Get_Type для виджетов наследуемых от Gtkmm виджетов. В каждом каталоге с виджетами(components,objects,typical и т.д.) находится файл wrap_init.h(cc) с определением функции инициализации виджетов.
В методе конструкторе( constructor() ) каждого виджета есть строчки:
    GObjectClass *goclass = G_OBJECT_GET_CLASS(gobj());
    goclass->set_property = &custom_set_property_callback<SomeWidget>;
Это назначение нового обработчика назначения свойств виджета и служит для корректного назначения свойств произоводного виджета. Код функции исправляет порядок инициализации виджета, вызывая функцию Obj_Wrap_New перед тем как задать свойства виджета. К этому моменту свойства класса виджета будут созданы и готовы к работе.
template<class Type> static void custom_set_property_callback_object(GObject * object, unsigned int property_id, const GValue * value, GParamSpec * param_spec)
{
    if( !Glib::ObjectBase::_get_current_wrapper(object) )
        Obj_Wrap_New<Type>(object);
        Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object);
        if (!wrapper)
                return;
        Type *renderer = dynamic_cast<Type *>(wrapper);
        if (!renderer)
                return;
    Glib::custom_set_property_callback(object,property_id,value,param_spec);
}
После инициализации всех виджетов можно загружать glade файл в формате Gtk::Builder(должен быть сохранен в соответствующем формате) и происходит это примерно так:
    ...
    Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create();

    try
    {
        //gladedir - путь к директории с glade файлами, guifile - файл главного окна приложения;
        builder->add_from_file(gladedir + guifile);
    }catch(Gtk::BuilderError& ex){
        cerr << "MW builder create: " << ex.what() << endl;
        return;
    }
    //Получить connector для опроса датчиков
    UProxyWidget *pw=0;
    builder->get_widget("uproxywidget1", pw);
    ...
Заметки:
При создании glade файла, для всех контейнеров SimpleObject необходимо указывать хотя бы одно свойства для виджета помимо стандартных. Например:
...
<child>
      <object class="gtkmm__CustomObject_uproxywidget" id="uproxywidget1">
        <property ... />
        <child>
              <object class="GtkFixed" id="fixed1">
                <property ... />
                <child>
                  <object class="gtkmm__CustomObject_ulocknotebook" id="ulocknotebook1">
                    <property ... />
                    <child>
                      <object class="GtkFixed" id="sensors">
                        <property ... />
                            <child>
                              <object class="gtkmm__CustomObject_simpleobject" id="simplesensor1_di">
                                <property name="visible">True</property>
                                <property name="disconnect_effect">1</property>
                                <property name="lock-view">1</property>
                                <child>
                                  <object class="gtkmm__CustomObject_simpleobject" id="simplesensor1_image1">
                                    <property name="visible">True</property>
                                    <property name="disconnect_effect">0</property>
                                    <child>
                                      ...
                                </child>
                                <child>
                                  <object class="gtkmm__CustomObject_simpleobject" id="simplesensor1_text2">
                                    <property name="visible">True</property>
                                    <property name="disconnect_effect">0</property>
                                      ...
                                </child>
                                <child>
                                  <object class="gtkmm__CustomObject_simpleobject" id="simplesensor1_textnum3">
                                    <property name="visible">True</property>
                                    <property name="disconnect_effect">0</property>
                                    ...
                                </child>
...
Если свойства указаны не будут, то виджет не будет создан как нужно!

Создание собственного GTK+ виджета и С++ обертки для него.

Можно создавать GTK+ виджеты,а потом писать класс обертку для них в Gtkmm. Это может быть полезно когда нужно создать новый специфический тип виджета, которого нет в стандартном наборе. Данный пример показывает создание класса виджетов индикаторов уровня.

Создание собственного GTK+ виджета.

Виджет индикатора уровня представляет из себя область, в которой отрисовывается столбик переменной высоты в зависимости от текущего значения уровня. Структура виджета задана в struct _GtkBar, где указан базовый объект GtkWidget и два свойства объекта: значение уровня и цвет(в примере не используется, а показывает как работать со свойствами). В начале файла под директивами define определяются служебные макросы для получения типа виджета и приведения к разным типам объекта. В структуре struct _GtkBarClass объявляются функции, который в дальнейшем буду virtual в gtkmm классе.
#ifndef __BAR_H
#define __BAR_H

#include <gtk/gtk.h>
#include <cairo.h>

G_BEGIN_DECLS

#define GTK_TYPE_BAR        (gtk_bar_get_type ())
#define GTK_BAR(obj)        (GTK_CHECK_CAST ((obj), GTK_TYPE_BAR, GtkBar))
#define GTK_BAR_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_BAR, GtkBarClass))
#define GTK_IS_BAR(obj)     (GTK_CHECK_TYPE ((obj), GTK_TYPE_BAR))
#define GTK_IS_BAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_BAR))
#define GTK_BAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_BAR, GtkBarClass))

typedef struct _GtkBar GtkBar;
typedef struct _GtkBarClass GtkBarClass;


struct _GtkBar {
  GtkWidget widget;

  gchar *color;
  gint sel;
};

struct _GtkBarClass {
  GtkWidgetClass parent_class;

  void (* bar_paint) (GtkBar *bar);
};


GtkType gtk_bar_get_type(void);
void gtk_bar_set_sel(GtkBar *bar, gint sel);    // функция для выставления значения уровня
GtkWidget * gtk_bar_new();          // создание виджета
void gtk_bar_set_color(GtkBar *bar,     // выставить занчение переменной цвет
                       const gchar *str);
gchar* gtk_bar_get_color(GtkBar *bar);      // получить значение переменной цвета
gint gtk_bar_get_sel(GtkBar *bar);      // получить значение переменной уровня

G_END_DECLS

#endif // __BAR_H

Реализация находится в файле test/test_figure/bar.cc. В файле bar.cc объявлены все необходимые статические функциии и переменные

static gpointer gtk_bar_parent_class = NULL;
static void gtk_bar_class_init(GtkBarClass *klass);
static void gtk_bar_init(GtkBar *bar);
static void gtk_bar_size_request(GtkWidget *widget, GtkRequisition *requisition);
static void gtk_bar_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
static void gtk_bar_realize(GtkWidget *widget);
static gboolean gtk_bar_expose(GtkWidget *widget, GdkEventExpose *event);
static void gtk_bar_paint_real(GtkBar *bar);
static void gtk_bar_destroy(GtkObject *object);
static void gtk_bar_set_arg(GtkObject *object,GtkArg *arg,guint arg_id);
static void gtk_bar_get_arg(GtkObject *object,GtkArg *arg,guint arg_id);
static void gtk_bar_paint(GtkBar *bar);
static GtkWidgetClass *parent_class = NULL;
Отметим основной метод инициализации класса виджета:
...
static void
gtk_bar_class_init(GtkBarClass *klass)
{
  GtkWidgetClass *widget_class;
  GtkObjectClass *object_class;


  widget_class = (GtkWidgetClass *) klass;
  object_class = (GtkObjectClass *) klass;

  parent_class = (GtkWidgetClass *)gtk_type_class(gtk_widget_get_type());

  gtk_object_add_arg_type ("GtkBar::color", GTK_TYPE_STRING, GTK_ARG_READWRITE,ARG_COLOR);

  gtk_bar_parent_class = g_type_class_peek_parent (klass);
  klass->bar_paint = gtk_bar_paint_real;

  object_class->set_arg = gtk_bar_set_arg;
  object_class->get_arg = gtk_bar_get_arg;
  widget_class->realize = gtk_bar_realize;
  widget_class->size_request = gtk_bar_size_request;
  widget_class->size_allocate = gtk_bar_size_allocate;
  widget_class->expose_event = gtk_bar_expose;
  object_class->destroy = gtk_bar_destroy;
}
...
Здесь функции объявленные выше задаются в качестве указателей на функции базового виджета и теперь в них определено поведение виджета.

Создание Gtkmm обертки для нового виджета.

После создания GTK+ виджета нужно сделать для него C++ обертку. Реализация этого класса находится в файлах unibar.h, unibar.cc(test/test_figure/) и unibar_p.h(test/test_figure/private). Заголовочный файл объявлен следующим образом:
#ifndef _GTKMM_BAR_H
#define _GTKMM_BAR_H

#include <glibmm.h>

#include <gtkmm/widget.h>
#include <gtkmm/stockid.h>


#ifndef DOXYGEN_SHOULD_SKIP_THIS
typedef struct _GtkBar GtkBar;
typedef struct _GtkBarClass GtkBarClass;
#endif // DOXYGEN_SHOULD_SKIP_THIS

namespace Gtk
{
namespace UniWidgets
{

class Bar_Class;

namespace Stock { struct BuiltinStockID; }

class Bar : public Widget
{
  public:
#ifndef DOXYGEN_SHOULD_SKIP_THIS
  typedef Bar CppObjectType;
  typedef Bar_Class CppClassType;
  typedef GtkBar BaseObjectType;
  typedef GtkBarClass BaseClassType;
#endif // DOXYGEN_SHOULD_SKIP_THIS

  virtual ~Bar();

#ifndef DOXYGEN_SHOULD_SKIP_THIS
  void set_sel(gint value);
  gint get_sel();
private:
  friend class Bar_Class;
  static CppClassType bar_class_;

  // noncopyable
  Bar(const Bar&);
  Bar& operator=(const Bar&);

protected:
  explicit Bar(const Glib::ConstructParams& construct_params);
  explicit Bar(GtkBar* castitem);

#endif // DOXYGEN_SHOULD_SKIP_THIS

  virtual void bar_paint_vfunc();

public:
#ifndef DOXYGEN_SHOULD_SKIP_THIS
  static GType get_type()      G_GNUC_CONST;
  static GType get_base_type() G_GNUC_CONST;
#endif

  GtkBar*       gobj()       { return reinterpret_cast<GtkBar*>(gobject_); }

  const GtkBar* gobj() const { return reinterpret_cast<GtkBar*>(gobject_); }


public:
#ifdef GLIBMM_PROPERTIES_ENABLED
    Glib::PropertyProxy<Glib::ustring> property_color() ;
#endif //GLIBMM_PROPERTIES_ENABLED
#ifdef GLIBMM_PROPERTIES_ENABLED
    Glib::PropertyProxy_ReadOnly<Glib::ustring> property_color() const;
#endif //GLIBMM_PROPERTIES_ENABLED
private:

public:

  Bar();

//   explicit Bar();

  explicit Bar(const StockID& stock_id);

};

} //namespace UniWidgets
} // namespace Gtk


namespace Glib
{
  Gtk::UniWidgets::Bar* wrap(GtkBar* object, bool take_copy = false);
} //namespace Glib


#endif // _GTKMM_BAR_H
Впринципе все как для стандартных виджетов Gtkmm, но здесь добалена виртуальная функция bar_paint_vfunc, для того чтобы можно было менять отрисовку в произодных виджетах перегружая одну функцию,и добалено свойство "color",чтобы можно было его добавлять из glade-3 редактора. Далее был создан файл unibar_p.h, в котором определен класс Bar_Class с методами инициализации при создании виджета.

#ifndef _GTKMM_BAR_P_H
#define _GTKMM_BAR_P_H
// -------------------------------------------------------------------------
#include <gtkmm/private/widget_p.h>
#include <glibmm/class.h>
// // -------------------------------------------------------------------------
namespace Gtk
{
namespace UniWidgets
{

class Bar_Class : public Glib::Class
{
public:
#ifndef DOXYGEN_SHOULD_SKIP_THIS
  typedef Bar CppObjectType;
  typedef GtkBar BaseObjectType;
  typedef GtkBarClass BaseClassType;
  typedef Gtk::Widget_Class CppClassParent;
  typedef GtkWidgetClass BaseClassParent;

  friend class Bar;
#endif // DOXYGEN_SHOULD_SKIP_THIS

  const Glib::Class& init();

  static void class_init_function(void* g_class, void* class_data);

  static Glib::ObjectBase* wrap_new(GObject*);

protected:

#ifdef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
#endif //GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED

  //Callbacks (virtual functions):
#ifdef GLIBMM_VFUNCS_ENABLED
  static void bar_paint_vfunc_callback(GtkBar* self);
#endif //GLIBMM_VFUNCS_ENABLED
};

} // namespace UniWidgets
} // namespace Gtk

#endif // _GTKMM_BAR_P_H

Создание производного виджета.

Создание виджета от собственного виджета аналогично созданию производного виджета от стандартного класса. В файлах unibar2 создан производный виджет и переопределен метод отрисовки.
#ifndef _UNIBAR2_H
#define _UNIBAR2_H
// -------------------------------------------------------------------------
#include <glibmm.h>
#include "unibar.h"
// -------------------------------------------------------------------------
namespace Gtk
{

namespace UniWidgets
{

class UniBar2 : public Bar
{
public:
    UniBar2();
    UniBar2(const Glib::ConstructParams& construct_params);
    explicit UniBar2(Bar::BaseObjectType* gobject);
    virtual ~UniBar2();

    virtual void bar_paint_vfunc();
    static GType Get_Type();
    static Glib::ObjectBase* Wrap_New(GObject* o);

protected:

    // Properties
    Glib::Property<Glib::ustring> property_back_color_;

private:

    void constructor();
    static void custom_get_property_callback(GObject *object, unsigned int property_id,
                                                 GValue *value, GParamSpec *param_spec);
    static void custom_set_property_callback(GObject *object, unsigned int property_id,
                                                 const GValue *value, GParamSpec *param_spec);
public:

    Glib::PropertyProxy<Glib::ustring> property_back_color() ;
    Glib::PropertyProxy_ReadOnly<Glib::ustring> property_back_color() const;

};

}//namespace UniWidgets
}//namespace Gtk

#endif
Для виджета переопределен виртуальный метод bar_paint_vfunc.

Использование виджетов с glade-3 редактором.

Для работы виджетов в glade-3 редакторе необходимо сделать несколько действий.

Пример glade файла из примера для создания приложения с помощью Gtk::Builder.

<?xml version="1.0"?>
<interface>
  <!-- interface-requires GtkBar 0.0 -->
  <requires lib="gtk+" version="2.16"/>
  <!-- interface-naming-policy project-wide -->
  <!-- interface-local-resource-path /srv/ilyap/Projects/uniwidgets/utilities/tests/libglade/svg -->
  <object class="GtkWindow" id="window1">
    <property name="width_request">500</property>
    <property name="height_request">500</property>
    <property name="title" translatable="yes">UniWidgets - test</property>
    <property name="resizable">True</property>
    <property name="default_width">500</property>
    <property name="default_height">500</property>
    <child>
      <object class="GtkFixed" id="fx">
        <property name="visible">True</property>
        <child>
      <object class="gtkmm__CustomObject_UniBar" id="bar">
            <property name="width_request">200</property>
            <property name="height_request">200</property>
            <property name="visible">True</property>
            <property name="color">green</property>
        <property name="back-color">black</property>
          </object>
      <packing>
          <property name="x">30</property>
          <property name="y">40</property>
          </packing>
        </child>
        <child>
          <object class="GtkVScrollbar" id="vs">
            <property name="width_request">200</property>
            <property name="height_request">80</property>
            <property name="visible">True</property>
            <property name="orientation">vertical</property>
          </object>
          <packing>
            <property name="x">300</property>
            <property name="y">20</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Документация по UniWidgets. Последние изменения: Fri Oct 10 09:57:50 2014. Создано системой  doxygen 1.5.9