当前位置: 首页 > news >正文

二十二、Gtk4-ListView

GTK 4添加了新的列表对象GtkListView、GtkGridView和GtkColumnView。这个新特性在Gtk API参考—列表小构件概述中有描述。

GTK 4还有其他实现列表的方法。它们是GtkListBox和GtkTreeView,它们是从GTK 3接管的。在Gtk开发博客中有一篇关于Matthias Clasen所写的列表小构件的文章。他描述了为什么开发GtkListView来取代GtkListBox和GtkTreeView。

Outline

列表是一种顺序数据结构。例如,有序字符串序列"one", “two”, “three”, "four"就是一个列表。每个元素都称为item。列表类似于数组,但在很多情况下,它是由指向列表下一项的指针实现的。它有一个起始点。因此,每个元素都可以通过索引来引用(第一元素,第二元素,…,第n个元素,…)。有两种情况。一个是从1开始的索引(以1为基数),另一个是从0开始的索引(以0为基数)。

Gio提供GListModel接口。它是一个从零开始的列表,它的元素都是相同类型的GObject后代对象,或者实现相同接口的对象。实现了GListModel的对象不是小构件。因此,列表不会直接显示在屏幕上。还有一个对象GtkListView,它是一个显示列表的小构件。列表中的项目需要连接到GtkListView中的项目。GtkListItemFactory实例将列表中的项目映射到GListView。

在这里插入图片描述

GListModel

如果你想用GListModel建立一个包含字符串的列表,例如"one", “two”, “three”, “four”,请注意,字符串不能是列表中的项。因为GListModel是GObject对象的列表,而字符串不是GObject对象。“GObject”这个词在这里的意思是“GObject类或其子类”。所以,你需要一个包装器,它是一个GObject,包含一个字符串。GtkStringObject是包装器对象,而实现了GListModel的GStringList是GtkStringObject的一个列表。还有一个是GtkDirectoryList(下面会讲到)。

  • GStringList
  • GtkDirectoryList
char *array[] = {"one", "two", "three", "four", NULL};
GtkStringList *stringlist = gtk_string_list_new ((const char * const *) array);

函数gtk_string_list_new创建一个GtkStringList对象。它的项是gtkstringgobject对象,包含字符串"one", “two”, “three"和"four”。有一些函数可以向列表中添加项或从列表中删除项。

  • gtk_string_list_append将一个元素添加到列表
  • gtk_string_list_remove从列表表删除一个元素
  • gtk_string_list_get_string取得列表中的一个字符串

有关更多信息,请参阅GTK 4 API参考——GtkStringList。

其他列表对象将在后面解释。

用xml格式创建GStringList模型。

<object class="GtkDropDown"><property name="model"><object class="GtkStringList"><items><item translatable="yes">test1</item><item translatable="yes">test2</item><item translatable="yes">test3</item></items></object></property><binding name="selected"><lookup name="out1" type="VpfCameraList"><lookup name="item">GtkListItem</lookup></lookup></binding>
</object>

GtkSelectionModel

GtkSelectionModel是一个支持选择的接口。有了这个模型,用户可以通过点击来选择项目。它由GtkMultiSelection、GtkNoSelection和GtkSingleSelection对象实现。这三个对象通常足以构建应用程序。它们是用GListModel创建的。你也可以单独创建它们,然后添加一个GListModel。

  • GtkMultiSelection支持多选。
  • GtkNoSelection不支持选择。当需要GtkSelectionModel时,它是GListModel的包装器。
  • GtkSingleSelection支持单选。
GtkNoSelection *gtk_no_selection_new (GListModel *model);
GtkSingleSelection *gtk_single_selection_new (GListModel *model);
GtkMultiSelection *gtk_multi_selection_new (GListModel *model);

GtkListView

GtkListView是一个显示GListModel item的构件。GtkListView使用GtkListItem表示列表模型中的项。但是,GtkListItem本身不是一个构件,因此用户需要将一个构件(例如GtkLabel)设置为GtkListItem的子构件,以显示list模型的一个项目。GtkListItem的"item"属性指向一个属于list模型的对象。
在这里插入图片描述
如果物品的数量非常大,例如超过1000个,GtkListItem会被回收并连接到另一个新显示的物品。这种循环使得gtklisttem对象的数量相当少,小于200。这对于抑制内存消耗的增长非常有效,因此GListModel可以包含很多元素,例如超过100万个元素。

每一项item都是一个独立的GtkListItem,下面例子中就是一个GtkListView中有四个Item(GtkListItem)

GtkListItemFactory

GtkListItemFactory创建或回收GtkListItem,并将其连接到list model的一个item。这个工厂有两个子类,GtkSignalListItemFactory和GtkBuilderListItemFactory。

GtkSignalListItemFactory

GtkSignalListItemFactory为用户提供配置gtklisttitem对象的信号。有四个信号。

  • 发出“setup”是为了设置GtkListItem对象。用户在处理程序中设置它的子部件。例如,创建一个GtkLabel部件并将gtklisttitem的child属性设置为它。即使gtklisttitem实例被回收(用于绑定到GListModel的另一项),这个设置也会保持不变。
  • "bind"是为了将列表模型中的项绑定到小部件上。例如,用户从GtkListItem实例的"item"属性中获取物品。然后获取该项的字符串,并使用该字符串设置GtkLabel实例的label属性。当GtkListItem新创建、回收或列表项发生某些更改时,就会发出此信号。
  • "unbind"是为了解除一个元素的绑定。用户在信号处理程序中撤销第2步中所做的一切。如果在第2步中创建了一些对象,那么它们必须被销毁。
  • “teardown”用来撤销第一步中完成的所有操作。因此,必须销毁步骤1中创建的widget。在这个信号之后,list元素将被销毁。

下面的程序list1.c给出了字符串"one"、“two”、"three"和"four"的列表。使用GtkNoSelection,因此用户不能选择任何项目。

 1 #include <gtk/gtk.h>2 3 static void4 setup_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {5   GtkWidget *lb = gtk_label_new (NULL);6   gtk_list_item_set_child (listitem, lb);7   /* Because gtk_list_item_set_child sunk the floating reference of lb, releasing (unref) isn't necessary for lb. */8 }9 
10 static void
11 bind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
12   GtkWidget *lb = gtk_list_item_get_child (listitem);
13   /* Strobj is owned by the instance. Caller mustn't change or destroy it. */
14   GtkStringObject *strobj = gtk_list_item_get_item (listitem);
15   /* The string returned by gtk_string_object_get_string is owned by the instance. */
16   gtk_label_set_text (GTK_LABEL (lb), gtk_string_object_get_string (strobj));
17 }
18 
19 static void
20 unbind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
21   /* There's nothing to do here. */
22 }
23 
24 static void
25 teardown_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
26   /* There's nothing to do here. */
27   /* GtkListItem instance will be destroyed soon. You don't need to set the child to NULL. */
28 }
29 
30 static void
31 app_activate (GApplication *application) {
32   GtkApplication *app = GTK_APPLICATION (application);
33   GtkWidget *win = gtk_application_window_new (app);
34   gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
35   GtkWidget *scr = gtk_scrolled_window_new ();
36   gtk_window_set_child (GTK_WINDOW (win), scr);
37 
38   char *array[] = {
39     "one", "two", "three", "four", NULL
40   };
41   /* sl is owned by ns */
42   /* ns and factory are owned by lv. */
43   /* Therefore, you don't need to care about their destruction. */
44   GtkStringList *sl =  gtk_string_list_new ((const char * const *) array);
45   GtkNoSelection *ns =  gtk_no_selection_new (G_LIST_MODEL (sl));
46 
47   GtkListItemFactory *factory = gtk_signal_list_item_factory_new ();
48   g_signal_connect (factory, "setup", G_CALLBACK (setup_cb), NULL);
49   g_signal_connect (factory, "bind", G_CALLBACK (bind_cb), NULL);
50   /* The following two lines can be left out. The handlers do nothing. */
51   g_signal_connect (factory, "unbind", G_CALLBACK (unbind_cb), NULL);
52   g_signal_connect (factory, "teardown", G_CALLBACK (teardown_cb), NULL);
53 
54   GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);
55   gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
56   gtk_window_present (GTK_WINDOW (win));
57 }
58 
59 /* ----- main ----- */
60 #define APPLICATION_ID "com.github.ToshioCP.list1"
61 
62 int
63 main (int argc, char **argv) {
64   GtkApplication *app;
65   int stat;
66 
67   app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
68 
69   g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
70 
71   stat =g_application_run (G_APPLICATION (app), argc, argv);
72   g_object_unref (app);
73   return stat;
74 }

文件list1.c位于目录src/misc下。请在下面创建一个shell脚本并将其保存到bin目录,例如$HOME/bin。

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

将当前目录更改为包含list1.c和type的目录,如下所示。

$ chmod 755 $HOME/bin/comp # or chmod 755 (your bin directory)/comp
$ comp list1
$ ./a.out

在这里插入图片描述
这个程序并不难。如果您感到有些困难,请再次阅读本节,特别是GtkSignalListItemFactory小节。

GtkBuilderListItemFactory

GtkBuilderListItemFactory是另一个gtklisttitemfactory。其行为是用ui文件定义的。

<interface><template class="GtkListItem"><property name="child"><object class="GtkLabel"><binding name="label"><lookup name="string" type="GtkStringObject"><lookup name="item">GtkListItem</lookup></lookup></binding></object></property></template>
</interface>

模板标签用于定义GtkListItem。它的子属性是GtkLabel对象。工厂看到这个模板并创建GtkLabel并设置GtkListItem的子属性。这与GtkSignalListItemFactory的设置处理程序所做的相同。

然后,将GtkLabel的label属性绑定到gtkstringgobject的string属性。string对象引用GtkListItem的item属性。所以,lookup标签是这样的:

label <- string <- GtkStringObject <- item <- GtkListItem

最后一个查找标签有一个内容GtkListItem。通常,像GtkListItem这样的C类型不会出现在标签的内容中。这是一个特例。Matthias Clasen在GTK开发博客中有解释。

请记住,ui模板中的classname (GtkListItem)用作“this”指针,指向正在实例化的对象。

因此,在求值时,使用GtkListItem实例作为lookup标记的this对象。这个对象将在第后面章节解释。

C语言源代码如下。其名称是list2.c,位于src/misc目录下。

 1 #include <gtk/gtk.h>2 3 char *4 get_file_name (GtkListItem *item, GFileInfo *info) {5   return G_IS_FILE_INFO (info) ? g_strdup (g_file_info_get_name (info)) : NULL;6 }7 8 static void9 app_activate (GApplication *application) {
10   GtkApplication *app = GTK_APPLICATION (application);
11   gtk_window_present (gtk_application_get_active_window(app));
12 }
13 
14 static void
15 app_startup (GApplication *application) {
16   GtkApplication *app = GTK_APPLICATION (application);
17   GtkWidget *win = gtk_application_window_new (app);
18   gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
19   GtkWidget *scr = gtk_scrolled_window_new ();
20   gtk_window_set_child (GTK_WINDOW (win), scr);
21 
22   GFile *file = g_file_new_for_path (".");
23   GtkDirectoryList *dl = gtk_directory_list_new ("standard::name", file);
24   g_object_unref (file);
25   GtkNoSelection *ns = gtk_no_selection_new (G_LIST_MODEL (dl));
26 
27   const char *ui_string =
28 "<interface>"
29   "<template class=\"GtkListItem\">"
30     "<property name=\"child\">"
31       "<object class=\"GtkLabel\">"
32         "<binding name=\"label\">"
33           "<closure type=\"gchararray\" function=\"get_file_name\">"
34             "<lookup name=\"item\">GtkListItem</lookup>"
35           "</closure>"
36         "</binding>"
37       "</object>"
38     "</property>"
39   "</template>"
40 "</interface>"
41 ;
42   GBytes *gbytes = g_bytes_new_static (ui_string, strlen (ui_string));
43   GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);
44 
45   GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);
46   gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
47 }
48 
49 /* ----- main ----- */
50 #define APPLICATION_ID "com.github.ToshioCP.list3"
51 
52 int
53 main (int argc, char **argv) {
54   GtkApplication *app;
55   int stat;
56 
57   app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
58 
59   g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
60   g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
61 
62   stat =g_application_run (G_APPLICATION (app), argc, argv);
63   g_object_unref (app);
64   return stat;
65 }

GtkBulderListItemFactory不需要信号处理程序。使用GtkSingleSelection,因此用户可以一次选择一个项目。

因为这是一个小程序,所以ui数据是一个字符串。

GtkDirectoryList

GtkDirectoryList是一个包含GFileInfo对象的列表模型,GFileInfo对象是某一目录下文件的信息。它使用g_file_enumerate_children_async()来获取GFileInfo对象。链表模型由gtk_directory_list_new函数创建。
ui数据(上面的xml数据)用于在运行时构建GListItem模板。GtkBuilder根据符号表查找函数get_file_name。

GtkDirectoryList *gtk_directory_list_new (const char *attributes, GFile *file);

attributes是用逗号分隔的文件属性列表。文件属性是键值对。键由命名空间和名称组成。例如,“standard::name"键是一个文件的名称。“标准”指一般档案资料。“名称”指的是文件名。下表给出了一些例子。
请添加图片描述
当前目录为”.”。下面的程序使GtkDirectoryList dl及其内容成为当前目录下的GFileInfo对象。

GFile *file = g_file_new_for_path (".");
GtkDirectoryList *dl = gtk_directory_list_new ("standard::name", file);
g_object_unref (file);

通过修改前一小节中的list2.c来编写文件列表程序并不困难。一个问题是GInfoFile没有属性。Lookup标签查找属性,因此它对于从GFileInfo对象中查找文件名是无用的。在这种情况下,闭包标签是合适的。闭包标记指定一个函数以及函数返回值的类型。

const char *
get_file_name (GtkListItem *item, GFileInfo *info) {return G_IS_FILE_INFO (info) ? g_strdup (g_file_info_get_name (info)) : NULL;
}
... ...
... ..."<interface>""<template class=\"GtkListItem\">""<property name=\"child\">""<object class=\"GtkLabel\">""<binding name=\"label\">""<closure type=\"gchararray\" function=\"get_file_name\">""<lookup name=\"item\">GtkListItem</lookup>""</closure>""</binding>""</object>""</property>""</template>"
"</interface>"
  • 字符串“gchararray”是一个类型名称。类型“gchar”是一个类型名称,它与C类型“char”相同。因此,“gchararray”是“char类型的数组”,与string类型相同。它用于获取GValue对象的类型。GValue是一个泛型值,它可以包含各种类型的值。例如,类型名可以是gboolean、gchar (char)、gint (int)、gfloat (float)、gdouble (double)、gchararray (char *)等。这些类型名是注册到类型系统的基本类型的名称。请参阅GObject教程。
  • 闭包标签有type属性和function属性。Function attribute指定函数名,type属性指定函数返回值的类型。闭包标签的内容(它位于<closure…></closure>)是函数的参数。<lookup name="item">GtkListItem</lookup>返回GtkListItem的item属性的值这将是函数的第二个参数。第一个参数总是this对象,即GListItem实例。
  • gtk_file_name函数是闭包标记的回调函数。它首先检查info参数。因为当GListItem元素无界时,它可以为NULL。如果是GFileInfo,则返回复制的文件名。因为g_file_info_get_name的返回值(文件名)是由GFileInfo对象拥有的。因此,需要复制字符串以将所有权授予调用者。绑定标签将GtkLabel的“label”属性绑定到闭包标签。

整个程序(list3.c)如下所示。该程序位于src/misc目录中。

 1 #include <gtk/gtk.h>2 3 char *4 get_file_name (GtkListItem *item, GFileInfo *info) {5   return G_IS_FILE_INFO (info) ? g_strdup (g_file_info_get_name (info)) : NULL;6 }7 8 static void9 app_activate (GApplication *application) {
10   GtkApplication *app = GTK_APPLICATION (application);
11   gtk_window_present (gtk_application_get_active_window(app));
12 }
13 
14 static void
15 app_startup (GApplication *application) {
16   GtkApplication *app = GTK_APPLICATION (application);
17   GtkWidget *win = gtk_application_window_new (app);
18   gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
19   GtkWidget *scr = gtk_scrolled_window_new ();
20   gtk_window_set_child (GTK_WINDOW (win), scr);
21 
22   GFile *file = g_file_new_for_path (".");
23   GtkDirectoryList *dl = gtk_directory_list_new ("standard::name", file);
24   g_object_unref (file);
25   GtkNoSelection *ns = gtk_no_selection_new (G_LIST_MODEL (dl));
26 
27   const char *ui_string =
28 "<interface>"
29   "<template class=\"GtkListItem\">"
30     "<property name=\"child\">"
31       "<object class=\"GtkLabel\">"
32         "<binding name=\"label\">"
33           "<closure type=\"gchararray\" function=\"get_file_name\">"
34             "<lookup name=\"item\">GtkListItem</lookup>"
35           "</closure>"
36         "</binding>"
37       "</object>"
38     "</property>"
39   "</template>"
40 "</interface>"
41 ;
42   GBytes *gbytes = g_bytes_new_static (ui_string, strlen (ui_string));
43   GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);
44 
45   GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);
46   gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
47 }
48 
49 /* ----- main ----- */
50 #define APPLICATION_ID "com.github.ToshioCP.list3"
51 
52 int
53 main (int argc, char **argv) {
54   GtkApplication *app;
55   int stat;
56 
57   app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
58 
59   g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
60   g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
61 
62   stat =g_application_run (G_APPLICATION (app), argc, argv);
63   g_object_unref (app);
64   return stat;
65 }

通常,链接器使用符号表将对象链接到可执行文件。它包括函数名及其位置。链接器通常不会在创建的可执行文件中放入符号表。但是如果给出了--export-dynamic选项,链接器会将符号表添加到可执行文件中。

为此,C编译器提供了一个选项-Wl,--export-dynamic

  • -Wl是一个C编译器选项,它将以下选项传递给链接器。
  • --export-dynamic是一个链接器选项。以下内容摘自链接器文档。“当创建动态链接的可执行文件时,将所有符号添加到动态符号表中。动态符号表是运行时动态对象可见的符号集合。”

编译然后执行

$ gcc -Wl,--export-dynamic `pkg-config --cflags gtk4` list3.c `pkg-config --libs gtk4`

你也可以制作shell脚本编译list3.c

gcc -Wl,--export-dynamic `pkg-config --cflags gtk4` $1.c `pkg-config --libs gtk4`

将这一行文件保存到文件comp中,然后将其复制到$HOME/bin并赋予其可执行权限。

$ cp comp $HOME/bin/comp
$ chmod +x $HOME/bin/comp
$ comp list3
$ ./a.out

在这里插入图片描述

http://www.lryc.cn/news/1431.html

相关文章:

  • ASP.NET Core3.1实战教程---基于Jquery单文件上传
  • 10 卷积神经网络CNN(基础篇)
  • Windows下LuaBridge2.8的环境配置及简单应用
  • 每天10个前端小知识 【Day 10】
  • 【LeetCode】1223. 掷骰子模拟
  • SPSS数据分析软件的安装与介绍(附网盘链接)
  • 2022年38女神节大促美妆、珠宝、母婴、保健电商数据回顾
  • Java笔记-线程同步
  • 通过python 调用OpenAI api_key提交问题解答
  • 图表控件LightningChart .NET再破世界纪录,支持实时可视化 1 万亿个数据点
  • 什么是响应性?
  • 黑马】后台管理176-183
  • Typescript - 类型守卫(typeof / in / instanceof / 自定义类型保护的类型谓词)通俗易懂详细教程
  • 6.8 左特征向量
  • 10个自动化测试框架,测试工程师用起来
  • 城市C友会【官方牵头更多的线下交流的机会,你有怎样的期待?】
  • CSDN 编程竞赛二十七期题解
  • RMI攻击中的ServerClient相互攻击反制
  • 值类型和引用类型
  • 后端开发必懂nginx面试40问
  • Redis为什么这么快?
  • 几种实现主题切换的方式
  • Jenkins使用(代码拉取->编译构建->部署上线)
  • IEEE期刊论文投稿前期准备
  • [AAAI 2022] TransFG: A Transformer Architecture for Fine-grained Recognition
  • 机器学习之决策树原理详解、公式推导(手推)、面试问题、简单实例(python实现,sklearn调包)
  • 一文搞懂CAS实现原理——怀玉
  • typora每次复制文档都要附带图片文件夹?学会配置gitee图床
  • Linux--gdb
  • c++11 标准模板(STL)(std::multimap)(二)