LinuxForum.Fans
Тема

GTK+ пример программы на чистом СИ с графическим интерфейсом

Автор: sklimkin 32 сообщений 39457 просмотров 📌 закреплена
29.08.2011 22:27 · отредактировано #188180

0. В качестве преамбулы
Видимая легкость классового подхода в ООП меня не устраивает. Да и программы, которые я намерен делать не столь грандиозны, чтобы переходить к созданию своих классов. А в качестве "несвоих" меня устраивают уже имеющиеся, грамотно встроенные в систему вместе с библиотеками GTK+ (тоже системными по сути) классы окон, виджетов и прочее. То что нормально воспринимают компилятор gcc и иже с ним отладчик и линкер. Обсуждать и размазывать сопли типа ООП -- неООП категорически не намерен - для этого уже предостаточно базаров на самых разных форумах, от "домохозяек" до "жрецов высшего посвящения в nix".

1. Среда создания программ

Большинство инструментов связано с объектно-ориентированным программированием и поэтому рассматривается, как правило C++. Сред разработки для C++ в том числе с API более чем ...
Для программ с использованим в качестве основы так называемого plain-C (а ведь Unix-Linux созданы и расширяются именно на СИ без ООП) выбор инструментов невелик (но и не мал): Code::Blocks IDE, CodeLite, Anjuta, Glade [FIXME]

2. Справочные документы, программы

Devhelp - замечательная справочная система для GTK+ 2.x (и не только), конкурирующая по емкости с небезизвестной MSDN. В составе Devhelp (помимо многого другого) есть: GLib Reference Manual, GTK+ Reference Manual, GObject Reference Manual, GDK Reference Manual, GTK+ 2.0 Tutorial, Anjuta Developers Reference Manual, Glade User Interface Designer Reference Manual, GTK+ FAQ и даже примеры программ. Devhelp содержит описание всех элементов GTK+. К слову сказать Code::Blocks имеет интеграцию с Devhelp, что позволяет из среды программирования просматривать описания и конструкции различных элементов GTK+

3. Примеры программирования
В сети можно найти немало примеров-программ написанных на "чистом" СИ. Но подавляющее число из них это консольный и окннный варианты вывода строки "Hello World!". Встречаются примеры, создающие окно из 2-3-4 элементов (меню, или инструментальная панель, текстовая строка label, кнопка button, строка редактируемого текста text_entry). Но меню имеет, как правило 3 пункта - File-open, Exit и About - то есть по ним вызываются стандартные встроенные диалоговые окна GTK+, а кнопка чаще всего дублирует системную [x]. Что делать дальше, отталкиваясь от таких примеров? Как например правильно создать и наполнить содержанием элемент управления combo-box? А затем указать программе методы взаимодействия других элементов и функций с элементом combo-box?
Нет в примерах (или я не нашел (?)) даже намеков на создание программы с двумя, тремя и более окнами ПОЛЬЗОВАТЕЛЯ. И опять же, как эти окна управляются программой в работе и взаимодействуют между собой? Как создать программу, которая может немного больше, чем проблямкать "Всем привет!", но без панели меню, или инструментальной панели (тяжеловесны они при видимой легкости создания - подозреваю я)?
Еще раз подчеркну: видимая легкость классового подхода в ООП меня не устраивает, поэтому смотри 4.

4. Программа-пример gtk4 (GTK+ more windows without menu-bar)
На ОС Linux (ubuntu) я перешел совсем недавно, точнее сказать перехожу. В ОС Windows я немного программирую на СИ используя для этого Pelles-C (plain C). Естественно возник вопрос: что есть для Линукс аналогичное уже освоенному инструментарию? В результате выбраны: редактор gedit, Code::Blocks IDE и справочная система Devhelp.
Сначала почитал http://linuxforum.ru/viewtopic.php?id=8328&p=2 и http://linuxforum.ru/viewtopic.php?id=2061
Затем написал вопрос http://linuxforum.ru/viewtopic.php?id=15759
И начал таки писать строчку за строчкой. Первая программа - консольная для работы через COM-port (RS-232) с приемником/навигатором Garmin по его бинарному протоколу написана в редакторе gedit.
Вопросы по окошкам и главное по их взаимодействию друг-с-другом задавать на форуме уже не отваживаюсь.
При этом начинаю понимать важность в nix такого компонента системы как terminal. Но, поскольку до уровня просветленных жрецов nix я не дорос (да и не стремлюсь к этому), то программа работающая ТОЛЬКО через командную стоку терминала не совсем отвечает поставленной задаче. А в задаче то, что требует представления данных в пресловутых "окошечках" с "кнопочками". Произошел переход в Code::Blocks IDE (не смог одолеть Анюту по причине очень скудной документации и все тех же примеров с окончанием на "Hello вам всем").
Программа gtk4 написана полностью "врукопашную" для того, чтобы понять, как создаются и взаимодействуют элемены графического интерфейса. Чтобы понять, как уйти от некоторых "стандартов" окна, которые мне не нужны в данной программе.

Программа создает окна:
main - главное окно с кнопками, заменяющими меню (конечное назначение программы - наладонник, а там иконки меню слишком мелкие и можно промахнуться), а также со строкой статус-бар;
files - встроенное диалоговое окно для открытия файла, куда же без него;
window1 - 2 кнопки (одна закрывает окно, другая - программу), 2 метки label, строка текста с пользовательскими цветами фона и текста, строка статус-бара в которую выводится содержание строки текста;
window2 - то же что и window1 с некоторыми вариациями;
sample dialog - стандартное диалоговое окно message-box с программным включением в него виджета чек-бокс;
sky - повторение sample dialog, но добавлены еще label и combo-box с наполнением в 9 строк;
about - стандартный диалог "ебаут" - до кучи (хотя и в него программист может много чего напихать, если надо), кнопка "ЗАКРЫТЬ" почему-то не работает, только крестик [x].
В функциях для sample dialog и about предусмотрены вызовы других функций программы. В текст исходника вставил, сколько мог, комментарии на кривом аглицком, уж не обессудьте.

Выкладываю программу и ее исходник для тех, кто может-быть также ищет решения для "чистого/голого" СИ и пока не нашел его. Если будут вопросы типа "а как мне ...", то скорее всего ответить не смогу, бо сам только начал осваивать, если только местные "жрецы" сподобятся.

"...как советовать, так все чатлане..." (к/ф Кин-Дза-Дза).

Проект в Code::Blocks целиком, главное в нем файл main.c (порадовал меня размер исполняемого файла 17.6 кБ release-версия).
Файл gtk4.tar.gz номер: 5516514, размер: 55 Кб, размещён: 2011-08-29 21:55
ссылка на файл: http://webfile.ru/5516514
Описание: GTK+ plain-C programming
Пароль: gtk

Специально разложил все 7 окошек программы на рабочем столе
Файл gtk4.jpg номер: 5516531, размер: 121 Кб, размещён: 2011-08-29 22:00
ссылка на файл: http://webfile.ru/5516531
Описание: gtk4_screenshots

Добавление 19 янв. 2012г.
------------------------------
Прошу прощения, файл по предыдущему адресу давно затерт.
Вот новый адрес хранения, надеюсь более долговечный:
http://www.mediafire.com/?ah0h1mtg9rcj549
файл gtk4.tar.gz - 123 Кб, внутри файла лежит также скриншот.

29.08.2011 22:31 #188182
sklimkin написал:
выбор инструментов невелик (но и не мал): Code::Blocks IDE, CodeLite, Anjuta, Glade [FIXME]

vim, emax
//fixed

30.08.2011 12:53 #188315

sklimkin, молодец)) прикрепил.

31.08.2011 22:22 #188798

Для начинающих я бы порекомендовал найти книгу Andrew Krause "Foundations of GTK+ Development ", в ней все понятно объяснено. Также много интересного по gtk+ и не только можно найти на: http://www.gtkforums.com/viewtopic.php?t=988 и http://live.gnome.org/GnomeLove.
не понял смысла в топике обсуждения ООП - неООП, ведь GTK+ реализован в стиле ООП и для gui ООП как раз и подходит больше всего.
И да я тоже предпочитаю vim :), для которого даже syntax файл есть для gtk+

01.09.2011 00:40 #188824

Для начинающих я бы порекомендовал найти ...

За ссылки спасибо, посмотрю.

не понял смысла в топике ...

См. 0. В качестве преамбулы

И да я тоже предпочитаю vim ...

Анекдот из советского прошлого:
В паспортный стол обратился гражданин с просьбой сменить его неблагозвучную фамилию (Семижопов) на более приличную.
Собрал и сдал все справки.
Велели подождпть 3 месяца.
Приходит через 3 месяца за новой фамилией.
Начальник паспортного стола протягивает ему новый паспорт и радостно сообщает:
"Поздравляю Вас товарищ Семижопов, теперь Ваша фамилия Пятижопов!"

09.09.2011 20:13 #191142

В развитие темы.
Язык программы СИ (без плюсов без диезов) и GTK+ для окошечек-кнопочек, среда CODE::Blocks IDE - есть некоторые удобства по сравнению с gedit.

"Может вам она как кляча,
А мне так просто в самый раз.
Я на Вачу ехал плача,
А возвращаюсь веселясь!"
("Про речку Вачу и попутчицу Валю" В.С. Высоцкий)

Проект еще совсем "сырой".
Поэтому 1. кнопка "FILES", 2. кнопка "DIALOG2", все остальное пока развлекуха.
Цель проекта: работа с GPS приемниками и навигаторами на чипсетах SirfStar по бинарному протоколу Garmin и SirfStar.
Сейчас программа способна соединиться с Гарминами и записать получаемый от них через RS-232 бинарный поток.
Модуль работы с портом, в виде консольной программы, получился достаточно быстро.
Затем попробовал "нарисовать" окна и кнопки, тоже получилось.
Дать пользователю выбор места для сохранения выходных файлов программы - работает.
Присоединил к окнам модуль comport - вызывается и работает, НО!!!
Чтение из порта (в цикле по 1-секундному таймеру) должно продолжаться до завершения программы пользователем.
А выполнить это посредством какой-либо кнопки в каком-либо окне программы я не могу, потому что не знаю, как выйти из функции comport() к этим самым кнопкам в окошках. При этом надо оставить цикл в comport() работающим.
Кроме того, после запуска цикла пользователь должен иметь доступ к окошкам-кнопкам для просмотра информации работающей программы: текущие координаты, количество захваченных спутников, уровни их сигналов и т.п.

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

Я уже пробовал применить нити-threads, но безуспешно.
Возможно решение гораздо проще - в использовании системных сигналов signals, но я их пока не понимаю.
Удивительно, но спрашивать помощи на русскоязычных линукс-форумах, КАК ПРАВИЛО, это потеря времени и нервных клеток.

Поэтому: если вдруг объявится такой человек, который знает и может не "через губу" и некосноязычно показать правильное решение (а для этого надо посмотреть исходник - ссылка в конце поста), то пусть он просто ткнет пальцем в то место программы, где НЕПРАВИЛЬНО и тут же напишет как ПРАВИЛЬНО. Всем прочим с толстыми книжками, man'ами и и другими доберманами просьба не беспокоиться понапрасну и не опускаться с высот ООП на такой низкий уровень (имею в виду тему в заголовке), а то так и до ассемблера недолго.

Комментарии в исходниках на очень сломанном аглицком - упражнения для возможного диалога с пиндосами и дань моде. А то бы я все, вплоть до имен переменных и фунькций, прописал на родном великом и могучем.
К исходникам приложу 2 файла, которые пишет программа и terminal-log.txt, из которого видно что и как отрабатывается.
В частности, что pthread создается и даже что-то делает: 10 раз через 3 сек. в ст.вывод вкрапляется строка "I'm still running!!!"
Раскройте спойлер:

Показать
sklimkin@sklimkin-desktop:~/projects/CodeBlocks/Tmp1/bin/Debug$ ./Tmp1
Selected folder: /home/sklimkin/projects/CodeBlocks/Tmp1/bin/Debug
thread 'Main' exist and is running
Function comport() in thread is running...

WARNING! Press Ctrl+C to stop program!
16 bytes written to COM-port
Selected folder: /home/sklimkin/projects/CodeBlocks/Tmp1/bin/Debug/fileout.bin

Bytes readed 1905
Bytes readed 1905, found Garmin Message 54

2 Garmin commands is written!
I'm still running!!!
1432 bytes written in file
990 bytes written in file
1000 bytes written in file
I'm still running!!!
992 bytes written in file
992 bytes written in file
983 bytes written in file
I'm still running!!!
992 bytes written in file
1008 bytes written in file
992 bytes written in file
I'm still running!!!
999 bytes written in file
990 bytes written in file
I'm still running!!!
1000 bytes written in file
992 bytes written in file
991 bytes written in file
I'm still running!!!
993 bytes written in file
974 bytes written in file
985 bytes written in file
I'm still running!!!
983 bytes written in file
985 bytes written in file
984 bytes written in file
I'm still running!!!
984 bytes written in file
984 bytes written in file
979 bytes written in file
I'm still running!!!
979 bytes written in file
986 bytes written in file
992 bytes written in file
I'm still running!!!
999 bytes written in file
995 bytes written in file
998 bytes written in file
991 bytes written in file
1000 bytes written in file
983 bytes written in file
984 bytes written in file
992 bytes written in file
992 bytes written in file
982 bytes written in file
1000 bytes written in file
993 bytes written in file
991 bytes written in file
987 bytes written in file
991 bytes written in file
1008 bytes written in file
988 bytes written in file
991 bytes written in file
993 bytes written in file
991 bytes written in file
992 bytes written in file
986 bytes written in file
Убито
sklimkin@sklimkin-desktop:~/projects/CodeBlocks/Tmp1/bin/Debug$

Из него можно увидеть, что завершение бесконечного цикла с портом достигается варварским методом по иконке крестик [x] - и УБИТО!

Вот ссылка на исходник
http://webfile.ru/5537520

08.10.2011 20:39 · отредактировано #198475

Всем доброго времени суток!

немного оффтопа:

Показать
Пытаюсь осилить абсолютно аналогичную задачу - создание приложения GTK+, общающегося через последовательный порт со внешним устройством (gprs-модемом). Понятно, что полно готового софта, но задача стоит чисто спортивная/академическая - расшариться в программировании приложений.

Опыта в данной области исчезающе мало - с позавчерашнего вечера занялся :) , и до сих пор в линуксе строчил только bash-скрипты для бытовых нужд. Я уже более трех лет пользуюсь дома и на работе только линём: инет, офис, фильмы, файлокачка на антресолях и т.п.. Нет, не религиозные убеждения-заблуждения! Просто так мне объективно удобнее. И вот недавно почувтствовал потребность в получении новых навыков.
Есть богатый опыт работы с разными микроконтроллерами (си, ассемблер), но тут этот опыт мало чем помогает. Так что подключаюсь к вашей интересной теме, если никто не против :)
Запуск модема и последовательного порта средствами линукс прошли сравнительно быстро и гладко (в первый же вечер :) ), вчера занялся знакомством с приемами создания приложений GTK+, в процессе чего возникли те же самые непонятки, что и в начальном сообщении: как заставить приложение делать хоть что-нибудь, кроме обработки главного цикла gtk_main().

Чисто интуитивно возникла несколько вариантов:

1. Вместо "вечного" главного цикла gtk_main() использовать его итерационный/однотактный вариант - gtk_main_iteration(), чтобы можно было чередовать обработку графической мордочки с обработкой пользовательского кода.

2. Можно попробовать:

  • функцию таймаута, которая должна вызывать обработчик каждые N-миллисекунд,
  • функцию простоя, которая бдует вызывать обработчик при бездействии главного цикла,
  • функцию ожидания ввода, которая ждет готовности дескриптора файла для чтения или записи и вызывает обработчик.

Собственно сам пользовательский код можно втиснуть в эти обработчики.

3. Каким-то образом загнать gtk_main() в параллельный поток, чтобы мордочка обрабатывалась отдельно от остального пользовательского кода; тут я совсем слаб, но мне кажется, что надо копать в сторону нитей (thread).

Подозреваю также, что существуют и другие, возможно более изящные варианты.


Вдогонку.

По первому варианту вот что пишут (тыкаем Next и читаем про все функции): http://www.opennet.ru/docs/RUS/gtk/node71.html

09.10.2011 12:22 · отредактировано #198529

Думаю, для работы с последовательным портом вполне можно использовать функцию таймаута.
До запуска главного цикла gtk_main() нужно добавить в очередь пользовательскую callback-функцию, в которой мы будем читать данные из порта, скажем, пару раз в секунду, разбираться с принятой информацией и решать, что с ней делать дальше. После запуска главного цикла, наша функция будет вызываться через заданные интервалы времени. Если захотим заглушить ее (прекратить вызов из главного цикла GTK+), то можно из этой функции вернуть результат FALSE вместо TRUE, или вызвать gtk_timeout_remove (разумеется, не из самой отменяемой функции).

Поигрался вчера с распространенным "хелоуворлдом" на GTK+ с двумя кнопочками: "Выход" и "Кнопка2", пристроил туда свою callback-функцию и добавил функцию таймаута в очередь до запуска главного цикла GTK+:

text
static gboolean timeout_callback(gpointer data)
{
    g_print("Timeout callback\n");
    return TRUE; //Если вернуть FALSE, то данная функция снимается с очереди
}
text
g_timeout_add(500, timeout_callback, NULL);

Допиленный пример хелоуворлда:

Показать
text
#include <gtk/gtk.h>
//#include <gtk/gtkmain.h>

static void button_clicked(GtkWidget * widget, gpointer data) { gint i = * (gint *) data; g_print("Button #%i is pressed!\n", i); } static gboolean delete_event(GtkWidget * widget, GdkEvent * event, gpointer data) { g_print("Delete event occurred\n"); return FALSE; } static void destroy(GtkWidget * widget, gpointer data) { g_print("Destroy signal sent\n"); gtk_main_quit(); }

//---------------------------------------------------------------- static gboolean timeout_callback(gpointer data) { g_print("Timeout callback\n"); return TRUE; //Если вернуть FALSE, то данная функция снимается с очереди } //----------------------------------------------------------------

int main(int argc, char ** argv) { GtkWidget * window; GtkWidget * button1; GtkWidget * button2; GtkWidget * box; gint i1, i2; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Buttons"); gtk_container_set_border_width(GTK_CONTAINER(window), 10); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);

g_timeout_add(500, timeout_callback, NULL);

box = gtk_hbox_new(TRUE, 2); gtk_container_add(GTK_CONTAINER(window), box); button1 = gtk_button_new_with_label("Выход"); i1 = 1; g_signal_connect(G_OBJECT(button1), "clicked", G_CALLBACK(button_clicked), &i1); g_signal_connect_swapped(G_OBJECT(button1), "clicked", G_CALLBACK(gtk_widget_destroy), G_OBJECT(window)); gtk_box_pack_start(GTK_BOX(box), button1, TRUE, TRUE, 0); button2 = gtk_button_new_with_label("Кнопка 2"); i2 = 2; g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button_clicked), &i2); gtk_box_pack_start(GTK_BOX(box), button2, TRUE, TRUE, 0); gtk_widget_show(button1); gtk_widget_show(button2); gtk_widget_show(box); gtk_widget_show(window); gtk_main(); return 0; }

Выхлоп в консоль
(строчка "Timeout callback" выдается каждые полсекунды; "Button #2 is pressed!" выдается, если тыкать Кнопку2):[console]mike@desktop:~/proj/gtk_hello$ ./helloworld
Timeout callback
Timeout callback
Timeout callback
Timeout callback
Timeout callback
Timeout callback
Button #2 is pressed!
Button #2 is pressed!
Button #2 is pressed!
Timeout callback
Button #2 is pressed!
Button #2 is pressed!
Timeout callback
Timeout callback
Timeout callback
Button #1 is pressed!
Destroy signal sent
mike@desktop:~/proj/gtk_hello$ [/console]

09.10.2011 17:42 #198580

К слову, вот еще хорошая, годная статья по GtkBuilder:
http://www.ibm.com/developerworks/ru/library/l-GTKbuilder/

Если я правильно понимаю, в GTK+ интегрировано собственное API (GtkBuilder) для взаимодействия с Glade взамен устаревшей libglade.
Ковыряясь в однообразных и урезанных примерах, так сразу и не разберешься :)

11.10.2011 02:46 · отредактировано #198830

Вот спасибо, мил человек, что откликнулся!
И даже нащупал решение.
Я тоже немного доработал свой пример и немного погодя его выложу.

Просто в поисках оптимального для голого СИ решения я нашел fd-xforms.
https://savannah.nongnu.org/projects/xforms
[url]ftp://lists.gnu.org/xforms-development/[/url]
Пакет работает с Иксами (не с GTK) имеет свою библиотеку для форм-кнопочек.
Позволяет "рисовать" формы-окна и выдает на-гора готовый сишный код в 3-х файлах, которые легко компилятся.

И я несколько отвлекся, увлекся разработкой программы в этом xforms.
К тому же пакет поддерживается одним немцем из Берлина, который пока консультирует меня.
Он даже выпускает обновления пакета.
У его группы есть площадка для обмена письмами. И это очень удобно.

Таймауты в GTK+ обязательно опробую и сообщу результат.

Сейчас попробую выложить здесь картинку того, что наваял в xforms.



Добавление 22 янв. 2012г.
------------------------------
Прошу прощения, файл по предыдущему адресу давно затерт.
Вот новый адрес хранения, надеюсь более долговечный:
http://www.mediafire.com/download.php?2mp1prwtmnwsz6q
файл surveyfd-12.zip - 167 Кб, внутри файла лежит также скриншот.
Это одна из первых весий освоения xforms - многого пока нет.

11.10.2011 03:14 · отредактировано #198834

Выкладываю немного доработанный проект в Code:Blocks целиком.
Скомпилированный исполняемый файл в /bin/debug/gnssmonitor1
Там же "продукты жизнедеятельности" программы.

Если есть какой-нибудь Garmin-навигатор, переключите его в Garmin-Garmin (binary) протокол в настройках и подключите кабелем к serial-port.
В программе надо зайти в окно "FILES" и выбрать директорию для записи файлов, а затем в окно "SURVEYING" и нажать кнопку "OK".
Если навигатор подключен и в бинарном режиме, то начнется чтение-запись. Для остановки нажать кнопку "ОТМЕНА".
Если это окно сдвинуть то увидите под ним Main-окно. Пока все.

Вот где пример:
http://webfile.ru/5596249

Добавление 19 янв. 2012г.
------------------------------
Прошу прощения, файл по предыдущему адресу давно затерт.
Вот новый адрес хранения, надеюсь более долговечный:
http://www.mediafire.com/?ah0h1mtg9rcj549
файл gtk4.tar.gz - 123 Кб, внутри файла лежит также скриншот.

11.10.2011 03:51 #198836

Если я правильно понимаю, в GTK+ интегрировано собственное API (GtkBuilder) для взаимодействия с Glade взамен устаревшей libglade.

не совсеи так. GTK+ это набор библиотек (богатый). А Glade - это IDE среда для "рисования" окошек-кнопок.
Glade позволяет набросать в окно его элементы и сохранить это в файле xxx.glade - это UI (UserInterface).
Он в xml-формате и принимается-понимается такими пакетами как Anjuta или Code:Blocks, где собственно и наполняется кодом программа.

Мне не нравится xml, но это стандарт де-факто. Поэтому я и искал нечто, где все пишется на СИ (без плюсов).
Вот Вам для сравнения 2 файла: в одном интерфейс (и код функций) на СИ, в другом только интерфейс на xml.

http://webfile.ru/5596260

11.10.2011 11:24 · отредактировано #198884
sklimkin написал:
Вот спасибо, мил человек, что откликнулся!

"Делай добро и бросай его в воду" (с)

sklimkin написал:
Пакет работает с Иксами (не с GTK) имеет свою библиотеку для форм-кнопочек.
Позволяет "рисовать" формы-окна и выдает на-гора готовый сишный код в 3-х файлах, которые легко компилятся.

Я нечто подобное искал, когда хотел замутить человеко-машинный интерфейс на одноплатном компе с ТФТ-шкой и тачскрином, но я хотел совсем без иксов, например, на фреймбуфере (DirectFB или что-то в этом духе). Но все потуги так и остались в прошлом. Не до того было :)

sklimkin написал:
Если есть какой-нибудь Garmin-навигатор

Чего нет, того нет :/

sklimkin написал:
не совсеи так. GTK+ это набор библиотек (богатый). А Glade - это IDE среда для "рисования" окошек-кнопок.

Видимо, я не вполне точно выразился :) Я не имел в виду ничего подобного. Я понимаю, что Глэйд лишь генерирует xml, а его уже надо скармливать либо libglade-у (отдельная устаревшая библиотечка), либо GtkBuilder-у - встроенное API внутри самой GTK+. Я толковал только про два способа использования xml-ных файлов (на первых порах для меня не было очевидным что делать с xml-ом).

sklimkin написал:
Мне не нравится xml, но это стандарт де-факто. Поэтому я и искал нечто, где все пишется на СИ (без плюсов).

Ну, мне повезло, что меня совершенно не заботит наличие в итоговой софтине XML-файлика на пару десятков килобайт (а вообще, его содержимое можно втиснуть в строковую переменную в теле самого Си-шного кода и передать GtkBuilder-у через соответствующу функцию). Зато появляется возможность оперативной замены графического интерфейса простой заменой внешнего файлика, да и богатство выбора виджетов я бы не стал оставлять без винмания. С Xforms я не знаком, но если там выбор виджетов меньше, то мое предпочтение будет отдано однозначно GTK+.

11.10.2011 16:08 #198930

Mike53

а вообще, его содержимое можно втиснуть в строковую переменную в теле самого Си-шного кода и передать GtkBuilder-у через соответствующу функцию

Если найдете время, сделайте развитие Вашего примера до 2-х окон. Пусть это будут glade и GtkBuilder с СИ-шным файлом и Makefile для него.
И я пойму как работать с xml и другим надеюсь будет полезно/интересно.
Раз уж модератор вынес эту тему в "важняки". :P

12.10.2011 14:29 · отредактировано #199122
sklimkin написал:
Если найдете время, сделайте развитие Вашего примера до 2-х окон. Пусть это будут glade и GtkBuilder с СИ-шным файлом и Makefile для него.

Вчера времени не было (готовлюсь к отъезду), а сегодня выдалась минутка и я набросал примерчик с двумя окнами; заодно и сам попрактиковался :)
Сам код (main.c):

Показать
text
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>

// ОБРАТИТЕ ВНИМАНИЕ на типы указателей! GtkBuilder *builder; //Тут необходимо объявить указатели на структуры (объекты) GtkWidget *window1; // Названия придумываем призвольные, т.к. псевдонимы указателей GtkWidget *window2; // актуальны только внутри нашего кода и не от чего больше не зависят; GtkLabel *label_win1; // т.е. вместо window1 вполне можно было указать okno_pervoe

/* * Далее объявляем фукнции, имена которых должны СТРОГО соответствовать * сигналам из проекта Glade (в свойствах каждого виджета на закладке "Сигналы" * необходимо указать эти самые имена сигналов; например, у кнопки 1 в разделе * GtkButton в раскрывающемся списке надо отыскать событие clicked и в этой строче * в графу "обработчик" вписать любое имя или выбрать предложенное и скопировать * это имя СЮДА (в данный си-шный код) в строчку объявления функции-обработчика (callback) * * В свойствах окна можно найти сигнал в разделе GtkObject - destroy * С этим сигналом ДЛЯ ГЛАВНОГО ОКНА необходимо связать обработчик, который позволил бы * уничтожать главное окно. * * С каждым сигналом в функцию обработчик можно передавать пользовательские данные, но этим, * по всей видимости, мало кто пользуется * */

void on_window1_destroy (GtkObject *object, gpointer user_data) { // тут можно что-либо поделать перед тем, как закрыть окно; напрмер проверить сохранены ли все файлы // и, в случае необходимости выкатить дочернее окошко с предупреждением gtk_main_quit(); }

void on_button1_clicked (GtkObject *object, gpointer user_data) { gtk_label_set_text (label_win1, "Кнопка1 нажата"); }

void on_button2_clicked (GtkObject *object, gpointer user_data) { gtk_label_set_text (label_win1, "А теперь Кнопка2"); }

void on_button3_clicked (GtkObject *object, gpointer user_data) { gtk_widget_show_all (window2); }

void on_button4_clicked (GtkObject *object, gpointer user_data) { gtk_widget_hide_all (window2); }

// Это та самая функция, которая вызывается по таймауту из главного цикла GTK+, // когда билдер уже отработал и был запущен этот самый цикл static gboolean timeout_callback(gpointer data) { g_print("Timeout callback\n"); return TRUE; //Если вернуть FALSE, то данная функция снимается с очереди }

/* * Тут лежит основной код */

int main (int argc, char *argv[]) {

gtk_init (&argc, &argv); //Любое приложение GTK+ обязательно должно инициализировать библиотеку GTK+

builder = gtk_builder_new (); //инициализируем указатель на структуру билдера gtk_builder_add_from_file (builder, "glade02.glade", NULL); //загружаем в нее данные из проекта xml (теперь GTK+ будет знать всю структуру проекта из Glade)

gtk_builder_connect_signals (builder, NULL); //соединяем сигналы проекта с нашим кодом

window1 = GTK_WIDGET (gtk_builder_get_object (builder, "window1")); //инициализируем указатели на виджеты, window2 = GTK_WIDGET (gtk_builder_get_object (builder, "window2")); //которые планируется использовать в нашем коде (кроме тех, label_win1 = GTK_LABEL (gtk_builder_get_object (builder, "label1"));// которые и так уже связаны сигналами с функциями-обработчиками сигналов) g_object_unref (G_OBJECT (builder)); //Мавр сделал свое дело - Освобождаем занимаемую им память. Далее пользуемся GTK-шными функциями как обычно

gtk_widget_show_all (window1); //Вот и указатель на виджет пригодился - вызываем функцию отображащую виджет - первое (главное) окно. Можно показать сразу и второе окно. // В очередь обработки главного цикла добавляем вызов функции-обрабочика ДО ТОГО как будет вызван сам главный цикл g_timeout_add(500, timeout_callback, NULL);

gtk_main (); //Алес! Теперь GTK+ владеет структурой виджетов (окна, кнопки, где что расположено, что можно двигать/сжимать), //главное окно построено на экране, функция таймаута сидит в очереди, можно запускать ГЛАВНЫЙ ЦИКЛ.

return 0; }

Проект из Glade (glade02.glade) - в свойствах дочернего окна я указал свойство "модальное" - пока оно не будет закрыто, нельзя обратиться к другим окнам данного приложения:

Показать
text
<?xml version="1.0"?>
<interface>
  <requires lib="gtk+" version="2.16"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="window1">
    <property name="resizable">False</property>
    <property name="window_position">center</property>
    <signal name="destroy" handler="on_window1_destroy"/>
    <child>
      <object class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="label" translatable="yes">label</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="padding">10</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkHButtonBox" id="hbuttonbox1">
            <property name="visible">True</property>
            <property name="border_width">5</property>
            <property name="orientation">vertical</property>
            <property name="layout_style">end</property>
            <child>
              <object class="GtkButton" id="button1">
                <property name="label" translatable="yes">button_1</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_button1_clicked"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button2">
                <property name="label" translatable="yes">button_2</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_button2_clicked"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button3">
                <property name="label" translatable="yes">&#x412;&#x44B;&#x437;&#x43E;&#x432; &#x434;&#x43E;&#x447;&#x435;&#x440;&#x43D;&#x435;&#x433;&#x43E; &#x43E;&#x43A;&#x43D;&#x430;</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_button3_clicked"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">2</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkWindow" id="window2">
    <property name="resizable">False</property>
    <property name="modal">True</property>
    <property name="window_position">center-on-parent</property>
    <child>
      <object class="GtkVBox" id="vbox2">
        <property name="visible">True</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel" id="label2">
            <property name="visible">True</property>
            <property name="label" translatable="yes">&#x414;&#x43E;&#x447;&#x435;&#x440;&#x43D;&#x435;&#x435; &#x43E;&#x43A;&#x43D;&#x43E;</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="padding">10</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button4">
            <property name="label">gtk-close</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <signal name="clicked" handler="on_button4_clicked"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="padding">5</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

и мэйк-файл (Makefile):

text
# Makefile

all: main.c gcc -Wall -export-dynamic -g main.c -o helloGtkBuilder `pkg-config --cflags --libs gtk+-2.0`

12.10.2011 14:48 · отредактировано #199126

Что-то я никак в толк не возьму, как тут файлы к сообщениям прикреплять :o

По теме накину, пожалуй, несколько хороших ссылок:
http://symmetrica.net/gtk/
http://www.ibm.com/developerworks/ru/library/l-GTKbuilder/ - Очень хорошая статейка по GtkBuilder-у. За основу моего примера был взят код из этой статьи.
http://www.opennet.ru/docs/RUS/gtk-reference/gtk-reference-prog.html.gz#GtkListStore_html - Справочное руководство по GTK+ (по-русски)
http://habrahabr.ru/blogs/programming/116268/ - Превосходная статья по работе в Glade с двумя очень важными, на мой взгляд, виджетами: GtkTreeView и GtkListStore, а также использование их в своем коде. Правда, сам автор проявляет излишнее стремление к Си++, но после вышеприведенных примеров, разобраться с его кодом и использовать в своем Си-шном - проблем не составляет. Вот один из коментариев:

Я насчитал только три вещи из C++, которые здесь используются:
extern "C"

static_cast<>

false (тип bool)
Сомнительная мотивация только из-за них использовать C++.

В остальном больше вопросов нет. И я бы с радостью почитал статью про gtkmm.
12.10.2011 14:57 #199130

Кстати, в статье с Хабры присутствует весьма интересный момент. При редактировании проекта в Glade, для элементов виджета недоступты некоторые свойства, которые могут использоваться в GTK+. В частности, там врукопашную редактируется свойство цвета текста, тогда как в Glade в выпадающем списке такого свойства нет. После ручного редактирования проект можно открыть в Glade и это свойство там будет доступно :)

12.10.2011 17:02 #199156

To Mime53:

(готовлюсь к отъезду)

АмерЫка, Эсраэль, КНР, Индустан ?

Что-то я никак в толк не возьму, как тут файлы к сообщениям прикреплять

А никак. Только размещать на бесплатных файлообменниках.

По теме накину, пожалуй, несколько хороших ссылок:

Вот за это спасибо!

Может быть этого еще нет:

Показать
"The Linux Programming Interface"
http://depositfiles.com/files/fdzhtnib4

в свойствах дочернего окна я указал свойство "модальное"

Мотивация? (Это window2 ?)

Комментарии на 5+

После ручного редактирования проект можно открыть в Glade и это свойство там будет доступно

Ценное указание, может съэкономить нервы и время.
Реп. +1

14.10.2011 02:52 · отредактировано #199537

Привет Mike53, куда пропал/переехал ?

Я поковырялся с Glade и Вашим примером и мне это понравилось.
Не удержался и самовольно кое-что доделал.
1. дочернее окно window2 сделал немодальным и вот почему:
2. добавил 2-й таймер в 1 сек., который вкл.-выкл. в дочернем окне
3. добавил переключение между окнами show <--> hide
4. включение 1-го таймера в 0.5 сек. перенес из старта программы (main.c) на кнопку в окне window1
5. ну и немного по мелочам.

Все это конечно непричесанное, но порадовала возможность легко и без pthread создавать/убирать
таймеры на таймаутах, которые (и в которые) можно "запихать" все, что потребуется для программы.
Еще раз СПАСИБО за подсказку!

Вот как выглядит включалка/выключалка 1-го таймера:

Показать
text

/* Switch Timer 1 */
void on_button5_clicked (GtkObject *object, gpointer user_data)
{
    //printf("blnTimeout1State = %d\n", (int)blnTimeout1State);
    //printf("blnTimeout2State = %d\n", (int)blnTimeout2State);
    //printf("blnTimeout1Work = %d\n", (int)blnTimeout1Work);
    blnTimeout1State = TRUE;  // Timer 1 can Start in future

if(blnTimeout2State == TRUE) // First Stop Timer 2 { blnTimeout2State = FALSE; blnTimeout2State = timeout2_callback(user_data); } if(blnTimeout1Work == FALSE) // Timer 1 NOT worked { // Now Start Timer 1 g_timeout_add(500, timeout1_callback, NULL); blnTimeout1Work = TRUE; // Timer 1 IS worked gtk_button_set_label(object, "STOP TimeOut1"); g_print("Now START TimeOut1\n"); } else // Timer 1 IS worked { // Now Stop Timer 1 blnTimeout1State = FALSE; // to Stop Timer 1 g_timeout_add(500, timeout1_callback, NULL); blnTimeout1Work = FALSE; // Timer 1 NOT worked gtk_button_set_label(object, "START TimeOut1"); g_print("Now STOP TimeOut1\n"); } }

Вот картинка работающей программы:
http://webfile.ru/5603267
Файлы проекта положил здесь:
http://webfile.ru/5603231

Добавление 21 янв. 2012г.
------------------------------
Прошу прощения, файлы по предыдущему адресу давно затерты.
Вот новый адрес хранения, надеюсь более долговечный:
http://www.mediafire.com/download.php?paqdkv8ryfuft18
Вот скриншот работающей программы (оба окна в терминале):
http://www.mediafire.com/i/?qs11r111ntrvk9v

23.10.2011 13:34 #200473

Я тоже рад приветствовать, sklimkin!

sklimkin написал:
Привет Mike53, куда пропал/переехал ?

см. PM

Вроде бы основная проблема старта решена, понятно "как можно делать" и теперь остается только расшариваться в самой GTK+. Но меня волнует еще вопрос целесообразности применения тредов (в смысле разделения программы на несколько параллельных нитей/потоков, в одном из которых будет графический интерфейс программы, а в другом(других) основной алгоритм). Каковы "накладные расходы" системы в случае переключения между процессами (сохранение контекста и т.п.) и в случае обслуживания очереди в главном цикле gtk_main()? Т.е. хотелось бы понять "как правильно делать".

Для своих текущих задач я решил использовать вызов пользовательских функций из главного цикла GTK+. Насколько это хорошо/правильно будет видно в процессе использования :)

Войдите или зарегистрируйтесь, чтобы ответить.