1 (19.01.2012 22:24:25 отредактировано sklimkin)

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 Кб, внутри файла лежит также скриншот.

2

sklimkin пишет:

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

vim, emax
//fixed

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

3

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

iMac 21.5, Asus U36JC, iPad 2, iPhone 4S

4

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

5

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

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

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

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

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

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

6

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

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

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

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

Я уже пробовал применить нити-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

7 (08.10.2011 21:13:42 отредактировано Mike53)

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

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

+ открыть спойлер

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

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

8 (09.10.2011 12:36:42 отредактировано Mike53)

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

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

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

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

+ открыть спойлер
#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):

 Консоль:
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$

9

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

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

10 (22.01.2012 20:47:20 отредактировано sklimkin)

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

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

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

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

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

http://webfile.ru/5596238

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

11 (20.01.2012 00:01:08 отредактировано sklimkin)

Выкладываю немного доработанный проект в 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 Кб, внутри файла лежит также скриншот.

12

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

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

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

http://webfile.ru/5596260

13 (11.10.2011 11:59:16 отредактировано Mike53)

sklimkin пишет:

Вот спасибо, мил человек, что откликнулся!

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

sklimkin пишет:

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

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

sklimkin пишет:

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

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

sklimkin пишет:

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

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

sklimkin пишет:

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

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

14

Mike53

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

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

15 (12.10.2011 15:02:33 отредактировано Mike53)

sklimkin пишет:

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

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

+ открыть спойлер
#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) - в свойствах дочернего окна я указал свойство "модальное" - пока оно не будет закрыто, нельзя обратиться к другим окнам данного приложения:

+ открыть спойлер
<?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):

# Makefile

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

16 (12.10.2011 14:51:13 отредактировано Mike53)

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

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

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

static_cast<>

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

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

17

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

18

To Mime53:

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

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

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

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

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

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

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

+ открыть спойлер

"The Linux Programming Interface"
http://depositfiles.com/files/fdzhtnib4

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

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

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

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

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

19 (30.01.2012 13:38:40 отредактировано sklimkin)

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

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

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

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

+ открыть спойлер
/* 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

20

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

sklimkin пишет:

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

см. PM

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

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

21

Как программировать рисование векторной графики в СИ на GTK+

Долгое время не мог "подступиться" к вопросу программирование РИСОВАНИЯ на СИ.
Речь не о том, как вставить в окно программы картинку/фото, или иконки - с этим как раз все просто.
Речь о "математическом" рисовании - линий, мультилиний, полигонов, дуг и т.п. - то есть о векторной графике.

Читал, искал примеры, вчитывался в справочную программу Devhelp, но не прояснялось.
Взялся за примеры из Devhelp под названием scribble (каракули).
Пример не компилировался по причине грубых ошибок в тексте программы.
Наконец нашел краткий и внятный пример-урок: Осциллограф, пример рисования в GTK2+ & Cairo
http://programmersforum.ru/showthread.php?t=59858
http://programmersforum.ru/showthread.php?t=59503

После него стал понятен пример из Devhelp scribble-simple.c который немного подправил и к рисованию отрезков прямых добавил рисование окружностей (эллипсов если быть точным) и прямоугольников. Хотя надо сказать, что пример все-равно очень убогий, хотя бы на фоне "Осциллографа".
scribble-simple.c - draw accumulated array of points on Cairo contexts, draw lines (+ circles & rectangles) from every point to every point.
http://cairographics.org/threaded_animation_with_cairo/ - GTK+ & Cairo - пример анимации.

А на основе примера "Осциллограф" написал то, что требовалось мне:
1. Исправленный/расширенный scribble-simple - gtkcairo
2. Немного переделанный/упрощенный "Осциллограф" - oscillator
3. Пример рисования в GTK2+ & Cairo "Небосвод - SKY" - sky
Переделывал исходные примеры, чтобы понять, как работает, ну и приближая к своим задачам.

Все примеры содержат по 3 файла: *.c  Makefile  и исполняемый бинарный.
Частенько в примерах на "рисование" окно не имеет никаких элементов управления - кнопок в частности.
В примере sky добавил 2 кнопки внизу окна - "HIDE WINDOW" и "REFRESH".
Кнопка "HIDE WINDOW" декоративная, она ничего не делает, потому что не имеет функции callback.
А кнопка "REFRESH" обновляет окно. Если подвигать окно по экрану и даже частично за пределы экрана, то на окне появляются "артефакты" - вот они и удаляются кнопкой "REFRESH", она связана с callback функцией button2_clicked, которая "обменивается" с функцией time_handler. Так принято в GTK.

Средствами ТОЛЬКО Cairo рисуется:
1 полусфера-небосвод,
2 главные оси,
3 окружности в 22 45 68 градусов на полусфере,
4 подграфка этих окружностей слева и снизу (корявенько, но показывает как выводить текст и цвет на картинку),
5 на небосвод выводится положение GPS-GLONASS спутников (квадратики с номерами спутников),
6 ниже небосвода рисуются столбики уровня сигналов от спутников,
7 ниже столбиков номера спутников,
8 а выше величины сигнал/шум в dB,
9 в самом низу 2 кнопки управления окном/программой.
10 Продемонстрирована заливка контуров цветом - зеленоватый для неба и коричневатый для уровня сигналов (чтобы не напрягало глаза).

Ссылка для скачивания:
http://www.mediafire.com/?3w34do3fmgbd1wu
(навязчивая реклама - расплата за бесплатный хостинг, выскакивает окно с поздравлением - Вы выиграли! - его надо закрыть).

Несколько ссылок по программированию в GTK+ на СИ:
нашел блог http://blog.borovsak.si/2009/04/ (Posted 29th April 2009 by Tadej Borovšak) - автор рассквзывает внятно и с примерами кода.
http://zetcode.com/tutorials/gtktutorial/ - хорошие небольшие примеры.
http://www.opennet.ru/docs/124.shtml - описания и примеры на русском языке.
http://www.opennet.ru/docs/RUS/gtk_plus … og.html.gz - преревод учебника на русский язык, один большой файл html.gz
http://symmetrica.net/uploads/gtk/lesson1.htm - Программирование графического интерфейса с помощью GTK+  2006-2007 Андрей Боровский <anb @ symmetrica.net>  - на мой взгляд самые внятные уроки на русском языке.

Тяжеловесность Linux-ubuntu (я работаю на версии lucid-lynx 10.04) и тенденции ubuntu к дальнейшему утяжелению за счет "свистелок-перделок" при не очень видимом движении в сторону "функционал + скорость" заставляют искать альтернативные решения. Меня очень заинтересовали сборки Линукса размером менее 1 ГБ, которые можно установить как на винчестер, так и на USB-flash в разделы форматированные под ext2--ext4 и FAT32. Это конечно отдельная и серьезная тема, но ...

Сейчас тестирую некоторые из них:
http://ru.wikipedia.org/wiki/Damn_Small_Linux
http://ru.wikipedia.org/wiki/Puppy_Linux и русские версии http://uco.puppyrus.org/
http://ru.wikipedia.org/wiki/Tiny_Core_Linux
http://ru.wikipedia.org/wiki/SliTaz
http://tuxologia.blogspot.com/2009/08/austrumi.html  http://zenway.ru/page/austrumi-xxx
http://www.cdlinux.info/wiki/doku.php/
и до кучи: http://wiki.kolibrios.org/wiki/Main_Page

Список "маленьких" Линуксов намного больше приведенных мной, но тенденция к уменьшению и оптимизации ОС прослеживается.

В процессе "рассмотрения" особенностей различных сборок Линуксов (Linux-Ubuntu, Arch-linux, PuppyLinux, TinyCoreLinux, Austrimi-linux, CDLinux ...) и после установки в них компонентов для программирования на СИ (gcc/g++ CodeBlocks Glade ...) можно обнаружись отсутствие некоторых важных компонентов/библиотек.

Например программа сделанная ранее в Glade не исполняется в новой конфигурации системы, хотя Glade и установлена.
Или CodeBlocks при попытке скомпилировать программу может сообщить об отсутствии /usr/include/gtk-2.0/gtk/gtk/h
При попытке выполнить на стороннем компьютере скомпилированную и собранную в бинарный файл маленькую программу, можно долго бродить по отсутствующим зависимостям и доустанавливать на этот компьютер много чего.
Эти обстоятельства заставляют задуматься над вопросом: минимальный и достаточный набор инструментов для программирования в любом Линуксе, вплоть до самых "tiny". Такой, чтобы зависимостей было как можно меньше.

Вот мой набор:
компилятор gcc/g++
доп. пакет разработчика gtk-dev(elopment)
отладчик gdb
make
cmake
текстовый редактор gedit, или geany (среда разработки и редактор)
файл-менеджер gnome-commander, или mc (midnight-commander)

далее не обязательно, но желательно:
glade-gnome (glade-3 glade)
codeblocks 8.04 (codeblocks 10.05)
file-roller или xarchiver
sqlite и sqlite3
hexedit или тяжеловесный (.net-framework), но богатый bless.

22

Полезный материал. Спасибо, что взяли на себя труд поделиться плодами собственных поисков!

23 (12.07.2012 02:23:52 отредактировано sklimkin)

Есть такой инструмент gtkdialog связывающий скриптование на bash с графическим окном на GTK
http://linux.pte.hu/~pipas/gtkdialog/ - автор Laszlo Pere.
Сайт http://code.google.com/p/gtkdialog/ давно (около года) не обновлялся.
Загружать программу (400 КБ) надо с него.

Может быть здесь будет по теме.
gtkdialog имеет много хороших примеров bash-script.
Автор Laszlo Pere предоставил возможность делать программы и на языке СИ, но пример такой программы всего один (и очень маленький).
Возможности программы gtkdialog в этом случае значительно беднее, чем у bash-script.
Некоторые пользователи могут писать на bash-script хуже, чем на СИ (как я например).
Учитывая это я сделал пример на языке СИ.

Конструкции gtkdialog для СИ не позволяют например такие вещи:
<window window_position="1" title="System Tools">
или:
<action when="file-activated">echo File: $FILE </action>
Это удалось обойти таким способом:
"<action>cat [ echo $FILE ] | zenity --text-info  --width=720 --height=400 --title $FILE</action>"
Но даже с такими ограничениями мне было интересно сделать программу, которая не требует тяжелых библиотек (так я во всяком случае думаю).

Вот как выглядит самый простой пример с двумя кнопками в окне:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

main(){
    FILE *stream;
    char line[256];
    
    putenv(
"MAIN_DIALOG=<hbox>"
"  <button ok></button>"
"  <button cancel></button>"
"</hbox>");
    
    stream = popen("gtkdialog --program=MAIN_DIALOG", "r");
    fgets(line, 255, stream);
    if (strstr(line, "OK") != NULL)
        printf("You pressed the OK button.\n");
    else
        printf("This is not the OK button.\n");
    pclose(stream);
    
}

Архив с кодом программы, исполняемым файлом и скриншотом:
http://www.mediafire.com/?6vlarpi86332bom

Увидеть скриншот (и скачать пример) можно на мурге:
http://www.murga-linux.com/puppy/viewto … 831#639831

24

sklimkin пишет:

Есть такой инструмент gtkdialog связывающий скриптование на bash с графическим окном на GTK

Рискуя оказаться КОпетаном, упомяну zenity. Я не сравниваю эти инструменты, я только напоминаю о существовании одного из них. К примеру, мне, в своё время, очень не хватало подобной утилитки. Возможно, для кого-то эта ложка окажется как раз к обеду ab

#!/bin/bash
 
if zenity --question --text="Please press a button."; then
    zenity --info --text="You pressed Yes\!"
else
    zenity --error --text="You pressed No\!"
fi

http://upload.wikimedia.org/wikipedia/commons/2/29/Zenity-ubuntu-lucid.png

25

Привет Mike53!

Рискуя оказаться КОпетаном, упомяну zenity. Я не сравниваю эти инструменты, я только напоминаю о существовании одного из них. К примеру, мне, в своё время, очень не хватало подобной утилитки. Возможно, для кого-то эта ложка окажется как раз к обеду

В таком разе вот еще "ложечки из сервиза":
http://freecode.com/projects/xdialog
http://invisible-island.net/dialog/
http://hightek.org/dialog/
http://xdialog.free.fr/
http://code.google.com/p/yad/
- диалоги на самые разные вкусы.

26 (27.07.2012 21:35:56 отредактировано sklimkin)

Пример программы на GTK+ создающий заготовку меню с наполнением, которую пользователь может приспособить для своих конкретных нужд.
http://stuff.mit.edu/afs/sipb/user/xiph … _manager.c

/* UI Manager
 *
 * The GtkUIManager object allows the easy creation of menus
 * from an array of actions and a description of the menu hierarchy.
 */

#include <gtk/gtk.h>

static void
activate_action (GtkAction *action)
{
  g_message ("Action \"%s\" activated", gtk_action_get_name (action));
}

static void
activate_radio_action (GtkAction *action, GtkRadioAction *current)
{
  g_message ("Radio action \"%s\" selected", 
         gtk_action_get_name (GTK_ACTION (current)));
}

static GtkActionEntry entries[] = {
  { "FileMenu", NULL, "_File" },               /* name, stock id, label */
  { "PreferencesMenu", NULL, "_Preferences" }, /* name, stock id, label */
  { "ColorMenu", NULL, "_Color"  },            /* name, stock id, label */
  { "ShapeMenu", NULL, "_Shape" },             /* name, stock id, label */
  { "HelpMenu", NULL, "_Help" },               /* name, stock id, label */
  { "New", GTK_STOCK_NEW,                      /* name, stock id */
    "_New", "<control>N",                      /* label, accelerator */
    "Create a new file",                       /* tooltip */ 
    G_CALLBACK (activate_action) },      
  { "Open", GTK_STOCK_OPEN,                    /* name, stock id */
    "_Open","<control>O",                      /* label, accelerator */     
    "Open a file",                             /* tooltip */
    G_CALLBACK (activate_action) }, 
  { "Save", GTK_STOCK_SAVE,                    /* name, stock id */
    "_Save","<control>S",                      /* label, accelerator */     
    "Save current file",                       /* tooltip */
    G_CALLBACK (activate_action) },
  { "SaveAs", GTK_STOCK_SAVE,                  /* name, stock id */
    "Save _As...", NULL,                       /* label, accelerator */     
    "Save to a file",                          /* tooltip */
    G_CALLBACK (activate_action) },
  { "Quit", GTK_STOCK_QUIT,                    /* name, stock id */
    "_Quit", "<control>Q",                     /* label, accelerator */     
    "Quit",                                    /* tooltip */
    G_CALLBACK (activate_action) },
  { "About", NULL,                             /* name, stock id */
    "_About", "<control>A",                    /* label, accelerator */     
    "About",                                   /* tooltip */  
    G_CALLBACK (activate_action) },
  { "Logo", "demo-gtk-logo",                   /* name, stock id */
     NULL, NULL,                               /* label, accelerator */     
    "GTK+",                                    /* tooltip */
    G_CALLBACK (activate_action) },
};
static guint n_entries = G_N_ELEMENTS (entries);


static GtkToggleActionEntry toggle_entries[] = {
  { "Bold", GTK_STOCK_BOLD,                    /* name, stock id */
     "_Bold", "<control>B",                    /* label, accelerator */     
    "Bold",                                    /* tooltip */
    G_CALLBACK (activate_action), 
    TRUE },                                    /* is_active */
};
static guint n_toggle_entries = G_N_ELEMENTS (toggle_entries);

enum {
  COLOR_RED,
  COLOR_GREEN,
  COLOR_BLUE
};

static GtkRadioActionEntry color_entries[] = {
  { "Red", NULL,                               /* name, stock id */
    "_Red", "<control>R",                      /* label, accelerator */     
    "Blood", COLOR_RED },                      /* tooltip, value */
  { "Green", NULL,                             /* name, stock id */
    "_Green", "<control>G",                    /* label, accelerator */     
    "Grass", COLOR_GREEN },                    /* tooltip, value */
  { "Blue", NULL,                              /* name, stock id */
    "_Blue", "<control>B",                     /* label, accelerator */     
    "Sky", COLOR_BLUE },                       /* tooltip, value */
};
static guint n_color_entries = G_N_ELEMENTS (color_entries);

enum {
  SHAPE_SQUARE,
  SHAPE_RECTANGLE,
  SHAPE_OVAL
};

static GtkRadioActionEntry shape_entries[] = {
  { "Square", NULL,                            /* name, stock id */
    "_Square", "<control>S",                   /* label, accelerator */     
    "Square",  SHAPE_SQUARE },                 /* tooltip, value */
  { "Rectangle", NULL,                         /* name, stock id */
    "_Rectangle", "<control>R",                /* label, accelerator */     
    "Rectangle", SHAPE_RECTANGLE },            /* tooltip, value */
  { "Oval", NULL,                              /* name, stock id */
    "_Oval", "<control>O",                     /* label, accelerator */     
    "Egg", SHAPE_OVAL },                       /* tooltip, value */  
};
static guint n_shape_entries = G_N_ELEMENTS (shape_entries);

static const gchar *ui_info = 
"<ui>"
"  <menubar name='MenuBar'>"
"    <menu action='FileMenu'>"
"      <menuitem action='New'/>"
"      <menuitem action='Open'/>"
"      <menuitem action='Save'/>"
"      <menuitem action='SaveAs'/>"
"      <separator/>"
"      <menuitem action='Quit'/>"
"    </menu>"
"    <menu action='PreferencesMenu'>"
"      <menu action='ColorMenu'>"
"    <menuitem action='Red'/>"
"    <menuitem action='Green'/>"
"    <menuitem action='Blue'/>"
"      </menu>"
"      <menu action='ShapeMenu'>"
"        <menuitem action='Square'/>"
"        <menuitem action='Rectangle'/>"
"        <menuitem action='Oval'/>"
"      </menu>"
"      <menuitem action='Bold'/>"
"    </menu>"
"    <menu action='HelpMenu'>"
"      <menuitem action='About'/>"
"    </menu>"
"  </menubar>"
"  <toolbar  name='ToolBar'>"
"    <toolitem action='Open'/>"
"    <toolitem action='Quit'/>"
"    <separator action='Sep1'/>"
"    <toolitem action='Logo'/>"
"  </toolbar>"
"</ui>";

GtkWidget *
do_ui_manager (GtkWidget *do_widget)
{
  static GtkWidget *window = NULL;
  
  if (!window)
    {
      GtkWidget *box1;
      GtkWidget *box2;
      GtkWidget *separator;
      GtkWidget *label;
      GtkWidget *button;
      GtkUIManager *ui;
      GtkActionGroup *actions;
      GError *error = NULL;

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      gtk_window_set_screen (GTK_WINDOW (window),
                 gtk_widget_get_screen (do_widget));
      
      g_signal_connect (window, "destroy",
            G_CALLBACK (gtk_widget_destroyed), &window);
      g_signal_connect (window, "delete-event",
            G_CALLBACK (gtk_true), NULL);

      actions = gtk_action_group_new ("Actions");
      gtk_action_group_add_actions (actions, entries, n_entries, NULL);
      gtk_action_group_add_toggle_actions (actions, 
                       toggle_entries, n_toggle_entries, 
                       NULL);
      gtk_action_group_add_radio_actions (actions, 
                      color_entries, n_color_entries, 
                      COLOR_RED,
                      G_CALLBACK (activate_radio_action), 
                      NULL);
      gtk_action_group_add_radio_actions (actions, 
                      shape_entries, n_shape_entries, 
                      SHAPE_OVAL,
                      G_CALLBACK (activate_radio_action), 
                      NULL);

      ui = gtk_ui_manager_new ();
      gtk_ui_manager_insert_action_group (ui, actions, 0);
      gtk_window_add_accel_group (GTK_WINDOW (window), 
                  gtk_ui_manager_get_accel_group (ui));
      gtk_window_set_title (GTK_WINDOW (window), "UI Manager");
      gtk_container_set_border_width (GTK_CONTAINER (window), 0);
      
      if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
    {
      g_message ("building menus failed: %s", error->message);
      g_error_free (error);
    }

      box1 = gtk_vbox_new (FALSE, 0);
      gtk_container_add (GTK_CONTAINER (window), box1);
      
      gtk_box_pack_start (GTK_BOX (box1),
              gtk_ui_manager_get_widget (ui, "/MenuBar"),
              FALSE, FALSE, 0);

      label = gtk_label_new ("Type\n<alt>\nto start");
      gtk_widget_set_size_request (label, 200, 200);
      gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
      gtk_box_pack_start (GTK_BOX (box1), label, TRUE, TRUE, 0);


      separator = gtk_hseparator_new ();
      gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);


      box2 = gtk_vbox_new (FALSE, 10);
      gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
      gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);

      button = gtk_button_new_with_label ("close");
      g_signal_connect_swapped (button, "clicked",
                G_CALLBACK (gtk_widget_destroy), window);
      gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
      gtk_widget_grab_default (button);

      gtk_widget_show_all (window);
    }
  else
    {
      gtk_widget_destroy (window);
      window = NULL;
    }

  return window;
}

А это немного переработанный мной этот пример. Добавил окошечко со скромным названием "Supervisor" и полезный в жизни "MessageBox"
Мои вставки/добавления помечены added by SNK

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

/*modified by SNK http://stuff.mit.edu/afs/sipb/user/xiphmont/share/gtk-2.0/demo/ui_manager.c */
/* added by SNK GtkWidget* msgbox (void) */
gchar * strMsg;  // string for MessageBox - see at end of file
/*---------------------------------------------------------------------------*/

static void
activate_action (GtkAction *action)
{
  g_message ("Action \"%s\" activated", gtk_action_get_name (action));
}

static void
activate_radio_action (GtkAction *action, GtkRadioAction *current)
{
  g_message ("Radio action \"%s\" selected",
         gtk_action_get_name (GTK_ACTION (current)));
}

static GtkActionEntry entries[] = {
  { "FileMenu", NULL, "_File" },               /* name, stock id, label */
  { "PreferencesMenu", NULL, "_Preferences" }, /* name, stock id, label */
  { "ColorMenu", NULL, "_Color"  },            /* name, stock id, label */
  { "ShapeMenu", NULL, "_Shape" },             /* name, stock id, label */
  { "HelpMenu", NULL, "_Help" },               /* name, stock id, label */
  { "New", GTK_STOCK_NEW,                      /* name, stock id */
    "_New", "<control>N",                      /* label, accelerator */
    "Create a new file",                       /* tooltip */
    G_CALLBACK (activate_action) },
  { "Open", GTK_STOCK_OPEN,                    /* name, stock id */
    "_Open","<control>O",                      /* label, accelerator */
    "Open a file",                             /* tooltip */
    G_CALLBACK (activate_action) },
  { "Save", GTK_STOCK_SAVE,                    /* name, stock id */
    "_Save","<control>S",                      /* label, accelerator */
    "Save current file",                       /* tooltip */
    G_CALLBACK (activate_action) },
  { "SaveAs", GTK_STOCK_SAVE,                  /* name, stock id */
    "Save _As...", NULL,                       /* label, accelerator */
    "Save to a file",                          /* tooltip */
    G_CALLBACK (activate_action) },
  { "Quit", GTK_STOCK_QUIT,                    /* name, stock id */
    "_Quit", "<control>Q",                     /* label, accelerator */
    "Quit",                                    /* tooltip */
    G_CALLBACK (activate_action) },
  { "About", NULL,                             /* name, stock id */
    "_About", "<control>A",                    /* label, accelerator */
    "About",                                   /* tooltip */
    G_CALLBACK (activate_action) },
  { "Logo", "demo-gtk-logo",                   /* name, stock id */
     NULL, NULL,                               /* label, accelerator */
    "GTK+",                                    /* tooltip */
    G_CALLBACK (activate_action) },
};
static guint n_entries = G_N_ELEMENTS (entries);


static GtkToggleActionEntry toggle_entries[] = {
  { "Bold", GTK_STOCK_BOLD,                    /* name, stock id */
     "_Bold", "<control>B",                    /* label, accelerator */
    "Bold",                                    /* tooltip */
    G_CALLBACK (activate_action),
    TRUE },                                    /* is_active */
};
static guint n_toggle_entries = G_N_ELEMENTS (toggle_entries);

enum {
  COLOR_RED,
  COLOR_GREEN,
  COLOR_BLUE
};

static GtkRadioActionEntry color_entries[] = {
  { "Red", NULL,                               /* name, stock id */
    "_Red", "<control>R",                      /* label, accelerator */
    "Blood", COLOR_RED },                      /* tooltip, value */
  { "Green", NULL,                             /* name, stock id */
    "_Green", "<control>G",                    /* label, accelerator */
    "Grass", COLOR_GREEN },                    /* tooltip, value */
  { "Blue", NULL,                              /* name, stock id */
    "_Blue", "<control>B",                     /* label, accelerator */
    "Sky", COLOR_BLUE },                       /* tooltip, value */
};
static guint n_color_entries = G_N_ELEMENTS (color_entries);

enum {
  SHAPE_SQUARE,
  SHAPE_RECTANGLE,
  SHAPE_OVAL
};

static GtkRadioActionEntry shape_entries[] = {
  { "Square", NULL,                            /* name, stock id */
    "_Square", "<control>S",                   /* label, accelerator */
    "Square",  SHAPE_SQUARE },                 /* tooltip, value */
  { "Rectangle", NULL,                         /* name, stock id */
    "_Rectangle", "<control>R",                /* label, accelerator */
    "Rectangle", SHAPE_RECTANGLE },            /* tooltip, value */
  { "Oval", NULL,                              /* name, stock id */
    "_Oval", "<control>O",                     /* label, accelerator */
    "Egg", SHAPE_OVAL },                       /* tooltip, value */
};
static guint n_shape_entries = G_N_ELEMENTS (shape_entries);

static const gchar *ui_info =
"<ui>"
"  <menubar name='MenuBar'>"
"    <menu action='FileMenu'>"
"      <menuitem action='New'/>"
"      <menuitem action='Open'/>"
"      <menuitem action='Save'/>"
"      <menuitem action='SaveAs'/>"
"      <separator/>"
"      <menuitem action='Quit'/>"
"    </menu>"
"    <menu action='PreferencesMenu'>"
"      <menu action='ColorMenu'>"
"    <menuitem action='Red'/>"
"    <menuitem action='Green'/>"
"    <menuitem action='Blue'/>"
"      </menu>"
"      <menu action='ShapeMenu'>"
"        <menuitem action='Square'/>"
"        <menuitem action='Rectangle'/>"
"        <menuitem action='Oval'/>"
"      </menu>"
"      <menuitem action='Bold'/>"
"    </menu>"
"    <menu action='HelpMenu'>"
"      <menuitem action='About'/>"
"    </menu>"
"  </menubar>"
"  <toolbar  name='ToolBar'>"
"    <toolitem action='Open'/>"
"    <toolitem action='Quit'/>"
"    <separator action='Sep1'/>"
"    <toolitem action='Logo'/>"
"  </toolbar>"
"</ui>";
/*---------------------------------------------------------------------------*/

GtkWidget *
do_ui_manager (GtkWidget *do_widget)
{
  static GtkWidget *window = NULL;

  if (!window)
    {
      GtkWidget *box1;
      GtkWidget *box2;
      GtkWidget *separator;
      GtkWidget *label;
      GtkWidget *button;
      GtkUIManager *ui;
      GtkActionGroup *actions;
      GError *error = NULL;

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      gtk_window_set_screen (GTK_WINDOW (window),
                 gtk_widget_get_screen (do_widget));

      g_signal_connect (window, "destroy",
            G_CALLBACK (gtk_widget_destroyed), &window);
      g_signal_connect (window, "delete-event",
            G_CALLBACK (gtk_true), NULL);

      actions = gtk_action_group_new ("Actions");
      gtk_action_group_add_actions (actions, entries, n_entries, NULL);
      gtk_action_group_add_toggle_actions (actions,
                       toggle_entries, n_toggle_entries,
                       NULL);
      gtk_action_group_add_radio_actions (actions,
                      color_entries, n_color_entries,
                      COLOR_RED,
                      G_CALLBACK (activate_radio_action),
                      NULL);
      gtk_action_group_add_radio_actions (actions,
                      shape_entries, n_shape_entries,
                      SHAPE_OVAL,
                      G_CALLBACK (activate_radio_action),
                      NULL);

      ui = gtk_ui_manager_new ();
      gtk_ui_manager_insert_action_group (ui, actions, 0);
      g_object_unref (actions);
      gtk_window_add_accel_group (GTK_WINDOW (window),
                  gtk_ui_manager_get_accel_group (ui));
      gtk_window_set_title (GTK_WINDOW (window), "UI Manager");
      gtk_container_set_border_width (GTK_CONTAINER (window), 0);


      if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
        {
          g_message ("building menus failed: %s", error->message);
          g_error_free (error);
        }

      box1 = gtk_vbox_new (FALSE, 0);
      gtk_container_add (GTK_CONTAINER (window), box1);

      gtk_box_pack_start (GTK_BOX (box1),
              gtk_ui_manager_get_widget (ui, "/MenuBar"),
              FALSE, FALSE, 0);

      label = gtk_label_new ("Type\n<alt>\nto start");
      gtk_widget_set_size_request (label, 200, 200);
      gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
      gtk_box_pack_start (GTK_BOX (box1), label, TRUE, TRUE, 0);


      separator = gtk_hseparator_new ();
      gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);


      box2 = gtk_vbox_new (FALSE, 10);
      gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
      gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);

      button = gtk_button_new_with_label ("close");
      g_signal_connect_swapped (button, "clicked",
                G_CALLBACK (gtk_widget_destroy), window);

   /* added by SNK 21.05.2012 */
//    g_signal_connect (window, "destroy", gtk_main_quit, NULL);
//    g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_main_quit), window);

      gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
      gtk_widget_grab_default (button);

      gtk_widget_show_all (window);
      g_object_unref (ui);
    }
  else
    {
      gtk_widget_destroy (window);
      window = NULL;
    }

  return window;
}
/*---------------------------------------------------------------------------*/

/* all as downwrited added by SNK 21.06.2012 */
/* makes the widget-dialog "Message Box", used global string strMsg for messages */
GtkWidget* msgbox (void)
{
    GtkWidget *msgbox; // non-modal dialog
    GtkWidget * content_area;
    GtkWidget * label;
    //strMsg = "Test MessageBox";

    msgbox = gtk_dialog_new_with_buttons ("Message Box", NULL, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                                 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);

   content_area = gtk_dialog_get_content_area (GTK_DIALOG (msgbox));
   label = gtk_label_new (strMsg);
   gtk_container_add (GTK_CONTAINER (content_area), label);
   gtk_widget_show_all (msgbox);

    if (gtk_dialog_run(GTK_DIALOG (msgbox)) == GTK_RESPONSE_OK)
    gtk_widget_destroy (msgbox);

  return msgbox;
}
/*---------------------------------------------------------------------------*/

static void button_clicked(GtkWidget* widget, gpointer data)
{
  //g_print("Button was clicked!\n");
    strMsg = "Button1 'Quit' was clicked...";
    msgbox();
}

static void button2_clicked(GtkWidget* widget, gpointer data)
{
  //g_print("Button2 was clicked!\n");
    strMsg = "Button2 'Show-hide_UI' was clicked...";
    msgbox();
  do_ui_manager (data);
}

static gboolean delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
{
  //g_print("Delete event occurred\n");
    strMsg = "Delete event occurred";
    msgbox();

  return FALSE;
}

static void destroy(GtkWidget * widget, gpointer data)
{
  //g_print("Destroy signal was sent\n");
    strMsg = "Destroy signal was sent";
    msgbox();
  gtk_main_quit();
}
/*---------------------------------------------------------------------------*/

int main(int argc, char ** argv)
{
    GtkWidget *win = NULL;

    GtkWidget * window;
    GtkWidget *vbox;
    GtkWidget * button1;
    GtkWidget *button2;
    const gchar * title = "Supervisor";
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), title);
    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);
    button1 = gtk_button_new_with_label("Quit");
    button2 = gtk_button_new_with_label("Show-hide_UI");
    /* Callbacks to destroy window  "UI Manager" show<-->hide */
    g_signal_connect(G_OBJECT(button1), "clicked", G_CALLBACK(button_clicked), NULL);
    g_signal_connect_swapped(G_OBJECT(button1), "clicked", G_CALLBACK(gtk_widget_destroy), G_OBJECT(window));
    /* Callback to switch window  "UI Manager" show<-->hide */
    g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button2_clicked), win);
    /* create vertical-box frame on current window  "Supervisor" */
    vbox = gtk_vbox_new (FALSE, 3);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show (vbox);
    gtk_container_add(GTK_CONTAINER(vbox), button1);
    gtk_container_add(GTK_CONTAINER(vbox), button2);

    // go to window "UI Manager"
    do_ui_manager (win);

    gtk_widget_show(button1);
    gtk_widget_show(button2);
    gtk_widget_show(window);
    gtk_main();
    return 0;
}
/*---------------------------------------------------------------------------*/

В примере используется (в виде строк) вставка предварительно сделанного (в Glade ?) пользовательского интерфейса

static const gchar *ui_info =
"<ui>"
"  <menubar name='MenuBar'>"
... ... ...
"</ui>";

Makefile

TARGET = UIManager
CC = gcc
LDLIBS=`pkg-config --libs gtk+-2.0 gmodule-2.0`
CFLAGS=-Wall -g `pkg-config --cflags gtk+-2.0 gmodule-2.0`

all:
    $(CC) -o $(TARGET) main.c $(LDLIBS) $(CFLAGS)

clean:
    rm $(TARGET)

т.е. это в развитие того, что Mike53 рассказывал в начале этой темы.

27

Уверен что функции таймаута хватит для работы.

28 (02.11.2012 00:39:19 отредактировано sklimkin)

SCROT + GTKDIALOG для оконного интерфейса из bash-script
Мне нравится программа scrot (SCReenshOT) для "фотографирования" экрана монитора.
У нее есть необходимые и понятные аргументы/ключи для вызова, но... она консольная.

Сделал интерфейс gcc + GTK2 (в архиве есть и исхлдник на СИ и скриншоты):
http://www.mediafire.com/?u3860bikpsatd2t

Но размер исполняемого файла (37 КБ) заставил задуматься.
Взялся за gtkdialog (файл получился 17 КБ)  - но в gtkdialog очень мало СИ-шных возможностей.
Пришлось интенсивно читать о премудростях bash-script

Оказалось, что совсем непросто повторить в gtkdialog то, что легко получается просто в bash
Из основного модуля gtkdialog надо обращаться к внешним (по отношению к нему) функциям.
Вот как выглядят ф-ции, которые пишутся в начале скрипта:

call() {
funccommand $COMBOBOX1 $COMBOBOX2
echo "call_COMMANDSTRING= $COMMANDSTRING"
cat "$COMMANDSTRING" | scrot
}

она вызывается из основного кода скрипта:

<button>
<label>ACCEPT</label>
<variable>GOMMANDSRING</variable>
<action>echo "Start of scrot:"</action>
<action>call </action>
</button>

и она вызывает другую пользовательскую ф-цию funccommand () :

funccommand () {
if [ "$COMBOBOX1" == "All" ]; then
COMMAND1=
else COMMAND1="-s"
fi
... ... ...
... ... ...
echo "End of export variables."
}

В результате получаем строку аргументов для вызова программы scrot такого вида:
scrot -d 5 -s -e 'mv $f ~' (задержка 5 сек., выбор области, поместить в DIR)

Но для этого нужно иметь gtkdialig версии не ниже 0.8.0
Но и это еще не все,  надо объявить:

export -f имя_внешней-функции

Теперь скрипт (его размер 2 КБ) работает, но "командная строка" для scrot интерпретеруется не вся.
scrot "не видит" важного аргумента - тип захвата (все, окно, область-рамка) и захватывает весь экран.

ПОЭТОМУ: если есть знатоки ИМЕННО gtkdialog, подскажите где "дырка".

Привожу текст gtkdialog-скрипта :

#!/bin/bash
GTKDIALOG=gtkdialog

call() {
funccommand $COMBOBOX1 $COMBOBOX2
echo "call_COMMANDSTRING= $COMMANDSTRING"
cat "$COMMANDSTRING" | scrot
}

funccommand () {
if [ "$COMBOBOX1" == "All" ]; then
COMMAND1= 
else COMMAND1="-s"
fi
if [ "$COMBOBOX1" == "Window" ]; then
COMMAND1="-u"
fi
echo "COMMAND1= $COMMAND1"
COMMAND2="$COMBOBOX2"
echo "COMMAND2= $COMMAND2"
COMMANDSTRING="-d $COMMAND2 $COMMAND1 -e 'mv $f '"
echo "COMMANDSTRING= $COMMANDSTRING"
echo "End of export variables."
}

export -f call
export -f funccommand

export MAIN_DIALOG='
<window title="SCReenshOT  screen-grabber">
    <vbox>
        <frame Command button EXIT>
            <hbox>
                <button cancel></button>
            </hbox>
        </frame>

        <frame User-settings>
            <hbox>
                <vbox>
                    <text>
                        <label>Select grab-mode</label>
                    </text>
                    <combobox>
                        <variable>COMBOBOX1</variable>
                        <item>All</item>
                        <item>Window</item>
                        <item>Area</item>
                        <action>save:COMBOBOX1</action>
                        <variable>COMMAND1</variable>
                    </combobox>
                </vbox>
                <vbox>
                    <text>
                        <label>Time-delay-sec</label>
                    </text>
                    <combobox>
                        <variable>COMBOBOX2</variable>
                        <item>0</item>
                        <item>1</item>
                        <item>2</item>
                        <item>5</item>
                        <item>10</item>
                        <item>20</item>
                        <action>save:COMBOBOX2</action>
                        <variable>COMMAND2</variable>
                    </combobox>
                </vbox>
                <button>
                    <label>ACCEPT</label>
                    <variable>GOMMANDSRING</variable>
                    <action>echo "Start of scrot:"</action>
                    <action>call </action>
                </button>
            </hbox>
        </frame>

        <frame Select DIR for save screenshot>
            <chooser>
                <height>200</height>
                <width>200</width>
                <variable>FILE</variable>
                <default>/home/</default>
                <action>cat [ echo $FILE ] | zenity --text-info  --width=250 --height=250 --title $FILE</action>
            </chooser>
        </frame>
    </vbox>
</window>
'

case $1 in
    -d | --dump) echo "$MAIN_DIALOG" ;;
    *) $GTKDIALOG --program=MAIN_DIALOG ;;
esac

29

Отвечаю на свой вопрос сам: все дело в порядке ключей командной строки.
Должно быть: scrot  "grab-mode" "time-delay" "DIR-to-save-file"
"grab-mode"   -m или -s или -u
"time-delay"   -d 0 или 1 или 2 или ...
"DIR-to-save-file"   pwd из $FILE

Вот последний (?) вариант скрипта:

#!/bin/bash
GTKDIALOG=gtkdialog

call() {
funccommand $COMBOBOX1 $COMBOBOX2
echo "call_COMMANDSTRING= $COMMANDSTRING"
scrot "$COMMANDSTRING"
}

funccommand () {
if [ "$COMBOBOX1" == "All" ]; then
COMMAND1="-m"
else COMMAND1="-s"
fi
if [ "$COMBOBOX1" == "Window" ]; then
COMMAND1="-u"
fi
echo "COMMAND1= $COMMAND1"
COMMAND2="$COMBOBOX2"
echo "COMMAND2= $COMMAND2"
COMMANDSTRING="$COMMAND1 -b -d $COMMAND2 -e 'mv $f ~'"
echo "COMMANDSTRING= $COMMANDSTRING"
echo "End of export variables."
}

export -f call
export -f funccommand

export MAIN_DIALOG='
<window title="SCReenshOT  screen-grabber">
    <vbox>
        <frame Command button EXIT>
            <hbox>
                <button cancel></button>
            </hbox>
        </frame>

        <frame User-settings>
            <hbox>
                <vbox>
                    <text>
                        <label>Select grab-mode</label>
                    </text>
                    <combobox>
                        <variable>COMBOBOX1</variable>
                        <item>All</item>
                        <item>Window</item>
                        <item>Area</item>
                        <action>save:COMBOBOX1</action>
                        <variable>COMMAND1</variable>
                    </combobox>
                </vbox>
                <vbox>
                    <text>
                        <label>Time-delay-sec</label>
                    </text>
                    <combobox>
                        <variable>COMBOBOX2</variable>
                        <item>0</item>
                        <item>1</item>
                        <item>2</item>
                        <item>3</item>
                        <item>5</item>
                        <item>10</item>
                        <item>20</item>
                        <action>save:COMBOBOX2</action>
                        <variable>COMMAND2</variable>
                    </combobox>
                </vbox>
                <button>
                    <label>ACCEPT</label>
                    <variable>GOMMANDSRING</variable>
                    <action>echo "Start of scrot:"</action>
                    <action>call </action>
                </button>
            </hbox>
        </frame>

        <frame Select DIR for save screenshot>
            <chooser>
                <height>200</height>
                <width>200</width>
                <variable>FILE</variable>
                <default>/home/</default>
                <action>cat [ echo $FILE ] | zenity --text-info  --width=250 --height=250 --title $FILE</action>
            </chooser>
        </frame>
    </vbox>
</window>
'

case $1 in
    -d | --dump) echo "$MAIN_DIALOG" ;;
    *) $GTKDIALOG --program=MAIN_DIALOG ;;
esac

30

Два варианта GUI для scrot на СИ

1. в теле СИ-функции все строки, как адаптация bash-script, работают со scrot через gtkdialog
   В результате компиляции получается бинарный файл scrotdlg 9.5 КБ
http://www.mediafire.com/download.php?el5ruubgov613qg

2. СИ-файл использует только GTK+ виджеты/функции и работает со scrot через GTK+
   В результате компиляции получается бинарный файл scrotdlg 31.7 КБ
http://www.mediafire.com/?k8e2cbw21hcczdc

В архивах СИ-исходник, бинарный файл, Malefile и по паре скриншотов.
То есть это 2 разные возможности работы со скрин-грабером scrot.
Через gtkdialog и через GTK+

3-й вариант использующий только bash-скрипт (который обращается к gtkdialog) смотри на:
http://linuxforum.ru/viewtopic.php?id=25092