Как создать главное окно программы в Gtk+


   
 

Версия: 0.2 Aвтор: Андрей Ржавсков (Andrey Rjavskov [Rzhavskov]) as known rjaan


   
 

1.Основные элементы

 
 

Как театр начинается с вешалки, так и графическое приложение (программа) ugtkmainwindow начинается с главного окна UGtkMainWindow. Оно, окно, наследует базовый тип GtkWindow, c помощью которого размещаются виджеты или элементы управления графического интерфейса оконной системы. С помощью GtkWindow пользователи могут интерактивно взаимодействовать с главным окном программы, используя основные операции, такие как изменение размера, перемещение по графическому рабочему столу, закрытия и т.д.

В предлагаемом примере, как показано на рисунке слева, в главном окне программы ugtkmainwindow вертикально сверху вниз размещены следующие виджеты:

  • панель меню UGtkMenubar (не реализован в данной версии),
  • панель инструментов UGtkToolbar (не реализован в данной версии),
  • статус-панель UGtkStatusbar (не реализован в данной версии),
  • Пользовательские виджеты без ограничения их количества.
  • Для того, чтобы перечисленные виджеты изменяли свой размер вместе с размером главного окна, в UGtkMainWindow используется бокс вертикального размещения виджетов GtkVBox.

   
 

2. Программный интерфейс

 
 

Главное окно программы ugtkmainwindow реализовано в виде программного интерфейса, использующего объектно-ореентированную среду разработки, лежащей в основе Gtk+ объектной системы Glib (библиотека gobject) для языка Си (далее по тексту — С/GObject). Её не следует путать с библиотекой GLIB, предоставляющей функциональный набор для работы с фундаментальными типами и алгоритмами в Gtk+, такими как списки, хэши и т.д.

Для того, чтобы при запуске программы ugtkmainwindow было создано главное окно, необходимо сначало зарегистрировать тип UGtkMainWindow с использованием в С/GObject структуры GTypeInfo и функции g_type_register_static(). Данный тип является статическим типом и позволяет наследовать такой же статический тип GtkWindow.

#include <gobject/gtype.h>
typedef struct _GTypeInfo     GTypeInfo; /* информация о вновь создаваемом типе */
 
struct _GTypeInfo
{
/* Типы интерфейсов, классифицируемые типы, экземпляры типов */
guint16              class_size; /* размер класса */
 
GBaseInitFunc        base_init; /* Функция инициализации ( как конструктор в С++) */
GBaseFinalizeFunc    base_finalize; /* Функция завершения ( как деструктор в С++) */
/* Типы интерфейсов,классифицируемые типы, экземпляры типов */
GClassInitFunc       class_init; /* конструктор класса */
GClassFinalizeFunc   class_finalize; /* деструктор класса */
gconstpointer        class_data; * приватные данные класса */
 
/* экземпляры типов */
guint16              instance_size; /* размер экземпляра */
guint16              n_preallocs; /* количество созданных экземпляров c
выделением памяти (оператор new() в С++ ) */
GInstanceInitFunc    instance_init; /* Инциализация экземпляра*/
 
/*Таблица функций, встроенных обработчиков GValues */
const GTypeValueTable  *value_table;
};
 
/* регистрация статического типа */
GType g_type_register_static ( GType parent_type, /* Родительский тип */
const gchar *type_name, /* текстовое имя типа */
const GTypeInfo *info, /* GTypeInfo */
GTypeFlags flags); /* Флаги */

Виджеты (объекты) в С/GObject имеют строгую иерархию с наследованием свойств базового типа. Основным базовым типом является GObject, а уже от него в С/GObject создается тип GtkWidget, которой опять же является производным типом GtkWindow.

   
 

3. Создание дерева проекта библиотеки ugtk-widgets

 
 

Давайте не надолго отвлечемся от программирования и обратимся к управлению проектами. Создадим проект ugtkmainwindow со встроенной в него статической библиотекой Gtk-виджетов ugtk-widgets для чего воспользуемся Build System GNU.

Статическая библиотека ugtk-widgets будет имееть выходной файл libugtk-widgets.a. Рецепт встраивания и подключения данной библиотеки можно найти в статье Build System GNU: Как собирать и cкомпилировать статическую библиотеку.

После создания дерева проекта ugtkmainwindow и статической библиотеки Gtk-виджетов ugtk-widgets, можем перейти к её заполнению, т.е. реализации программного интерфейса UGtkMainWindow.

   
 

4. Реализация программного интерфейса UGtkMainWindow

 
 

4.1. Описание интерфейса

 
 

Программный интерфейс "Главного окна" реализуется через тип UGtkMainWindow, являющийся вершиной в цепочки наследования типов GtkWindow → GtkWidget → GObject. Базовым типом програмного интерфейса UGtkMainWindow является GtkWindow от которого фактически он и будет создан.

Создание типа UGtkMainWindow и его экземпляра в С/GObject значительно отличается от объектно-ореентированного программирования (ООП) на языке С++. Потому что в последнем достаточно задекларировать тип (класс) объекта, а затем через конструктор инициализировать его экземпляр. В С/GObject этого не достаточно, так как в отличии от ООП-ореентированных С++ и Java в С/GObject:

  • отсутствуют операторы перегрузки;
  • полиморфизм выполняется явным образом;
  • инкапсуляция выполняется в ручную, но ни как автоматически как это происходит в языках ООП.
   
 

4.2. Декларация типа и свойств объекта

 
 

Первая декларируемая структура _UGtkMainwindowClass используется для создания свойств класса, функции финализации (что-то наподобие деструктора класса ООП), добавление приватных данных и т.д.

Файл include/libugtk-widgets/ugtkmainwindow.h

 . . .
 
typedef struct _UGtkMainwindowClass UGtkMainwindowClass;
 
 . . .
 
struct _UGtkMainwindowClass {
GtkWindowClass    parent_class;
 
/* сигналы */
gboolean (*window_destroy) ( UGtkMainwindow* window,
GdkEvent *event );
};
 
 . . .
 

Вторая декларируемая структура будет использоваться экземпляром объекта данного типа и содержит переменные, указатели на открытые методы и свойства для данного типа объекта. Так же данная структура содержит указатель на структуру с закрытыми свойствами объекта.

В отличие от С++, где с помощью ключевых слов private и public свойства и методы деляться на закрытые и открытые, в С/GObject такая возможность отсутствует. Поэтому обычно прибегают к трюку, который реализован в компиляторе GCC, чтобы "скрыть" декларацию cтруктурного типа от его использования. Так в файле исходного кода ugtkmainwindow.c отдельно декларируется структура _UgtkMainwindowPriv, а затем в заголовочном файле ugtkmainwindow.h через спецификатор typedef опеределяется пользовательский тип, а вернее её синоним UgtkMainwindowPriv.

Файл libugtk-widgets/ugtkmainwindow.c

#include "<libugtk-widgets/ugtkmainwindow.h>"
 
 . . .
 
struct _UGtkMainwindowPriv {
 
/* Закрытые свойства */
gchar    *window_title; /* заголовок окна */
gchar    *host_name; /* имя узла */
};
 
 . . .
 

Файл include/libugtk-widgets/ugtkmainwindow.h

 
 . . .
 
typedef struct _UgtkMainwindowPriv   UGtkMainwindow;
 
 . . .
 

   
 

4.3 Сервисные макроопределения

 
 

В отличии от ООП таких как Java и С++ в С/GObject для манипуляций с типом объекта, таких как создание и приведение к типу, используются "сервисные" макроопределения (далее по тексту — макросы).

Для создания типа UGtkMainwindow, по-мимо выше указанных структур, нужно определить некоторые "сервисные" макросы в самом начале заголовочного файла исходного кода include/libugtk-widgets/ugtkmainwindow.h

Первым всегда определяется макрос возвращающий тип данного класса объекта.

#define UGTK_MAINWINDOW_TYPE (ugtk_mainwindow_get_type())

При первом использовании данного макроса в ugtk_mainwindow_new(), происходит создание типа объекта UGtkMainwindow с помощью функции g_type_register_static() и структуры GTypeInfo, описание которой приводилось выше. Последующие вызовы ugtk_mainwindow_new() будут приводить только к созданию объекта. Нередко данный макрос используют в документации в качестве обозначения типа объекта.

Следом за макросом UGTK_MAINWINDOW_TYPE() обычно определяется макрос UGTK_MAINWINDOW(), использумый для приведения типа объекта (заданной ему структурой экземпляра объекта) в цепочки наследования типов GtkWindow → GtkWidget → GObject и в соответсвие с определенном типом UGTK_MAINWINDOW_TYPE.

#define UGTK_MAINWINDOW(obj)(G_TYPE_CHECK_INSTANCE_CAST ((obj), UGTK_MAINWINDOW_TYPE, UGtkMainwindow))

Макрос UGTK_MAINWINDOW_CLASS() используется для приведения класса объекта к заданной ему структуре класса объекта и в соответствие с определенном типом UGTK_MAINWINDOW_TYPE. Данный макрос часто используют в документации в качестве обозначения класса объекта.

#define UGTK_MAINWINDOW_CLASS(klass)(G_TYPE_CHECK_CLASS_CAST ((klass), UGTK_MAINWINDOW_TYPE, UGtkMainwindowClass))

Два последних макроса служат для определения соответствию классу и экземпляру объекта в соответсвии с типом UGTK_MAINWINDOW_TYPE. Обычно такие макросы используются в функциях на подобие g_return_val_fail() и g_return_fail(), в публичных методах данного типа объекта, позволяя избегать пользователю критических ситуаций, связанных с неправильным обращением по типу объекта (Invalid object type).

#define UGTK_IS_MAINWINDOW(obj)(G_TYPE_CHECK_INSTANCE_TYPE ((obj), UGTK_MAINWINDOW_TYPE))
#define UGTK_IS_MAINWINDOW_CLASS(klass)(G_TYPE_CHECK_CLASS_TYPE ((klass), UGTK_MAINWINDOW_TYPE))

   
 

5. Использование программного интерфейса UGtkMainwindow

 
 

5.1. Создание экземплярa объекта

 
 

Использование программного интерфейса начинается с создание экземпляра объекта UGtkMainwindow вызовом публичного метода ugtk_mainwindow_new(), которая для этого использует g_object_new().

Файл srс/main.c

gint main ( gint argc, gchar *argv[] )
{
GtkWidget    *window;
 
 . . .
 
window = ugtk_mainwindow_new ();
gtk_widget_show ( window );
 
 . . .
 
return 0;
}

Файл libugtk-widgets/ugtkmainwindow.c

 
 . . .
 
GtkWidget* ugtk_mainwindow_new ( void )
{
GtkWidget    *widget;
 
widget = g_object_new ( UGTK_MAINWINDOW_TYPE, NULL );
 
return widget;
}
 
 . . .

Функция g_object_new() используется в С/GObject для создания нового экземпляра объекта производного от GObject с возможностью установки свойств объекта, которые были заданы при инициализации класса объекта.

После создание экземпляра объекта, его необходимо визуализировать, воспользовавшись для этого gtk_widget_show().

   
 

5.2. Инициализация класса и экземпляра объекта

 
 

В отличии от ООП языков, в С/GObject класс объекта UGtkMainwindow не достаточно просто описать структурным типом UgtkMainwindowСlass. Ему ещё явно нужно в функции ugtk_mainwindow_class_init() создать свойства объекта (в.т.ч сигналы), определить методы "завершения" объекта, установки и получение свойств объекта. Поэтому, для проведения таких манипуляций с структурой класса UgtkMainwindowСlass, следует в функцииu gtk_mainwindow_class_init() воспользоваться макросом приведения к G_OBJECT_CLASS() для последущего создания и определения:

  • закрытых свойств объекта, описанных структурным типом UGtkMainwindowPriv;
  • свойств объекта "mainwindow-title" и "mainwindow-hostname";
  • метода ugtk_mainwindow_finalize(), который будет вызван при "разрушении" экземпляра объекта данного типа;
  • cигнала "window-destroy", испускаемого при попытке закрыть окно главной формы, формируемого через использование экземпляра типа объекта UGtkMainwindow.

Файл libugtk-widgets/ugtkmainwindow.c

 
static GObjectClass   *parent_class = NULL;
 
 . . .
 
static void ugtk_mainwindow_class_init ( UGtkMainwindowClass *klass )
{
GObjectClass   *object_class;
 
parent_class = g_type_class_peek_parent(klass);
object_class = G_OBJECT_CLASS (klass);
 
 . . .
}

Функция ugtk_mainwindow_class_init() вызывается первой во время создания типа объекта UGTK_MAINWINDOW_TYPE, а второй функция ugtk_mainwindow_init(), инициализируящая по умолчанию:

  • свойства экземпляр объекта;
  • геометрические, стилистические и другие параметры интерфейса "Главного окна"
  • назначение обработчиков сигналов и событий.

Файл libugtk-widgets/ugtkmainwindow.c

 
void ugtk_mainwindow_init ( UGtkMainwindow *window )
{
GtkWidget *vbox;
UGtkMainwindowPriv *priv = UGTK_MAINWINDOW_PRIVATE(window);
 
window->priv = priv;
 
 . . .
 
/* Создание бокса вертикального размещения виджетов */
window->vbox = GTK_WIDGET(gtk_vbox_new ( FALSE, 0 ));
gtk_container_add ( GTK_CONTAINER( window ), window->vbox );
 
 . . .
 
}

Макрос UGTK_MAINWINDOW_PRIVATE() определен тут же в файле libugtk-widgets/ugtkmainwindow.c и позволяет получать доступ к приватным данным указанного типа. В данном случае, приватные данные описаны структурным типом UGtkMainwindowPriv, а ниже паказано создание бокса вертикального размещения виджетов и включение его в структуру окна.

   
 

5.4. Использование методов объекта

 
 

5.4.1. Метод ugtk_mainwindow_set_title()

 
 

Изменяет свойство объекта "mainwindow-title" и устанавливаливает новый заголовок "Главному окну". Для этого используется g_object_set() — функция С/GObject.

Файл libugtk-widgets/ugtkmainwindow.c

 
gboolean ugtk_mainwindow_set_title ( UGtkMainwindow *self, gchar *fmt, ... )
{
va_list   ap;
gchar     *title = NULL;
 . . .
g_object_set ( G_OBJECT(self), "mainwindow-title", title, NULL );
 . . .
return TRUE;
}
   
 

5.4.2. Метод ugtk_mainwindow_get_title()

 

Возвращает текущий заголовок "Главного окна". Для этого используется g_object_get() — функция С/GObject.

Файл libugtk-widgets/ugtkmainwindow.c

gchar* ugtk_mainwindow_get_title ( UGtkMainwindow *self )
{
gchar   *title;
 
 . . .
 
g_object_get ( G_OBJECT(self), "mainwindow-title", &title, NULL );
 
return title;
}
   
 

5.4.3. Метод ugtk_mainwindow_get_hostname()

 
 

Возвращает имя машины (hostname) или имя сетевого узла, на котором была запущена программа ugtkmainwindow. Для этого используется таже самая функция С/GObject, что и в методе ugtk_mainwindow_get_title(). (код не приводится, реализация подобна, описанной в п.5.4.1. )

   
 

5.4.4. Метод ugtk_mainwindow_set_hostname()

 
 

В настоящее время не определен. Его использование планируется в последующих версиях ugtkmainwindow. (код не приводится, в связи с отсутствием реализации)

   
 

5.4.5. Метод ugtk_mainwindow_widget_append()

 
 

Добавляет виджеты в бокс вертикального размещения виджетов GtkVBox, являющегося публичным свойством и определенного в структурном типе UGtkMainwindow. Поэтому, пользователь статической библиотеки libugtk-widgets может сделать свою реализацию манипуляцией виджатами в боксе вертикального размещения GtkVBox.

gboolean ugtk_mainwindow_widget_append (
UGtkMainwindow *self,
GtkWidget *widget,
gint space_left,
gint space_top )
{
GtkWidget*    hbox;
 
 . . .
 
hbox = GTK_WIDGET(gtk_hbox_new ( FALSE, 0 ));
gtk_box_pack_start ( GTK_BOX(hbox), widget, TRUE, TRUE, space_left );
gtk_box_pack_start ( GTK_BOX(self->vbox), hbox, TRUE, TRUE, space_top );
 
 . . .
 
gtk_widget_show_all(self->vbox);
 
 . . .
 
return TRUE;
}
   
 

6. Библиография

 
 

6.1. GObject Reference Manual
6.2. Introduction to GObject
6.3. The Official GNOME 2 Developer's Guide
6.4. GtkWindow
6.5. How to define and implement a new GObject

   
   
Вернуться к Оглавлению

 Copyright © 2010-2014 rjaan as Andrey Rjavskov(Rzhavskov) <rjaan@yandex.ru> <arjavskov@gmail.com>
 
Сайт создан в системе uCoz