十五、Gtk4-tfeapplication.c

ffeapplication.c包含了除tfetxtview.c和tfenotebook.c之外的所有代码。它:

  • 应用程序支持,主要处理命令行参数。
  • 使用ui文件构建构件。
  • 连接按钮信号及其处理程序。
  • 管理CSS。

main

函数main是C语言中第一个被调用的函数。它将用户给出的命令行与Gtk应用程序连接起来。

 1 #define APPLICATION_ID "com.github.ToshioCP.tfe"
 2
 3 int
 4 main (int argc, char **argv) {
 5   GtkApplication *app;
 6   int stat;
 7
 8   app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
 9
10   g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
11   g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
12   g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
13
14   stat =g_application_run (G_APPLICATION (app), argc, argv);
15   g_object_unref (app);
16   return stat;
17 }
  • 1:定义应用id。很容易找到应用程序id,而且比id嵌入gtk_application_new要好。
  • 8:创建GtkApplication对象。
  • 10-12:将"startup"、"activate"和"open"信号连接到它们的处理程序。
  • 14:运行应用。
  • 15-16:释放对应用程序的引用并返回状态。

startup signal handler

启动信号是在GtkApplication实例初始化之后发出的。信号处理程序需要做的是初始化应用程序。

  • 使用ui文件构建小构件。
  • 连接按钮信号及其处理程序。
  • 设置CSS。

处理程序如下所示。

 1 static void
 2 app_startup (GApplication *application) {
 3   GtkApplication *app = GTK_APPLICATION (application);
 4   GtkBuilder *build;
 5   GtkApplicationWindow *win;
 6   GtkNotebook *nb;
 7   GtkButton *btno;
 8   GtkButton *btnn;
 9   GtkButton *btns;
10   GtkButton *btnc;
11 
12   build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui");
13   win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win"));
14   nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb"));
15   gtk_window_set_application (GTK_WINDOW (win), app);
16   btno = GTK_BUTTON (gtk_builder_get_object (build, "btno"));
17   btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn"));
18   btns = GTK_BUTTON (gtk_builder_get_object (build, "btns"));
19   btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc"));
20   g_signal_connect_swapped (btno, "clicked", G_CALLBACK (open_cb), nb);
21   g_signal_connect_swapped (btnn, "clicked", G_CALLBACK (new_cb), nb);
22   g_signal_connect_swapped (btns, "clicked", G_CALLBACK (save_cb), nb);
23   g_signal_connect_swapped (btnc, "clicked", G_CALLBACK (close_cb), nb);
24   g_object_unref(build);
25 
26 GdkDisplay *display;
27 
28   display = gtk_widget_get_display (GTK_WIDGET (win));
29   GtkCssProvider *provider = gtk_css_provider_new ();
30   gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
31   gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
32 
33   g_signal_connect (win, "destroy", G_CALLBACK (before_destroy), provider);
34   g_object_unref (provider);
35 }
  • 12-15:使用ui文件(资源)构建小构件。用gtk_window_set_application连接顶级窗口和应用程序。
  • 16-23:获取按钮并连接它们的信号和处理程序。
  • 24:释放对GtkBuilder的引用。
  • 26 ~ 31:设置CSS。Gtk中的CSS类似于HTML中的CSS。你可以用CSS设置外边距、边框、内边距、颜色、字体等。在这个程序中,CSS在第30行。它设置GtkTextView的padding, font-family和font- size。
  • 26-28: GdkDisplay用于设置CSS。CSS将在下一小节中解释。
  • 33:连接主窗口上的destroy信号和before_destroy处理程序。下一小节将解释该处理程序。
  • 34:对于启动处理程序来说,provider是无用的,因此g_object_unref(provider)被调用。注意:这并不意味着provider的销毁。它由display引用,因此引用计数不为零。

CSS in Gtk

CSS是Cascading Style Sheet(层叠样式表)的缩写。它最初与HTML一起用于描述文档的表示语义。您可能已经发现Gtk中的小部件类似于浏览器中的窗口。这意味着CSS也可以应用于Gtk窗口系统。

CSS nodes, selectors

CSS的语法如下。

selector { color: yellow; padding-top: 10px; ...}

每个widget都有CSS node。例如,GtkTextView有textview节点。如果你想设置样式为GtkTextView,用“textview”代替选择器。

textview {color: yellow; ...}

类、ID和其他一些东西可以应用到选择器上,比如Web CSS。有关更多信息,请参阅GTK 4 API参考——GTK中的CSS。

在第30行,CSS是一个字符串。

textview {padding: 10px; font-family: monospace; font-size: 12pt;}
  • 内边距是边界和内容之间的空白。这个空格让textview更容易阅读。
  • font-family是字体的名称。“monospace”是一个通用的字体家族关键词。
  • font-size设置为12pt。

GtkStyleContext, GtkCSSProvider and GdkDisplay

GtkStyleContext是一个存储影响窗口组件的样式信息的对象。每个小构件都连接到相应的GtkStyleContext。可以通过gtk_widget_get_style_context获取context。

GtkCssProvider是一个解析CSS以设置窗口组件样式的对象。

要将CSS应用于窗口组件,您需要将GtkStyleProvider (GtkCSSProvider的接口)添加到GtkStyleContext。但是,相反,您可以将它添加到窗口的GdkDisplay(通常是顶层窗口)。

再次查看启动处理程序的源文件。

  • 28:通过gtk_widget_get_display获取display。
  • 29:创建一个GtkCssProvider实例。
  • 30:将CSS放入provider中。
  • 31:将provider添加到display中。gtk_style_context_add_provider_for_display的最后一个参数是样式提供程序的优先级。GTK_STYLE_PROVIDER_PRIORITY_APPLICATION是特定于应用程序的样式信息的优先级。也经常使用GTK_STYLE_PROVIDER_PRIORITY_USER,它是最高的优先级。因此,GTK_STYLE_PROVIDER_PRIORITY_USER通常用于特定的小构件。

可以将provider添加到GtkTextView的context而不是gdkdisplay中。为此,重写tfe_text_view_new。首先,获取TfeTextView对象的GtkStyleContext对象。然后将CSS提供程序添加到上下文。

GtkWidget *
tfe_text_view_new (void) {
  GtkWidget *tv;

  tv = gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);

  GtkStyleContext *context;

  context = gtk_widget_get_style_context (GTK_WIDGET (tv));
  GtkCssProvider *provider = gtk_css_provider_new ();
  gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
  gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);

  return tv;
}

context中的CSS优先于display中的CSS。

1 static void
2 before_destroy (GtkWidget *win, GtkCssProvider *provider) {
3   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (win));
4   gtk_style_context_remove_provider_for_display (display, GTK_STYLE_PROVIDER (provider));
5 }

当部件被销毁时,或者更准确地说,在其释放过程中,会发出一个“destroy”信号。before_destroy处理程序连接到主窗口中的信号。(参见app_startup的程序列表。)因此,它会在窗口被销毁时被调用。

处理程序从GdkDisplay中删除CSS提供程序。

activate and open handler

activate和open信号的处理程序分别是app_activate和app_open。他们只是创建了一个新的GtkNotebookPage。

 1 static void
 2 app_activate (GApplication *application) {
 3   GtkApplication *app = GTK_APPLICATION (application);
 4   GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));
 5   GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));
 6   GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
 7 
 8   notebook_page_new (nb);
 9   gtk_window_present (GTK_WINDOW (win));
10 }
11 
12 static void
13 app_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) {
14   GtkApplication *app = GTK_APPLICATION (application);
15   GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));
16   GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));
17   GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
18   int i;
19 
20   for (i = 0; i < n_files; i++)
21     notebook_page_new_with_file (nb, files[i]);
22   if (gtk_notebook_get_n_pages (nb) == 0)
23     notebook_page_new (nb);
24   gtk_window_present (GTK_WINDOW (win));
25 }
  • 1: app_activate。
  • 8-10:创建一个新页面并显示窗口。
  • 12-25:app_open。
  • 20-21:创建带有文件的notebook页面。
  • 22-23:如果没有创建页面,可能是因为读取错误,那么它将创建一个空页面。
  • 24:显示窗口。

多亏了tfenotebook.c和tfetextview.c,这些代码变得非常简单。

Primary instance

每个会话一次只能运行一个GApplication实例。会话是一个有点困难的概念,而且与平台有关,但粗略地说,它对应于图形桌面登录。当你使用你的电脑时,你可能先登录,然后你的桌面出现,直到你注销。这就是会议。

但是,Linux是多进程操作系统,可以运行同一个应用程序的两个或多个实例。这不是矛盾吗?

当第一个实例启动时,它用它的应用程序ID(例如com.github.ToshioCP.tfe)注册自己。在注册之后,启动信号被发出,然后激活或打开信号被发出,并且实例的主循环运行。我在前一小节中写了“启动信号是在应用程序实例初始化之后发出的”。更准确地说,它是在注册之后发出的。

如果调用具有相同应用程序ID的另一个实例,它也会尝试注册自己。因为这是第二个实例,ID的注册已经完成,所以它失败了。由于失败,启动信号没有发出。之后,activate或open信号在主实例中发出,而不是在第二个实例中。primary实例接收信号,并调用其处理程序。另一方面,第二个实例没有收到信号,它立即退出。

试着连续运行两个实例。

$ ./_build/tfe &
[1] 84453
$ ./build/tfe tfeapplication.c
$

首先,主实例打开一个窗口。然后,在第二个实例运行之后,一个包含tfeapplication.c内容的新的notebook页面将出现在主实例的窗口中。这是因为open信号是在主实例中发出的。第二个实例立即退出,因此shell提示符很快出现。

a series of handlers correspond to the button signals

 1 static void
 2 open_cb (GtkNotebook *nb) {
 3   notebook_page_open (nb);
 4 }
 5 
 6 static void
 7 new_cb (GtkNotebook *nb) {
 8   notebook_page_new (nb);
 9 }
10 
11 static void
12 save_cb (GtkNotebook *nb) {
13   notebook_page_save (nb);
14 }
15 
16 static void
17 close_cb (GtkNotebook *nb) {
18   notebook_page_close (GTK_NOTEBOOK (nb));
19 }

open_cb、new_cb、save_cb和close_cb只调用相应的notebook页面函数。

meson.build

 1 project('tfe', 'c')
 2 
 3 gtkdep = dependency('gtk4')
 4 
 5 gnome=import('gnome')
 6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
 7 
 8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')
 9 
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)

在这个文件中,只修改了源文件名,而不是以前的版本。

source files

您可以从存储库下载这些文件。有两种选择。

  • 使用git并克隆。
  • 运行浏览器并打开顶部页面。然后点击“代码”按钮,在弹出菜单中点击“下载ZIP”。之后,解压缩归档文件。

如果使用git,运行终端并输入以下命令。

$ git clone https://github.com/ToshioCP/Gtk4-tutorial.git

源文件在/src/tfe5目录下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值