Создаём приложение на gtk

Я вижу часто отсылки на то, как реализуется то или иное на qt, но gtk тоже хорош. Я пишу на C и не хочу учить каждые три года стандарты C++, чтобы быть на волне. Мне хватает простой сишечки и чтобы делать графический софт, выбор пал на gtk.

Документацию к нему я использую в программе devhelp. Итак начнем.

Первое что нужно сделать, это создать приложение, которое будет видно на dbus шине.

GtkApplication *app;

static void app_activate_cb ( GtkApplication *app, gpointer user_data ) {
}

int main ( int argc, char **argv ) {
	app = gtk_application_new ( "com.xverizex.configurator", G_APPLICATION_FLAGS_NONE );
	g_application_register ( ( GApplication * ) app, NULL, NULL );
	g_signal_connect ( app, "activate", G_CALLBACK ( app_activate_cb ), NULL );
	return g_application_run ( ( GApplication * ) app, argc, argv );
}

После того, как приложение активировалось, нужно наполнить его виджетами. Я создаю в этом файле структуру, которая будет хранить все виджеты, которые будут изменяться в оформлении, если указать другую цветовую схему.

Вот структура:

struct widgets {
	GtkWidget *main_window;
	GtkWidget *notebook;
	GtkWidget *tree_view_sources;
	GtkWidget *label_tab_sources;
	GtkWidget *header_bar;
	GtkWidget *search_entry;
	GtkWidget *button_add_item;
	GtkWidget *tree_view_reactions;
	GtkWidget *label_tab_reactions;
	GtkWidget *window_add_source;
        ...
        GtkWidget *STUBS;

в конце я создаю специальную переменную, так называемую STUBS, которой я ничего не присваиваю. В приложении виджетов очень много и нужен какой то способ присвоить нужным виджетам нужное имя. Я делаю это так.

static void set_theme_name ( const char *name ) {
	struct widgets **p = ( struct widgets ** ) &w;
	for ( int i = 0; p[i] != NULL; i++ ) {
		gtk_widget_set_name ( ( GtkWidget * ) p[i], name );
	}
	char buf[255];
	snprintf ( buf, 255, "%s_info", name );
	gtk_widget_set_name ( w.label_info_db_settings, buf );
	gtk_widget_set_name ( w.label_info_mail_settings, buf );
}

тем виджетам, которым нужно ещё особенное оформление, я уже назначаю им отдельные названия. В этой функции я пробегаю по всем виджетам и указываю им имя темы.
Для создания меню используется вот такой код.

static void create_actions ( void ) {
	const GActionEntry entries[] = {
		{ "quit", action_activate_quit },
		{ "settings", action_settings },
		{ "apply_settings", action_activate_apply_settings },
		{ "select_light_theme", action_activate_select_light_theme },
		{ "select_dark_theme", action_activate_select_dark_theme },
		{ "select_standard_theme", action_activate_select_standard_theme }
	};
	g_action_map_add_action_entries ( G_ACTION_MAP (app), entries, G_N_ELEMENTS (entries), NULL );
}

И потом назначаем эти команды в меню.

	pop_menu = g_menu_new ( );
	GMenu *menu_app = g_menu_new ( );
	GMenu *menu_themes = g_menu_new ( );
	g_menu_append ( menu_app, "Настройки", "app.settings" );
	g_menu_append ( menu_app, "Обновить", "app.apply_settings" );
	g_menu_append ( menu_themes, "Светлая тема", "app.select_light_theme" );
	g_menu_append ( menu_themes, "Темная тема", "app.select_dark_theme" );
	g_menu_append ( menu_themes, "Стандартная тема", "app.select_standard_theme" );
	g_menu_append_submenu ( menu_app, "Сменить тему", ( GMenuModel * ) menu_themes );
	g_menu_append ( menu_app, "Выход", "app.quit" );

	gtk_application_set_app_menu ( app, ( GMenuModel * ) menu_app );

В итоге пописав это приложение, оно превращается в нечто прекрасное и красивое.

image
@xverizex
10.09.2020 12:37 UTC
Первоисточник

Комментарии

@
10.09.2020 08:25 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@QtRoS
10.09.2020 10:15 UTC
+3

Вопрос Вам и xverizex — а Vala пробовали использовать? С того самого момента как я о нем узнал, было интересно пообщаться с тем, кто использовал его.
Кстати API вида "все_эти_названия_функций" достаточно удобно оборачиваются в классы, например с помощью C#, если уж очень не хочется писать на C. Но как пишет автор статьи и за решеткой есть жизнь, и на кладбище есть плюсы (с) — C и правда хорош своей неизменностью.

10.09.2020 10:18 UTC
0
Нет. Vala не пробовал.
10.09.2020 11:29 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
10.09.2020 11:36 UTC
0
Мне не нравиться как в qt нужно подключать чужие библиотеки. там какая то непонятка с лицензией. да и ещё платить большую сумму за пользование qt ( вроде как для коммерческих целей ), это уже слишком. я лучше на gtk попишу. на gtk можно коммерческие создавать и бесплатная лицензия. да и что там. включил vim, создал makefile, указал нужные библиотеки и всё. всё как должно быть. ну это в моем случае. у вас же ситуация полюбому другая и вам виднее что лучше использовать. я ни на что не намекаю как бы.
10.09.2020 18:41 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
11.09.2020 04:44 UTC
0
Отстаете gtk4 перешел на meson сборку. Уже нужно создавть не makefile а meson.build
09.10.2020 16:17 UTC
0
Я пробовал, даже захотелось написать первую статью об этом, Vala очень хороша.
@COKPOWEHEU
10.09.2020 11:19 UTC
0
Несколько раз пролистал заметку, но так и не увидел ссылки на код.
@xverizex
10.09.2020 11:22 UTC
0
я указал только некоторые способы, которые делаются с помощью gtk, это же туториал. саму программу я показывать не хочу.
10.09.2020 13:04 UTC
+5
Тогда в чем смысл статьи?
Если показать что на gtk можно сделать и так, то тут почти ничего нет: ни контролов с обработкой событий, ни даже примера кода с флагами компилятора. Без этого выглядит как будто вы только что узнать что такое gtk и делитесь радостью. Но в чем польза для сообщества?
Меня, например, заинтересовало ваше переключение между темами, но воспроизвести его у себя вы не даете.
Или вот вы пишете «я создаю специальную переменную, так называемую STUBS, которой я ничего не присваиваю.» — а в чем тогда ее смысл? Она ведь даже нигде по коду больше не используется.
«Первое что нужно сделать, это создать приложение, которое будет видно на dbus шине.» — а зачем приложению быть видимым на dbus шине? Особенно учитывая что gtk штука кроссплатформенная, а на некоторых платформах dbus шины просто нет.
10.09.2020 13:18 UTC
0
STUBS нужен для того, чтобы в функции set_theme, смотрите. там проверяется на NULL. этот виджет всегда NULL, и это значит что выполнение цикла нужно прекратить. если вы не поняли код, то вот что происходит — здесь по всем виджетам структуры проходит указатель и назначает каждому виджету новое имя для темы оформления.
	struct widgets **p = ( struct widgets ** ) &w;
	for ( int i = 0; p[i] != NULL; i++ ) {
		gtk_widget_set_name ( ( GtkWidget * ) p[i], name );
	}

насчет dbus. ну я считаю что не нужно ограничивать приложение только из-за того, что в других ос нет dbus. я хочу пользоваться всеми преимуществами ос и правильней будет использовать dbus. потому что этот dbus просто классная штука. а если хотите для windows 10 делать, то лучше использовать WinRT или как она там называется, которая работает на windows 8 и 10. ну это моё мнение. я бы так делал. то есть я бы не стал пытаться сделать gtk приложение нормальное для windows. большинство библиотек скомпилировано для VC, то есть visual studio и было бы логичней для windows писать в Visual Studio не классические приложения, а новые. а в linux я предпочитаю gtk, gio, glib.
10.09.2020 17:36 UTC
0

Вам, конечно, виднее, но КМК если в структуре нужен NULL — нужно использовать NULL, а не неинициализированную переменную.
Идея использовать структуру в роли массива с именами — прикольная ;)

10.09.2020 18:17 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
11.09.2020 04:49 UTC
0
То есть вы хотите сказать, что компилятор нарошно между вторым и третьим элементом в стурктуре, где все элементы одного размера, вставит область, чтобы потом обращаться к виджету не так
mov rax, [rax + 8]

а вот так?
mov rax, [rax + 10]

мне слабо вериться что компилятор будет так делать. откуда вы вообще взяли, что компилятор будет в структуре добавлять что-то?
11.09.2020 05:03 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
11.09.2020 05:13 UTC
0
потому что он выравнивает всё по 4 байта. если он будет делать это по другому, то будет не эффективная работа для получения следующего адреса. поэтому если в структуре все элементы равны 8 байтам, то компилятор врядли будет добавлять в структуру промежуток. всё ведет к эффективности работы. вот посмотрите как компилятор высчитывает следующий адрес в структуре.
0x0000555555558050 <+71>: lea rdx,[rax*8+0x0]
0x0000555555558058 <+79>: mov rax,QWORD PTR [rbp-0x118]
0x000055555555805f <+86>: add rax,rdx

всё просто и логично. каждый раз умножается на 8 индекс. если бы по вашему компилятор работал по другому, то такого цикла в принципе быть не могло, и тогда он каждый вызов будет писать по порядку. но разве есть такой компилятор?
11.09.2020 05:52 UTC
0
А если размер указателя, скажем, 4 байта, а выравнивание идет по 8?
11.09.2020 08:20 UTC
0
а такое может быть? а в каких случаях?
11.09.2020 09:22 UTC
0
Не знаю. Обычно указатели все же делают по размеру машинного слова.
11.09.2020 11:03 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
10.09.2020 13:20 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
10.09.2020 13:21 UTC
0
а как ты к виджетам будешь обращаться? не надоест высчитывать индекс виджета окна? через структуру правильней.
10.09.2020 14:03 UTC
+1
STUBS нужен для того, чтобы в функции set_theme, смотрите. там проверяется на NULL
Но вы не инициализируете его нулем, то есть в общем случае значение там может быть любое.
Да и потом, структура у вас ведь константная, что мешает использовать обычный sizeof?
Ах да, еще ведь у вас виджеты не сферические в вакууме, а выстроены в какую-то иерархию. Так может, проще пройтись по дереву?
я бы не стал пытаться сделать gtk приложение нормальное для windows
Вариантов я вижу три:
— полностью забить на поддержку «некошерной» платформы
— писать под каждую платформу код почти с нуля
— использовать кроссплатформенные библиотеки, а то, чего там нет, выносить в слой совместимости.
Собственно, я сейчас развлекаюсь именно с третьим способом: если уже есть кроссплатформенная gtk, почему я должен следовать вашему совету и лишаться этого преимущества?
10.09.2020 14:14 UTC
0
Но вы не инициализируете его нулем, то есть в общем случае значение там может быть любое.

не может оно быть любое. по соглашению всё что находится в глобальной области видимости при создании инициализируется нулем.
gtk, почему я должен следовать вашему совету и лишаться этого преимущества?

не я имел ввиду использовать по полной возможности ос. например в linux есть dbus, в windows есть реестр. вы можете из gtk в реест записывать данные? я например не знаю как это сделать. да и в принципе я в windows не программирую. немного пописал классические приложухи. вот например я в своем twitch боте сделал функцию такую, что можно audacious плеером управлять через чат. и это благодаря dbus. и то что вы приняли как за совет, я писал что это бы я так сделал. то есть это бы я писал для windows на этом, а для linux на этом.
10.09.2020 15:07 UTC
+2
Не знаю как вы, а я стараюсь исходить из задач. Задача «записать в реестр» редко бывает задачей сама по себе. Чаще это «сохранить настройку» и в общем-то неважно куда. А раз так, я ее с чистой совестью сохраню в домашний каталог пользователя с соответствующим именем.
Или dbus — он ведь нужен не сам по себе, а для вполне конкретного взаимодействия программ. Скажем, перетаскивание файлов из какого-то другого окна в наше. Если в линуксе это делается через dbus, а в винде через… winapi — значит, надо писать слой совместимости.
@alkresin
10.09.2020 13:22 UTC
+5
Gtk — это, конечно, хорошо, но вот на tutorial эта статья никак не тянет.
@Lsh
10.09.2020 14:47 UTC
+2
Печаль. Ничего против языка C не имею, но даже тут, на таком небольшом примере, сразу видно, что GUI на нём писать не нужно.
@
10.09.2020 16:00 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@iroln
10.09.2020 21:18 UTC
+1

GLib, GObject, GTK — это всё, конечно, наркомания высших порядков (если интересно, можно почитать пару статей про GObject). Однажды кому-то пришла в голову очень смелая мысль: почему бы не сделать систему с объекто-ориентированным дизайном на языке, который для этого совсем не предназначен. И они сделали. Не сказал бы, что результат трудов оказался негодным, скорее всего это венец того, что можно создать с помощью голого C, но писать на этом программы всё равно выходит совсем не простым делом. Если бы это было просто и удобно, такие вещи как Vala никогда не появились.


К чему я это? Да собственно к тому, что если не хочется писать GUI на Qt/C++ для Linux, возьмите хотя бы Vala, это может сэкономить много нервных клеток. :)

@negodnik
11.09.2020 09:40 UTC
0
(...) не хочу учить каждые три года стандарты C++, чтобы быть на волне.

Грустно