GTK+ в примерах (GtkVPaned и GtkHPaned)

Модератор: Модераторы разделов

Аватара пользователя
sergeyvp
Сообщения: 807
ОС: ubuntu

GTK+ в примерах

Сообщение sergeyvp »

Пример создания рабочего пространства программы с произвольным количеством фреймов.

Код:

#include <gtk/gtk.h> // Структурный тип для создания рабочего пространства typedef struct { GtkWidget *parent; GtkWidget *frame; gint mode; }SWorkspace; SWorkspace *workspace; // Функция формирующая рабочее пространство void get_workspace_frame( SWorkspace *workspace); // Функция формирующая содержимое рабочего пространства GtkWidget* new_frame(); // Функция удаления дочернего виджета из панельного виджета // (эта функция присутствует в GTK+ версии 2.10.13, но отсутсвует в более ранних версиях библиотеки) void gtk_paned_remove (GtkContainer *container, GtkWidget *widget) { GtkPaned *paned; gboolean was_visible; paned = GTK_PANED (container); was_visible = GTK_WIDGET_VISIBLE (widget); if (paned->child1 == widget) { gtk_widget_unparent (widget); paned->child1 = NULL; if (was_visible && GTK_WIDGET_VISIBLE (container)) gtk_widget_queue_resize (GTK_WIDGET (container)); } else if (paned->child2 == widget) { gtk_widget_unparent (widget); paned->child2 = NULL; if (was_visible && GTK_WIDGET_VISIBLE (container)) gtk_widget_queue_resize (GTK_WIDGET (container)); } } // Функция переключения переменных рабочего пространства void set_frame_workspace( GtkWidget *frame) { workspace->frame = frame; workspace->parent = frame->parent; } // Рекурсивная функция удаления рабочего пространства // если родительский панельный виджет удаляемого фрейма оказывается пустым // функция вызывается повторно с родительским виджетом в качестве // параметра указывающего виджет для удаления void widget_destroy( GtkWidget *widget) { GtkPaned *paned; if( GTK_IS_PANED( widget->parent)) { paned = GTK_PANED( widget->parent); gtk_paned_remove( GTK_CONTAINER( widget->parent), widget); if(GTK_IS_WIDGET( widget)) { gtk_widget_destroy( widget); } if( paned->child1 == NULL && paned->child2 == NULL) { widget_destroy( (GtkWidget*)paned); } } } // Функция удаления содержимого рабочего пространства void widget_clear( GtkWidget *widget) { if(GTK_IS_WIDGET( widget)) { gtk_widget_destroy( widget); } } // Функция устанавливающая режим деления по вертикали void set_workspace_mode1( SWorkspace *workspace) { workspace->mode = 1; get_workspace_frame( workspace); } // Функция устанавливающая режим деления по горизонтали void set_workspace_mode2( SWorkspace *workspace) { workspace->mode = 2; get_workspace_frame( workspace); } int main(int argc, char* argv[]) { gtk_init(&argc, &argv); GtkWidget *window; // Выделяем память для переменных рабочего пространства workspace = g_malloc( sizeof(SWorkspace)); // создаем основное окно программы window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // устанавливаем начальный размер основного окна gtk_window_set_default_size(GTK_WINDOW(window), 400, 400); // устанавливаем позицию окна при запуске программы gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // определяем заголовок основного окна gtk_window_set_title(GTK_WINDOW(window), "GtkPaned example"); // подключаем сигнал выхода из программы при нажатии кнопки закрытия окна g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); // Определяем начальные переменные рабочего пространства workspace->parent = window; workspace->mode = 0; get_workspace_frame( workspace); // Отображаем всё содержимое окна gtk_widget_show_all( window); gtk_main(); return 0; } GtkWidget* new_frame() { // Ярлык имитирующий содержимое рабочего пространства GtkWidget *label = gtk_label_new("FRAME"); // Основная рамка рабочего пространства GtkWidget *frame = gtk_frame_new( NULL); gtk_frame_set_shadow_type( GTK_FRAME( frame), GTK_SHADOW_IN); // Изображение для кнопки закрыть GtkWidget *image_close = gtk_image_new_from_stock( GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); // Кнопка закрыть GtkWidget *button_close = gtk_button_new(); gtk_button_set_relief( (GtkButton*)button_close, GTK_RELIEF_NONE); gtk_button_set_image( (GtkButton*)button_close, image_close); // Подключаем функцию рекурсивного удаления рабочего пространства g_signal_connect_swapped( G_OBJECT( button_close), "clicked", G_CALLBACK( widget_destroy), frame); // Изображение для кнопки очиcтки рабочего пространства GtkWidget *image_clear = gtk_image_new_from_stock( GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU); // Кнопка очистки рабочего пространства GtkWidget *button_clear = gtk_button_new(); gtk_button_set_relief( (GtkButton*)button_clear, GTK_RELIEF_NONE); gtk_button_set_image( (GtkButton*)button_clear, image_clear); // Подключаем функцию очистки рабочего пространства g_signal_connect_swapped( G_OBJECT( button_clear), "clicked", G_CALLBACK( widget_clear), label); // Изображение для кнопки вставки рабочего пространства в лево GtkWidget *image_left = gtk_image_new_from_stock( GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU); // Кнопка добавляющая деление в левую часть существующего виджета GtkWidget *button_left = gtk_button_new(); gtk_button_set_relief( (GtkButton*)button_left, GTK_RELIEF_NONE); gtk_button_set_image( (GtkButton*)button_left, image_left); // Подключаем функции переключения переменных рабочего пространства g_signal_connect_swapped( G_OBJECT( button_left), "enter", G_CALLBACK( set_frame_workspace), frame); g_signal_connect_swapped( G_OBJECT( button_left), "button-press-event", G_CALLBACK( set_workspace_mode2), workspace); // Изображение для кнопки вставки рабочего пространства снизу GtkWidget *image_bottom = gtk_image_new_from_stock( GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU); // Кнопка добавляющая деление снизу GtkWidget *button_bottom = gtk_button_new(); gtk_button_set_relief( (GtkButton*)button_bottom, GTK_RELIEF_NONE); gtk_button_set_image( (GtkButton*)button_bottom, image_bottom); // Подключаем функции переключения переменных рабочего пространства g_signal_connect_swapped( G_OBJECT( button_bottom), "enter", G_CALLBACK( set_frame_workspace), frame); g_signal_connect_swapped( G_OBJECT( button_bottom), "button-press-event", G_CALLBACK( set_workspace_mode1), workspace); // Контейнер для кнопок GtkWidget *buttonbox = gtk_hbox_new( FALSE, 0); gtk_box_pack_end( (GtkBox*)buttonbox, button_close, FALSE, FALSE, 0); gtk_box_pack_end( (GtkBox*)buttonbox, button_clear, FALSE, FALSE, 0); gtk_box_pack_start( (GtkBox*)buttonbox, button_bottom, FALSE, FALSE, 0); gtk_box_pack_start( (GtkBox*)buttonbox, button_left, FALSE, FALSE, 0); // Вертикальный контейнер GtkWidget *vbox = gtk_vbox_new( FALSE, 0); gtk_box_pack_start( (GtkBox*)vbox, buttonbox, FALSE, FALSE, 0); gtk_box_pack_start( (GtkBox*)vbox, label, TRUE, FALSE, 0); gtk_container_add( GTK_CONTAINER( frame), vbox); return frame; } void get_workspace_frame( SWorkspace *workspace) { // Вертикальная панель деления GtkWidget *vpaned = gtk_vpaned_new(); // Горизонтальная панель деления GtkWidget *hpaned = gtk_hpaned_new(); // Создаём содержимое фрейма GtkWidget *frame = new_frame(); if( workspace->mode > 0) { if( workspace->mode == 1) { // Вертикальное деление // Создаём ссылку на фрейм для удаления из родительского контейнера gtk_widget_ref( workspace->frame); // Если родитель - панельный виджет if( workspace->frame->parent == vpaned) { // Удаляем фрейм из родителя для последующей перепаковки gtk_paned_remove( GTK_CONTAINER( workspace->frame->parent), workspace->frame); // Упаковываем в родительский виджет новый вертикальный панельный виджет gtk_paned_pack1( GTK_PANED( workspace->parent), vpaned, FALSE, FALSE); } else { // Иначе удаляем фрейм из родительского контейнера gtk_container_remove( GTK_CONTAINER( workspace->frame->parent), workspace->frame); // Добавляем в родительский контейнер панельный виджет вертикального деления gtk_container_add( GTK_CONTAINER( workspace->parent), vpaned); } // Упаковываем во вновь созданный панельный виджет предыдущий frame и новый gtk_paned_pack1( GTK_PANED( vpaned), workspace->frame, FALSE, FALSE); gtk_paned_pack2( GTK_PANED( vpaned), frame, FALSE, FALSE); // Определяем нового родителя для фрейма workspace->parent = frame->parent; } else if( workspace->mode == 2) { // Горизонтальное деление // Создаём ссылку на фрейм для удаления из родительского контейнера gtk_widget_ref( workspace->frame); // Если родитель - панельный виджет if( workspace->frame->parent == hpaned) { // Удаляем фрейм из родителя для последующей перепаковки gtk_paned_remove( GTK_CONTAINER( workspace->frame->parent), workspace->frame); // Упаковываем в родительский виджет новый горизонтальный панельный виджет gtk_paned_pack1( GTK_PANED( workspace->parent), hpaned, FALSE, FALSE); } else { // Иначе удаляем фрейм из родительского контейнера gtk_container_remove( GTK_CONTAINER( workspace->frame->parent), workspace->frame); // Добавляем в родительский контейнер панельный виджет горизоньального деления gtk_container_add( GTK_CONTAINER( workspace->parent), hpaned); } // Упаковываем во вновь созданный панельный виджет предыдущий и новый фреймы gtk_paned_pack1( GTK_PANED( hpaned), workspace->frame, FALSE, FALSE); gtk_paned_pack2( GTK_PANED( hpaned), frame, FALSE, FALSE); // Определяем нового родителя для фрейма workspace->parent = frame->parent; } } else { // Если режим равен 0 - занчит рабочее пространство не имеет деления // просто добавляем фрейм в родительский контейнер gtk_container_add( GTK_CONTAINER( workspace->parent), frame); // Определяем нового родителя для фрейма workspace->frame = frame; } gtk_widget_show_all( workspace->parent); }


Код можно скомпилировать командой
gcc `pkg-config --cflags --libs gtk+-2.0` panedexample.c -o panedexample
при условии что в вашей системе установлено всё необходимое, а код находится в файле panedexample.c
Подробности тут:
http://linfoline.homedns.org/gtk-2.10-refe.../GtkHPaned.html
http://linfoline.homedns.org/gtk-2.10-refe.../GtkVPaned.html
http://linfoline.homedns.org/gtk-2.10-refe...u/GtkPaned.html

P/S Данный пример содержит несколько ошибок, правильный вариант можно будет увидеть в программе TFX :)
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Спасибо сказали:
d_n_k
Сообщения: 636
ОС: Gentoo GNU/Linux

Re: GTK+ в примерах

Сообщение d_n_k »

Код: Выделить всё

if( GTK_WIDGET_TYPE( widget->parent) != 134690360 &&
GTK_WIDGET_TYPE( widget->parent) != 134690352)

плохой пример
все сказанное есть имхо...
Спасибо сказали:
d_n_k
Сообщения: 636
ОС: Gentoo GNU/Linux

Re: GTK+ в примерах

Сообщение d_n_k »

лучше

Код: Выделить всё

if ( GTK_IS_PANED(widget->parent) )
...
все сказанное есть имхо...
Спасибо сказали:
Аватара пользователя
sergeyvp
Сообщения: 807
ОС: ubuntu

Re: GTK+ в примерах

Сообщение sergeyvp »

Спасибо что заметил :)
Спасибо сказали:
Аватара пользователя
sergeyvp
Сообщения: 807
ОС: ubuntu

Re: GTK+ в примерах

Сообщение sergeyvp »

Немного подправил пример :)
Убрал лишние обработчики сигналов и добавил коментарии.
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: GTK+ в примерах

Сообщение Zeus »

И снова я:
как лучше организовать прорисовку окна программы из рабочего потока?

Суть задачи (всё тот же осциллограф): в программе два потока - gtk_main и рабочий поток, читающий файл устройства.
Когда есть что нарисовать - рабочий поток забивает std::vector значениями и кидает сигнал основному потоку
g_signal_emit_by_name (drawing_area, "expose_event", 0, &intValue);

Но как-то неудовлетворительно это всё работает:
1. Иногда прорисовка просто "замирает" - т.е. окно вообще не обновляется. Через десяток другой секунд - отмирает.
2. Иногда прорисовка сигнала будто не заканчивается. Т.е. он не доходит до границы области отображения. Но если переключиться в другое окно, а потом опять на осциллограф - то всё нормально.

Вопрос: как правильно посылать сигнал о перерисовке из рабочего потока в поток выборки сообщений окна?
Спасибо сказали:
Аватара пользователя
cherep36
Сообщения: 128
Статус: Йа пробудилсо!
ОС: Arch Linux

Re: GTK+ в примерах

Сообщение cherep36 »

Я нашёл в этой проге баг, на 40-ом клике на стрелке в право пропадает заголовок окна :D
p.s. Как сделать чтоб она сомкнулась? :D :D
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Спасибо сказали:
Аватара пользователя
sergeyvp
Сообщения: 807
ОС: ubuntu

Re: GTK+ в примерах

Сообщение sergeyvp »

Zeus писал(а):
14.08.2007 15:41
Но как-то неудовлетворительно это всё работает:
Вопрос: как правильно посылать сигнал о перерисовке из рабочего потока в поток выборки сообщений окна?

Я к сожалению плохо разбираюсь в многопоточных приложениях, так что ничем помочь не смогу :)
Может на форуме есть гуру, ау-у-у-у отзовитесь :)

cherep36 писал(а):
14.08.2007 16:26
Я нашёл в этой проге баг, на 40-ом клике на стрелке в право пропадает заголовок окна
p.s. Как сделать чтоб она сомкнулась?

Это баг не проги а берила, так что все вопросы туда :)
Спасибо сказали:
d_n_k
Сообщения: 636
ОС: Gentoo GNU/Linux

Re: GTK+ в примерах

Сообщение d_n_k »

все сказанное есть имхо...
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: GTK+ в примерах

Сообщение Zeus »

d_n_k, спасибо. Возможно это мой случай - завтра попробую.
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: GTK+ в примерах

Сообщение Zeus »

Похоже помогло.
Часик осциллограф крутится: не падает, прорисовка не "виснет", на воздействия реагирует.

Кроме этих функций для многопоточного программирования на Gtk+ особо помогла gdk_flush();
Без неё прорисовка была какая-то неполноценная.
Спасибо сказали: