二、Gtk4-GtkApplication and GtkApplicationWindow

1 GtkApplication

1.1 GtkApplication and g_application_run

人们编写编程代码来开发应用程序。什么是应用程序?应用程序是使用库运行的软件,其中包括操作系统、框架等。在GTK 4编程中,GTK应用程序是使用GTK库运行的程序(或可执行程序)。

编写GtkApplication的基本方法如下。

  • 创建一个GtkApplication实例。
  • 运行这个实例。

这是所有。非常简单。下面是代表上述方法的C代码。

 1 #include <gtk/gtk.h>
 2 
 3 int
 4 main (int argc, char **argv) {
 5   GtkApplication *app;
 6   int stat;
 7 
 8   app = gtk_application_new ("com.github.ToshioCP.pr1", G_APPLICATION_DEFAULT_FLAGS);
 9   stat =g_application_run (G_APPLICATION (app), argc, argv);
10   g_object_unref (app);
11   return stat;
12 }
13 

第一行说明这个程序包含Gtk库的头文件。函数main是C语言中的一个启动函数。变量app被定义为一个指向GtkApplication实例的指针。函数gtk_application_new创建一个GtkApplication实例,并返回一个指向该实例的指针。GtkApplication实例是一个C结构体数据,其中存储了有关应用程序的信息。后面会解释这些参数的含义。函数g_application_run运行实例定义的应用程序。(我们经常说这个函数运行app。实际上,app不是一个应用程序,而是一个指向该应用程序实例的指针。但是,它简单而简短,可能不会造成混淆。)

这里我使用了单词instance。实例、类和对象是面向对象编程中的术语。我用同样的方式使用这些词。但是,在本教程中,我将经常使用“对象”而不是“实例”。这意味着“对象”和“实例”是相同的。对象是一个有点模棱两可的词。从广义上讲,对象比实例具有更广泛的含义。因此,读者应注意语境,以找到“对象”的含义。在很多情况下,object和instance是相同的。

要编译它,需要运行以下命令。字符串pr1.c是上述C源代码的文件名。

gcc `pkg-config --cflags gtk4` pr1.c `pkg-config --libs gtk4`

./a.out
(a.out:13533): GLib-GIO-WARNING **: 15:30:17.449: Your application does not implement
g_application_activate() and has no handlers connected to the “activate” signal.
It should do one of these.

哦,它只是产生一个错误信息。这个错误消息意味着GtkApplication对象运行了,毫无疑问。现在,让我们想想这条消息意味着什么。

1.2 信号

这个消息告诉我们:

  1. 应用程序GtkApplication没有实现g_application_activate(),
  2. 它没有连接到“activate”信号的处理程序,而且
  3. 你需要解决其中至少一个问题。

这两个error的原因与信号有关。我先给你们解释一下。

当有事情发生时,就会发出信号。例如,创建窗口,销毁窗口,等等。当应用被激活时,会发出"activate"信号。(Activated和started有点不同,但你可以认为到目前为止两者几乎是一样的。)如果信号连接到一个函数,该函数被称为信号处理程序或简单地称为处理程序,那么在信号发出时将调用该函数。

流程是这样的:

  1. 一些事情发生。
  2. 如果它与某个信号相关,那么这个信号就会被发射出来。
  3. 如果信号预先连接到某个处理程序,则调用该处理程序。

信号在对象中定义。例如,“activate”信号属于GApplication对象,它是GtkApplication对象的父对象(注意GApplication和GtkApplication区别)。

GApplication对象是GObject对象的子对象。GObject是所有对象层次结构中的顶层对象。

GObject -- GApplication -- GtkApplication
<---parent                      --->child

子对象从父对象继承信号、函数、属性等。因此,GtkApplication也有“activate”信号。

现在我们可以解决pr1.c中的问题。我们需要将“activate”信号连接到一个处理程序。我们使用函数g_signal_connect将信号连接到处理程序。

 1 #include <gtk/gtk.h>
 2 
 3 static void
 4 app_activate (GApplication *app, gpointer *user_data) {
 5   g_print ("GtkApplication is activated.\n");
 6 }
 7 
 8 int
 9 main (int argc, char **argv) {
10   GtkApplication *app;
11   int stat;
12 
13   app = gtk_application_new ("com.github.ToshioCP.pr2", G_APPLICATION_DEFAULT_FLAGS);
14   g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
15   stat =g_application_run (G_APPLICATION (app), argc, argv);
16   g_object_unref (app);
17   return stat;
18 }
19 

首先,我们定义处理程序app_activate,它只是显示一条消息。函数g_print是在GLib中定义的,它类似于C标准库中的printf。在函数main中,我们在g_application_run之前添加g_signal_connect。函数g_signal_connect有4个参数。

  • 信号所属的实例。
  • 信号的名称。
  • 一个处理程序函数(也称为callback),需要用G_CALLBACK强制转换。
  • 传递给处理程序的数据。如果没有必要的数据,则应该给出NULL。

在GObject API参考中有描述。正确地说,g_signal_connect是一个宏(不是C函数)。

#define g_signal_connect(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)

你可以在API参考手册中找到每个信号的描述。例如,“activate”信号位于GIO API参考中的GApplication部分。

void
activate (
  GApplication* self,
  gpointer user_data
)

这是一个“activate”信号处理程序的声明。你可以使用任何名称来代替上面声明中的“activate”。参数有:

  • self是信号所属的实例。
  • user_data是g_signal_connect函数的第四个参数中定义的数据。如果它是NULL,那么你可以忽略并省略第二个参数。

API参考手册非常重要。你应该看到并理解它。

让我们编译上面的源文件(pr2.c)并运行它。

$ gcc `pkg-config --cflags gtk4` pr2.c `pkg-config --libs gtk4`
$ ./a.out
GtkApplication is activated.
$

好的,做得好。然而,你可能已经注意到,输入这么长的代码行来编译是很痛苦的。使用shell脚本来解决这个问题是个好主意。创建一个文本文件,其中包含下面一行。

gcc `pkg-config --cflags gtk4` $1.c `pkg-config --libs gtk4`

然后,将其保存在$HOME/bin目录下,通常是/ HOME/(用户名)/bin。(如果用户名为James,则目录为/home/james/bin)。然后打开文件的执行位。如果文件名是comp,这样做:

$ chmod 755 $HOME/bin/comp
$ ls -log $HOME/bin
    ...  ...  ...
-rwxr-xr-x 1   62 May 23 08:21 comp
    ...  ...  ...

如果这是第一次创建$HOME/bin目录并保存文件,则需要注销并重新登录。

$ comp pr2
$ ./a.out
GtkApplication is activated.
$

2 GtkWindow and GtkApplicationWindow

2.1 GtkWindow

在前面的小节中打印了一条消息“GtkApplication is activated.”。就gtk应用程序的测试而言,它是很好的。然而,由于Gtk是图形用户界面(GUI)的框架,因此它是不够的。现在我们继续在这个程序中添加一个窗口。我们需要做的是:

  • 创建一个GtkWindow。
  • 将它连接到GtkApplication。
  • 显示窗口。

现在重写函数app_activate。

创建一个GtkWindow

1 static void
2 app_activate (GApplication *app, gpointer user_data) {
3   GtkWidget *win;
4 
5   win = gtk_window_new ();
6   gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
7   gtk_window_present (GTK_WINDOW (win));
8 }

Widget是一个抽象的概念,它包括所有GUI界面,如窗口、对话框、按钮、多行文本、容器等。GtkWidget是所有GUI对象的派生对象的基类。

parent <-----> child
GtkWidget -- GtkWindow

GtkWindow顶部成员就是GtkWidget

在这里插入图片描述
函数gtk_window_new的定义如下。

GtkWidget *
gtk_window_new (void);

根据这个定义,它返回一个指向GtkWidget的指针,而不是GtkWindow。它实际上创建了一个新的GtkWindow实例(不是GtkWidget),但返回了一个指向GtkWidget的指针。但是,该指针指向GtkWidget,同时它也指向包含GtkWidget的GtkWindow。

如果您想将win用作指向GtkWindow类型实例的指针,则需要对其进行强制转换。

(GtkWindow *) win

它可以工作,但通常不被使用。相反,使用GTK_WINDOW宏。

GTK_WINDOW (win)

推荐使用宏,因为它不仅转换,还检查类型。

连接win到GtkApplication

函数gtk_window_set_application用于将GtkWindow连接到GtkApplication。

gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));

您需要用GTK_WINDOW宏把win转换到GtkWindow型指针,用GTK_WINDOW宏把app转换到GtkApplication型指针。

GtkApplication会继续运行,直到相关的窗口被销毁。如果你没有连接GtkWindow和GtkApplication, GtkApplication会立即销毁自己。因为没有窗口连接到GtkApplication,所以GtkApplication不需要等待任何东西。因为它会销毁自己,所以GtkWindow也会被销毁。

显示窗口

函数gtk_window_present将窗口呈现给用户(显示给用户)。

GTK4 将默认的小部件可见性更改为on,因此每个小部件都不需要将其更改为on。但是,有一个例外。顶层窗口(稍后解释)在创建时是不可见的。所以你需要使用上面的函数来显示窗口。

你可以使用gtk_widget_set_visible (win, true)代替gtk_window_present。但是这两者的行为是不同的。假设屏幕上有两个windows win1和win2, win1在win2后面。两个窗口都是可见的。函数gtk_widget_set_visible (win1, true)什么也不做,因为win1已经是可见的。所以win1仍然落后于win2。另一个函数gtk_window_present (win1)将win1移动到windows栈的顶部。因此,如果想显示窗口,应该使用gtk_window_present。

两个函数gtk_widget_show和gtk_widget_hide自GTK 4.10以来已被弃用。您应该使用gtk_widget_set_visible.

将程序保存为pr3.c,然后编译并运行它。

保存程序为pr3.c,这时编译运行它:

$ comp pr3
$ ./a.out

一个小窗口将会出现。

在这里插入图片描述
单击关闭按钮,然后窗口消失,程序结束。

2.2 GtkApplicationWindow

GtkApplicationWindow是GtkWindow的一个子对象。它有一些额外的功能,可以更好地与GtkApplication集成。建议将它用作应用程序的顶层窗口,而不是GtkWindow。

现在重写程序并使用GtkApplicationWindow。

1 static void
2 app_activate (GApplication *app, gpointer user_data) {
3   GtkWidget *win;
4 
5   win = gtk_application_window_new (GTK_APPLICATION (app));
6   gtk_window_set_title (GTK_WINDOW (win), "pr4");
7   gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
8   gtk_window_present (GTK_WINDOW (win));
9 }

当您创建GtkApplicationWindow时,您需要提供GtkApplication实例作为参数。然后它自动连接这两个实例。所以你不需要再调用gtk_window_set_application了。

该程序设置窗口的标题和默认大小。编译并运行a.out,你会看到一个标题为"pr4"的大窗口。

在这里插入图片描述
参考:Gtk4-tutorial sec3: GtkApplication and GtkApplicationWindow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值