Данный пост посвящен созданию приложений с использованием кроссплатформенной библиотеки GTK+. Ориентирован он в основном на новичков? поэтому какие-то вещи возможно для многих покажутся очень простыми и банальными, но я постарался максимально подробнее всё описать, чтобы было понятно для всех.
Немалым достоинством этой библиотеки является то, что она бесплатна для коммерческого использования. В интернете не так много документации и действительно качественных статей по работе с GTK+. В очень многих примерах интерфейс программы пишется «ручками», что порой очень неудобно. Я сам с этим столкнулся и довольно часто больше времени тратил на то, чтобы правильно расположить виджеты (объекты) на форме, в контейнеры, а не сосредоточиться на решении поставленной задачи.
Я продемонстрирую как можно очень быстро создать интерфейс для GTK+ с помощью приложения для визуального создания графических интерфейсов Glade и интегрировать его в вашу программу. Glade не является ни компилятором, ни отладчиком. Он позволяет лишь описать интерфейс и представить его в файлах XML-формата GladeXML.
Базовым интерфейсом для библиотеки GTK+ является язык C. Но я в данном примере буду ипользовать C++. Соответственно появятся небольшие особенности, о которых я обязательно расскажу. Дистрибутив Linux я использую Ubuntu 10.04.
Задача будет такая: написать небольшое приложение состоящее из холста, бокового меню (выбора того, что нарисовать на холсте), главного меню и строки состояния. При этом боковое меню и холст при изменении размера всего окна приложения должны оставаться постоянного размера. Но тот контейнер, в котором находится холст может изменять свой размер в зависмости от увеличения/уменьшения размеров главного окна. И при необходимости должны появляться полосы прокрутки.
План будет такой:
Предполагается, что у Вас уже установлен dev-пакет GTK+, если нет, то это можно сделать через консоль:
Установить Glade можно через Ubuntu Update Center или снова воспользовавшись консолью:
После установки запускаем через Applications — Programming — Glade Interface Designer
Устанавливаем все параметры как показано на рисунке. Версию инструментария (Toolkit Version) выбираем 2.16

Слева вы можете увидеть окно с набором различных виджетов. Те, кто работал с Windows Forms, или в среде разрабоки Delphi быстро разберутся. Справо два меню: одно показывает иерархию виджетов, а второе окно — окно свойств виджетов.
Создание графического интерфейса для вашего GTK+ приложения начинается с задания главной формы — заглавного окна программы. Находим в левом меню в разделе TopLevels виджет Window.
Справа в окне Properties задаём свойства этого виджета:
Name: topWindow
Windows position: Centre
Default Width: 500
Default Height: 320

Переходим во вкладку Signals (сигнал, событие) и GtkObject ищем destroy, в столбце Handler (обработчик) пишем topWindow_destroy_cb или выбираем из списка. Это будет названием процедуры которая будет обрабатывать сигнал destroy — закрытие главной формы.

Далее мы планируем, чтобы в нашем приложении было главное меню, основная функциональная часть (это холст и меню выбора того. что нарисовать) и строка состояния. Берём виджет-контейнер Vertical Box в левом меню в разделе Containers и кладём его нашу главную форму. В появившемя диалоге выбираем Number of Items: 3. Это означает, что в создаваемый нами контейнер можно будет положить 3 виджета.
У нас в контейнере vbox1 есть 3, так называемых ячейки, или отс��ка. Во второй (по центру) отсек кладём Frame (раздел Containers меню виджетов). Смотрим справа на дерево виджетов. Внутри созданного виджета frame1 будут два виждета: alignment1 и label1 — их можно удалить. На этом пока немного отклонимся и обратимся к теории.
Понимание того, как в GTK+ происходит управление размером виджетов, является очень важными для создания правильных и эффективных графических интерфейсов. Этот процесс часто называют процессом согласования, состоящего из двух составляющих (2 этапа): запрашиваемый размер и выделяемый размер.
Этап запроса представляет собой некий рекурсивный вопрос: «Сколько места тебе нужно?». Главное окно задаёт его дочерним окнам, дочерние окна задают его своим дочерним окнам и так пока все виджеты, во всей этой иерархии не ответят.
Тут есть 2 важных момента:
Теперь, когда главное окно знает сколько места нужно в идеале, принимается решение о том сколько места будет фактически доступно. Если на запрос пришло некоторое разумное значение — оно и является используемым по умолчанию. Но если размер окна устанавливается в ручную (например, используя gtk_window_setdefault_size) или как-то иначе, окно отбрасывает запрошенный размер и устанавливает другой.
На втором этапе (выделение размера) происходит команда от родителя к дочернему виджету: «Здесь у тебя есть некоторое место, сделай всё, чтобы уместиться в него». Если у виджета более одного дочернего виджета, то задача этого этапа должным образом разделить свободное место между дочерними виджетами.
Теперь переходим к главной части нашего приложения. Идём в меню виджетов и выбираем Horizontal Panes в разделе Containers и кладём его на frame1. Нам нужно чтобы наша левая часть была фиксированного размера, правая нет. Снова идём в меню виджетов и ищем там Scrolled Window. И кладём сначала один, а потом и второй Scrolled Window в левый и правый отсек hpand1 (Horizontal Panes). У левого Scrolled Window, который называется scrolledwindow1, устанавливаем свойство (во вкладке Common)Width reguеst равным 145 — это и будет означать запрашиваемый размер (для процесса согласования). Так же следует проверить, чтобы во вкладке Packing свойство Resize у scrolledwindow1 было No, а у scrolledwindow2 Yes.
Из раздела Containers кладём сначала первый виджет ViewPort, а потом и второй на оба наших scrolledwindow1 и scrolledwindow2 соответственно.

Займёмся боковым нашим меню. Добавляем во ViewPort1 Vertical Box с 3 отсеками. И в каждый отсек добавляем Radio Button. У каждого Radio Button устанавливаем свойство Expand No, чтобы они не растягивались при увеличении размера родительского виджета. Также во вкладке Common можно задать Width и Height request тогда они будут размера такого какой мы укажем, а не по умолчанию. Но в любом случае изменяться в размере они будут. Изменим их свойства во вкладке General:
Name соответственно на rbutRectangle, rbutEllipse, rbutTriangle
Label соответственно на Прямоугольник, Эллипс, Треугольник
и для rbutEllipse и rbutTriangle укажем в свойстве Group rbutRectangle с помощью специального диалога.

Должно получиться вот так:

Теперь необходимо прописать события для каждого Radio Button. Во вкладке Signals без разницы либо для события GroupChanged, либо для toggled (я сделаю для него) пропишем обработчик rbutton_toggled_cb:

Теперь обратим к правой части нашего окна. Добавляем в ViewPort2 вертикальный контейнер Vertical Box с двумя отсеками. В верхний кладём обычный Label, а в нижний Drawing Area — это будет холст. У Label1 в свойстве label пишем Холст размером 300 x 200. Переходим во вкладку Packing и устанавливаем Expand в No.
Теперь наш холст.
Name: drawingarea
Expend: No
Width request: 300
Height request: 200
Event: ставим галочку Exposure
В Signals ищем expose-event и пишем или выбираем из списка:
drawingarea_expose_event_cb

Теперь нам нужно добавить строку состояния (в самый нижний отсек) и главное меню (в самый верхний отсек). В самый верхний кладём Menu Bar (раздел Containers меню виджетов), В самый нижний отсек Status Bar (раздел Control and Display меню виджетов), про его добавлении появиться диалог, выберете Number of Items: 1.

В menubar1 для нашего примера оставим только один пункт меню — выход из программы. Соответственно можно удалить: menuitem2, menuitem3, menuitem4. Потом раскрываем menuitem1, заходим в menu1 и удаляем так всё кроме imagemenuitem5. Это пункт Quit. Можно нажать на неё и посмотреть свойство во вкладке General окна свойств свойство Stock Item, в котором прописано gtk-quit. Там можно «поиграться». Можно вообще выбрать Custom label and image и самому всё задать. Это не так важно. Для imagemenuitem5 во вкладке Signals устанавливаем для сигнала activate (нажатие на пункт меню) точно такой же обработчик как и для закрытия формы: topWindow_destroy_cb.
Вот так должен выглядеть в итоге наш интерфейс. Сохраняем наш проект с именем mainForm. Файл будет называться mainForm.glade

Создадим файл Example1.cpp в той же папке куда сохранили mainForm.glade. Сначало я приведу целиком весь код, а потом объясню некоторые важные моменты.
Сначало мы объявляем те объекты. с которыми мы будем работать. Потом начинаем прописывать обработчики сигналов. Т.к. мы пишем на С++, пользуясь extern «C» объявляем topWindow_destroy_cb, drawingarea_expose_event_cb, rbutton_toggled_cb внешними функциями и указываем, что они должны связываться согласно порядку связывания в С. GtkBuilder содержит ссылки на все объекты из описания в Glade. Функция gtk_builder_get_object получает ссылку на объект с соответствующим именнем из GtkBuilder (в данном случае из builder). Функция gtk_builder_connect_signals служит для соединения функций-обработчиков в коде с прописанными соответственными обработчиками сигналов в нам GladeXML. Это происходит автоматически. Важно, чтобы имена в Glade и в коде совпадали. Приложение нормально скомпилируется, просто обрабатываться сигналы не будут, а если вы запустите через консоль программу, там будут отображены warning's, что невозможно найти обработчик для такого-то сигнала.
Так же, т.к. мы пишем на С++ очень часто приходится пользоваться явным приведением типов. Например
если бы мы писали на C, то можно было бы обойтись и без явного привидения GTK_RADIO_BUTTON.
Для того, чтобы откомпилировать Вашу программу нужно использовать либо автоматическую систему сборки (например CMake), либо самому прописать makefile. Я на этом подробно останавливаться не буду. А лишь приведу пример описания makefile. В конце поста две самые последнии ссылки как раз касаются создания make file.
Компиляция происходит командой:
Запускаем…

… а затем изменяем размер окна

Как мы видим боковое меню и холст не растянулись и остались в тех же размерах, а вот контейнер, в котором холст, размеры изменил. И причём всё свободное пространство, которое появилось при увеличении размера формы досталось ему. Это всё потому что мы у scrolledwindow1 установили Width reguеst = 145 и resize Yes. Вот пример появления полос прокрутки как главное окно стало меньших размеров

А вот теперь я зайду в Glade3 и изменю у label1 свойство Expand в Yes. Проект не нужно перекомпилировать. Достаточно просто сохранить mainForm файл. И вот результат:

Почему так? Холст у нас фиксированного размера. А всё пространство, которое получается при увеличении главного окна, отдаётся виджету label1. Вы можете «поиграться» со всем этим делом. Например, установив обратно Expand в No и указав точное значение Height, например 70. И тогда снова будет фиксированный размер, но уже не по умолчанию, а тот, который зададите вы.
На этом этот пост оканчивается. Если эта тема будет интересна, у меня есть желание писать еще посты в этом направлении.
Немного теории взято из Glade3 tutorial — Size negotiation.
Советую прочитать: Packing Widgets, The Cairo graphics tutorial, Makefiles by example, GNU `make'
Немалым достоинством этой библиотеки является то, что она бесплатна для коммерческого использования. В интернете не так много документации и действительно качественных статей по работе с GTK+. В очень многих примерах интерфейс программы пишется «ручками», что порой очень неудобно. Я сам с этим столкнулся и довольно часто больше времени тратил на то, чтобы правильно расположить виджеты (объекты) на форме, в контейнеры, а не сосредоточиться на решении поставленной задачи.
Я продемонстрирую как можно очень быстро создать интерфейс для GTK+ с помощью приложения для визуального создания графических интерфейсов Glade и интегрировать его в вашу программу. Glade не является ни компилятором, ни отладчиком. Он позволяет лишь описать интерфейс и представить его в файлах XML-формата GladeXML.
Базовым интерфейсом для библиотеки GTK+ является язык C. Но я в данном примере буду ипользовать C++. Соответственно появятся небольшие особенности, о которых я обязательно расскажу. Дистрибутив Linux я использую Ubuntu 10.04.
Задача будет такая: написать небольшое приложение состоящее из холста, бокового меню (выбора того, что нарисовать на холсте), главного меню и строки состояния. При этом боковое меню и холст при изменении размера всего окна приложения должны оставаться постоянного размера. Но тот контейнер, в котором находится холст может изменять свой размер в зависмости от увеличения/уменьшения размеров главного окна. И при необходимости должны появляться полосы прокрутки.
План будет такой:
- Установка и запуск Glade3
- Создадим графический интерфейс нашего приложения в Glade3, расположив основные виджеты (объекты) в главном окне и контейнерах, определив события (Singnals, сигналы) для виджетов и их обработчик (Handler), т.е. процедуру в коде нашей программы, которая будет вызываться при наступлении данного сигнала
- напишем код программы: интегрируем XML файл с графическим интерфейсом, пропишем обработчики и немного функционала для демонстрации
- немного потестируем наше приложение
Установка и запуск Glade3
Предполагается, что у Вас уже установлен dev-пакет GTK+, если нет, то это можно сделать через консоль:
sudo aptitude install libgtk2.0-devУстановить Glade можно через Ubuntu Update Center или снова воспользовавшись консолью:
sudo apt-get install glade-gnomeПосле установки запускаем через Applications — Programming — Glade Interface Designer
Устанавливаем все параметры как показано на рисунке. Версию инструментария (Toolkit Version) выбираем 2.16

Слева вы можете увидеть окно с набором различных виджетов. Те, кто работал с Windows Forms, или в среде разрабоки Delphi быстро разберутся. Справо два меню: одно показывает иерархию виджетов, а второе окно — окно свойств виджетов.
Создание графического интерфейса
Создание графического интерфейса для вашего GTK+ приложения начинается с задания главной формы — заглавного окна программы. Находим в левом меню в разделе TopLevels виджет Window.
Справа в окне Properties задаём свойства этого виджета:
Name: topWindow
Windows position: Centre
Default Width: 500
Default Height: 320

Переходим во вкладку Signals (сигнал, событие) и GtkObject ищем destroy, в столбце Handler (обработчик) пишем topWindow_destroy_cb или выбираем из списка. Это будет названием процедуры которая будет обрабатывать сигнал destroy — закрытие главной формы.

Далее мы планируем, чтобы в нашем приложении было главное меню, основная функциональная часть (это холст и меню выбора того. что нарисовать) и строка состояния. Берём виджет-контейнер Vertical Box в левом меню в разделе Containers и кладём его нашу главную форму. В появившемя диалоге выбираем Number of Items: 3. Это означает, что в создаваемый нами контейнер можно будет положить 3 виджета.
У нас в контейнере vbox1 есть 3, так называемых ячейки, или отс��ка. Во второй (по центру) отсек кладём Frame (раздел Containers меню виджетов). Смотрим справа на дерево виджетов. Внутри созданного виджета frame1 будут два виждета: alignment1 и label1 — их можно удалить. На этом пока немного отклонимся и обратимся к теории.
Понимание того, как в GTK+ происходит управление размером виджетов, является очень важными для создания правильных и эффективных графических интерфейсов. Этот процесс часто называют процессом согласования, состоящего из двух составляющих (2 этапа): запрашиваемый размер и выделяемый размер.
Этап запроса представляет собой некий рекурсивный вопрос: «Сколько места тебе нужно?». Главное окно задаёт его дочерним окнам, дочерние окна задают его своим дочерним окнам и так пока все виджеты, во всей этой иерархии не ответят.
Тут есть 2 важных момента:
- Дочерние виджеты ничего не знают о размерах виджетов-родителей
- Размер виджетов-родителей зависит от размеров дочерних виджетов
Теперь, когда главное окно знает сколько места нужно в идеале, принимается решение о том сколько места будет фактически доступно. Если на запрос пришло некоторое разумное значение — оно и является используемым по умолчанию. Но если размер окна устанавливается в ручную (например, используя gtk_window_setdefault_size) или как-то иначе, окно отбрасывает запрошенный размер и устанавливает другой.
На втором этапе (выделение размера) происходит команда от родителя к дочернему виджету: «Здесь у тебя есть некоторое место, сделай всё, чтобы уместиться в него». Если у виджета более одного дочернего виджета, то задача этого этапа должным образом разделить свободное место между дочерними виджетами.
Теперь переходим к главной части нашего приложения. Идём в меню виджетов и выбираем Horizontal Panes в разделе Containers и кладём его на frame1. Нам нужно чтобы наша левая часть была фиксированного размера, правая нет. Снова идём в меню виджетов и ищем там Scrolled Window. И кладём сначала один, а потом и второй Scrolled Window в левый и правый отсек hpand1 (Horizontal Panes). У левого Scrolled Window, который называется scrolledwindow1, устанавливаем свойство (во вкладке Common)Width reguеst равным 145 — это и будет означать запрашиваемый размер (для процесса согласования). Так же следует проверить, чтобы во вкладке Packing свойство Resize у scrolledwindow1 было No, а у scrolledwindow2 Yes.
Из раздела Containers кладём сначала первый виджет ViewPort, а потом и второй на оба наших scrolledwindow1 и scrolledwindow2 соответственно.

Займёмся боковым нашим меню. Добавляем во ViewPort1 Vertical Box с 3 отсеками. И в каждый отсек добавляем Radio Button. У каждого Radio Button устанавливаем свойство Expand No, чтобы они не растягивались при увеличении размера родительского виджета. Также во вкладке Common можно задать Width и Height request тогда они будут размера такого какой мы укажем, а не по умолчанию. Но в любом случае изменяться в размере они будут. Изменим их свойства во вкладке General:
Name соответственно на rbutRectangle, rbutEllipse, rbutTriangle
Label соответственно на Прямоугольник, Эллипс, Треугольник
и для rbutEllipse и rbutTriangle укажем в свойстве Group rbutRectangle с помощью специального диалога.

Должно получиться вот так:

Теперь необходимо прописать события для каждого Radio Button. Во вкладке Signals без разницы либо для события GroupChanged, либо для toggled (я сделаю для него) пропишем обработчик rbutton_toggled_cb:

Теперь обратим к правой части нашего окна. Добавляем в ViewPort2 вертикальный контейнер Vertical Box с двумя отсеками. В верхний кладём обычный Label, а в нижний Drawing Area — это будет холст. У Label1 в свойстве label пишем Холст размером 300 x 200. Переходим во вкладку Packing и устанавливаем Expand в No.
Теперь наш холст.
Name: drawingarea
Expend: No
Width request: 300
Height request: 200
Event: ставим галочку Exposure
В Signals ищем expose-event и пишем или выбираем из списка:
drawingarea_expose_event_cb

Теперь нам нужно добавить строку состояния (в самый нижний отсек) и главное меню (в самый верхний отсек). В самый верхний кладём Menu Bar (раздел Containers меню виджетов), В самый нижний отсек Status Bar (раздел Control and Display меню виджетов), про его добавлении появиться диалог, выберете Number of Items: 1.

В menubar1 для нашего примера оставим только один пункт меню — выход из программы. Соответственно можно удалить: menuitem2, menuitem3, menuitem4. Потом раскрываем menuitem1, заходим в menu1 и удаляем так всё кроме imagemenuitem5. Это пункт Quit. Можно нажать на неё и посмотреть свойство во вкладке General окна свойств свойство Stock Item, в котором прописано gtk-quit. Там можно «поиграться». Можно вообще выбрать Custom label and image и самому всё задать. Это не так важно. Для imagemenuitem5 во вкладке Signals устанавливаем для сигнала activate (нажатие на пункт меню) точно такой же обработчик как и для закрытия формы: topWindow_destroy_cb.
Вот так должен выглядеть в итоге наш интерфейс. Сохраняем наш проект с именем mainForm. Файл будет называться mainForm.glade

Код нашей программы
Создадим файл Example1.cpp в той же папке куда сохранили mainForm.glade. Сначало я приведу целиком весь код, а потом объясню некоторые важные моменты.
/* Example1.cpp */
#include <cairo.h>
#include <gtk/gtk.h>
#define UI_FILE "mainForm.glade"
// описание виджетов
GtkBuilder *builder;
GtkWidget *topWindow;
GtkRadioButton *rbutRectangle, *rbutEllipse, *rbutTriangle;
GtkDrawingArea *drawingarea;
// описание обработчиков сигналов
extern "C" void topWindow_destroy_cb (GtkObject *object, gpointer user_data);
extern "C" gboolean drawingarea_expose_event_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data);
extern "C" void rbutton_toggled_cb (GtkObject *object);
int main( int argc, char **argv )
{
GError *error = NULL;
// инициализация GTK+
gtk_init( &argc, &argv );
// создание нового GtkBuilder объекта
builder = gtk_builder_new();
// загрузка пользовательского интерфеса из файла, который мы создали в Glade
if( ! gtk_builder_add_from_file( builder, UI_FILE, &error ) )
{
g_warning( "%s", error->message );
g_free( error );
return( 1 );
}
// связывание наших виджетов с описаннимем виджетов в GladeXML
topWindow = GTK_WIDGET(gtk_builder_get_object(builder, "topWindow"));
rbutRectangle = GTK_RADIO_BUTTON(gtk_builder_get_object(builder, "rbutRectangle"));
rbutEllipse = GTK_RADIO_BUTTON(gtk_builder_get_object(builder, "rbutEllipse"));
rbutTriangle = GTK_RADIO_BUTTON(gtk_builder_get_object(builder, "rbutTriangle"));
drawingarea = GTK_DRAWING_AREA(gtk_builder_get_object(builder, "drawingarea"));
// связываем сигналы с объектами графического интерфейса
gtk_builder_connect_signals (builder, NULL);
// освобождение памяти
g_object_unref( G_OBJECT( builder ) );
// Показываем форму и виджеты на ней
gtk_widget_show( topWindow );
// запуск главного цикла приложения
gtk_main();
return( 0 );
}
// закрытие приложения
void topWindow_destroy_cb (GtkObject *object, gpointer user_data)
{
// завершаем главный цикл приложения
gtk_main_quit();
}
// перерисовка холста
gboolean drawingarea_expose_event_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
cairo_set_line_width (cr, 7);
cairo_set_source_rgb (cr, 0, 0, 0);
// переключатель установлен на прямоугольник
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rbutRectangle)))
{
// рисуем прямоугольник
cairo_rectangle (cr, 20, 20, 200, 100);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 0, 0.8, 0);
}
// переключатель установлен на эллипс
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rbutEllipse)))
{
// рисуем эллипс
cairo_arc(cr, 150, 100, 90, 0, 2 * 3.14);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 0.8, 0, 0);
}
// переключатель установлен на треугольник
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rbutTriangle)))
{
// рисуем треугольник
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
cairo_move_to (cr, 40, 40);
cairo_line_to (cr, 200, 40);
cairo_line_to (cr, 120, 160);
cairo_line_to (cr, 40, 40);
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 0.8, 0, 0.8);
}
cairo_fill(cr);
cairo_destroy(cr);
return FALSE;
}
void rbutton_toggled_cb (GtkObject *object)
{
// перерисовка drawingarea
gtk_widget_queue_draw (GTK_WIDGET(drawingarea));
}
Сначало мы объявляем те объекты. с которыми мы будем работать. Потом начинаем прописывать обработчики сигналов. Т.к. мы пишем на С++, пользуясь extern «C» объявляем topWindow_destroy_cb, drawingarea_expose_event_cb, rbutton_toggled_cb внешними функциями и указываем, что они должны связываться согласно порядку связывания в С. GtkBuilder содержит ссылки на все объекты из описания в Glade. Функция gtk_builder_get_object получает ссылку на объект с соответствующим именнем из GtkBuilder (в данном случае из builder). Функция gtk_builder_connect_signals служит для соединения функций-обработчиков в коде с прописанными соответственными обработчиками сигналов в нам GladeXML. Это происходит автоматически. Важно, чтобы имена в Glade и в коде совпадали. Приложение нормально скомпилируется, просто обрабатываться сигналы не будут, а если вы запустите через консоль программу, там будут отображены warning's, что невозможно найти обработчик для такого-то сигнала.
Так же, т.к. мы пишем на С++ очень часто приходится пользоваться явным приведением типов. Например
rbutRectangle = GTK_RADIO_BUTTON(gtk_builder_get_object(builder, "rbutRectangle"));
если бы мы писали на C, то можно было бы обойтись и без явного привидения GTK_RADIO_BUTTON.
Для того, чтобы откомпилировать Вашу программу нужно использовать либо автоматическую систему сборки (например CMake), либо самому прописать makefile. Я на этом подробно останавливаться не буду. А лишь приведу пример описания makefile. В конце поста две самые последнии ссылки как раз касаются создания make file.
CC=g++
LDLIBS=`pkg-config --libs gtk+-2.0 gmodule-2.0`
CFLAGS=-Wall -g `pkg-config --cflags gtk+-2.0 gmodule-2.0`
Example1: Example1.o
$(CC) $(LDLIBS) Example1.o -o Example1
Example1.o: Example1.cpp
$(CC) $(CFLAGS) -c Example1.cpp
clean:
rm -f Example1
rm -f *.o
Компиляция происходит командой:
makeНебольшое тестирование нашей программы
Запускаем…

… а затем изменяем размер окна

Как мы видим боковое меню и холст не растянулись и остались в тех же размерах, а вот контейнер, в котором холст, размеры изменил. И причём всё свободное пространство, которое появилось при увеличении размера формы досталось ему. Это всё потому что мы у scrolledwindow1 установили Width reguеst = 145 и resize Yes. Вот пример появления полос прокрутки как главное окно стало меньших размеров

А вот теперь я зайду в Glade3 и изменю у label1 свойство Expand в Yes. Проект не нужно перекомпилировать. Достаточно просто сохранить mainForm файл. И вот результат:

Почему так? Холст у нас фиксированного размера. А всё пространство, которое получается при увеличении главного окна, отдаётся виджету label1. Вы можете «поиграться» со всем этим делом. Например, установив обратно Expand в No и указав точное значение Height, например 70. И тогда снова будет фиксированный размер, но уже не по умолчанию, а тот, который зададите вы.
На этом этот пост оканчивается. Если эта тема будет интересна, у меня есть желание писать еще посты в этом направлении.
Немного теории взято из Glade3 tutorial — Size negotiation.
Советую прочитать: Packing Widgets, The Cairo graphics tutorial, Makefiles by example, GNU `make'