七、Gtk4-Defining a final class

news2025/1/4 19:28:20

1 定义一个最终类

1.1 一个非常简单的编辑器

在上一节中,我们创建了一个非常简单的文件查看器。现在我们继续重写它,并将其转换为非常简单的编辑器。它的源文件是tfe目录下的tfe1.c(文本文件编辑器1)。

GtkTextView是一个多行编辑器。因此,我们不需要从头开始编写编辑器。我们只需要在文件查看器中添加两件事:

  • 指向GFile实例的指针。
  • 文本保存功能。

有两种方法可以存储指针。

  • 使用全局变量
  • 创建一个GtkTextView的子类,它的每个实例都保存一个指向GFile实例的指针。

使用全局变量很容易实现。定义一个指向GFile的指针数组。例如,

GFile * f [20];

变量f[i]对应于与第i个GtkNotebookPage关联的文件。

然而,有两个问题。第一个是数组的大小。如果用户提供了太多的参数(在上面的例子中超过20个),则不可能存储所有指向GFile实例的指针。二是程序维护困难。到目前为止,我们有一个小程序。但是,你开发的程序越多,它的规模就越大。一般来说,在大型程序中维护全局变量是非常困难的。在检查全局变量时,需要检查使用该变量的所有代码。

从维护的角度来看,创建子类是个好主意。我们更喜欢它而不是一个全局变量。

请注意,我们考虑的是“子类”,而不是“子构件”。子类和子部件完全不同。类是GObject系统的一个术语。如果你不熟悉GObject,请参阅:

  • GObject API reference
  • GObject tutorial for beginners

子类继承了父类的一切,而且还扩展了它的性能。我们将TfeTextView定义为GtkTextView的一个子类。它拥有GtkTextView拥有的一切,并添加了一个指向GFile的指针。

在这里插入图片描述

1.2 如何定义一个GtkTextView的子类

你需要了解GObject系统约定。首先,看看下面的程序。

#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)

struct _TfeTextView
{
  GtkTextView parent;
  GFile *file;
};

G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);

static void
tfe_text_view_init (TfeTextView *tv) {
}

static void
tfe_text_view_class_init (TfeTextViewClass *class) {
}

void
tfe_text_view_set_file (TfeTextView *tv, GFile *f) {
  tv -> file = f;
}

GFile *
tfe_text_view_get_file (TfeTextView *tv) {
  return tv -> file;
}

GtkWidget *
tfe_text_view_new (void) {
  return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
}
  • TfeTextView分为两部分。Tfe和TextView。Tfe称为前缀(prefix)或命名空间(namespace)。TextView被称为object。
    有三种不同的标识符模式。TfeTextView(驼峰式大小写),tfe_text_view(经常用于函数)和TFE_TEXT_VIEW(用于将对象转换为TfeTextView)。
  • 首先,定义TFE_TYPE_TEXT_VIEW宏为tfe_text_view_get_type()。名称总是(prefix)TYPE(object),并且字母都是大写。替换文本总是(prefix)_(object)_get_type(),字母都是小写。该定义放在G_DECLARE_FINAL_TYPE宏之前。
  • G_DECLARE_FINAL_TYPE宏的参数是驼峰式的子类名、小写带下划线、前缀(大写)、对象(大写带下划线)和父类名(驼峰式)。在该宏的扩展中声明了下列两个C语言结构。
    • typedef struct _TfeTextView TfeTextView
    • typedef struct {GtkTextViewClass parent_class;} TfeTextViewClass;
      这些声明告诉我们TfeTextView和TfeTextViewClass是C结构体。“TfeTextView”有两个含义,类名和C结构名。C语言结构体TfeTextView被称为object。类似地,TfeTextViewClass被称为class。
  • 声明结构体_TfeTextView。下划线是必要的。第一个成员是父对象(C结构)。注意,这不是一个指针,而是对象本身。第二个成员和后面的成员是子对象的成员。TfeTextView结构有一个指向GFile实例的指针作为成员。
  • G_DEFINE_TYPE宏。参数是子对象名称(驼峰大小写),带有下划线的小写和父对象类型(prefix)TYPE(module)。这个宏主要用于向类型系统注册新类。类型系统是GObject的基础系统。每个类都有自己的类型。GObject、GtkWidget和TfeTextView的类型分别是G_TYPE_OBJECT、GTK_TYPE_WIDGET和TFE_TYPE_TEXT_VIEW。这样的类型(例如,TFE_TYPE_TEXT_VIEW)是一个宏,它被扩展为一个函数(tfe_text_view_get_type())。它返回一个在所有GObject系统类中唯一的整数。
  • 实例init函数(tfe_text_view_init)在创建实例时被调用。它与其他面向对象语言中的构造函数相同。
    类init函数(tfe_text_view_class_init)在创建类时被调用。
  • 两个函数tfe_text_view_set_file和tfe_text_view_get_file是公共函数。公共函数是开放的,你可以在任何地方调用它们。它们与其他面向对象语言中的public方法相同。tv是一个指向TfeTextView对象(C结构)的指针。它有一个成员file,它是被tv->file指向。
  • TfeTextView实例创建函数是tfe_text_view_new。它的名字是(prefix)_(object)_new。它使用g_object_new函数创建实例。参数是(prefix)TYPE(object)、初始化属性的列表和NULL。NULL是属性列表的结束标记。这里没有初始化任何属性。返回值转换为GtkWidget。

这个程序展示了如何定义子类的大纲。

1.3 Close-request信号

假设您正在使用这个编辑器。首先,带着参数运行编辑器。参数是文件名。编辑器读取文件并显示一个包含文件文本的窗口。然后编辑文本。完成编辑后,退出编辑器。编辑器会在窗口关闭前更新文件。

GtkWindow在关闭之前发出"close-request"信号。我们将在before_close之前连接信号和处理程序。(处理程序是一个连接到信号的C函数。)before_close函数会在发出"close-request"信号时被调用。

g_signal_connect (win, "close-request"G_CALLBACK (before_close)NULL);

参数win是一个GtkApplicationWindow,其中定义了"close-request"信号,before_close是处理程序。G_CALLBACK转换对处理程序是必要的。before_close的程序如下所示。

 1 static gboolean
 2 before_close (GtkWindow *win, GtkWidget *nb) {
 3   GtkWidget *scr;
 4   GtkWidget *tv;
 5   GFile *file;
 6   char *pathname;
 7   GtkTextBuffer *tb;
 8   GtkTextIter start_iter;
 9   GtkTextIter end_iter;
10   char *contents;
11   unsigned int n;
12   unsigned int i;
13 
14   n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));
15   for (i = 0; i < n; ++i) {
16     scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i);
17     tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
18     file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));
19     tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
20     gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
21     contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
22     if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {
23       if ((pathname = g_file_get_path (file)) != NULL) {
24         g_printerr ("Can't save %s.", pathname);
25         g_free (pathname);
26       } else
27         g_printerr ("Pathname not exist.\n");
28     }
29     g_free (contents);
30     g_object_unref (file);
31   }
32   return FALSE;
33 }

项目左边的数字是源代码中的行号。

  • 14:将nb的总页数赋给n。
  • 15-31:针对每个页面的索引进行for循环。
  • 16-18: scr、tv和file被赋值给指向GtkScrolledWindow、TfeTextView和GFile的指针。当app_open处理程序被调用时,TfeTextView的GFile会被赋值(set)。稍后展示。
    19-21: tb被分配给TfeTextView的GtkTextBuffer。使用迭代器访问缓冲区。迭代器指向缓冲区中的某个位置。函数gtk_text_buffer_get_bounds将缓冲区的开始和结束分别分配给start_iter和end_iter。然后函数gtk_text_buffer_get_text返回start_iter和end_iter之间的文本,即缓冲区中的整个文本。
  • 22 ~ 28:文本保存到文件中。如果失败,则显示错误消息。
  • 29:释放存储内容的内存。
  • 30: GFile此时已经没用。g_object_unref减少GFile的引用计数。引用计数将在后面一节中解释。在这个程序中,引用计数将为0,GFile实例将销毁自己。(TfeTextView类没有定了处理file内存的函数,所以要自己unref)

1.4 Source code of tfe1.c

/* filename: tfe1.c */
  1 #include <gtk/gtk.h>
  2 
  3 /* Define TfeTextView Widget which is the child class of GtkTextView */
  4 
  5 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
  6 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
  7 
  8 struct _TfeTextView
  9 {
 10   GtkTextView parent;
 11   GFile *file;
 12 };
 13 
 14 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
 15 
 16 static void
 17 tfe_text_view_init (TfeTextView *tv) {
 18   tv->file = NULL;
 19 }
 20 
 21 static void
 22 tfe_text_view_class_init (TfeTextViewClass *class) {
 23 }
 24 
 25 void
 26 tfe_text_view_set_file (TfeTextView *tv, GFile *f) {
 27   tv->file = f;
 28 }
 29 
 30 GFile *
 31 tfe_text_view_get_file (TfeTextView *tv) {
 32   return tv -> file;
 33 }
 34 
 35 GtkWidget *
 36 tfe_text_view_new (void) {
 37   return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
 38 }
 39 
 40 /* ---------- end of the definition of TfeTextView ---------- */
 41 
 42 static gboolean
 43 before_close (GtkWindow *win, GtkWidget *nb) {
 44   GtkWidget *scr;
 45   GtkWidget *tv;
 46   GFile *file;
 47   char *pathname;
 48   GtkTextBuffer *tb;
 49   GtkTextIter start_iter;
 50   GtkTextIter end_iter;
 51   char *contents;
 52   unsigned int n;
 53   unsigned int i;
 54 
 55   n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));
 56   for (i = 0; i < n; ++i) {
 57     scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i);
 58     tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
 59     file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));
 60     tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
 61     gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
 62     contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
 63     if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {
 64       if ((pathname = g_file_get_path (file)) != NULL) {
 65         g_printerr ("Can't save %s.", pathname);
 66         g_free (pathname);
 67       } else
 68         g_printerr ("Pathname not exist.\n");
 69     }
 70     g_free (contents);
 71     g_object_unref (file);
 72   }
 73   return FALSE;
 74 }
 75 
 76 static void
 77 app_activate (GApplication *app) {
 78   g_print ("You need to give filenames as arguments.\n");
 79 }
 80 
 81 static void
 82 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
 83   GtkWidget *win;
 84   GtkWidget *nb;
 85   GtkWidget *lab;
 86   GtkNotebookPage *nbp;
 87   GtkWidget *scr;
 88   GtkWidget *tv;
 89   GtkTextBuffer *tb;
 90   char *contents;
 91   gsize length;
 92   char *filename;
 93   int i;
 94 
 95   win = gtk_application_window_new (GTK_APPLICATION (app));
 96   gtk_window_set_title (GTK_WINDOW (win), "file editor");
 97   gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
 98 
 99   nb = gtk_notebook_new ();
100   gtk_window_set_child (GTK_WINDOW (win), nb);
101 
102   for (i = 0; i < n_files; i++) {
103     if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {
104       scr = gtk_scrolled_window_new ();
105       tv = tfe_text_view_new ();
106       tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
107       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
108       gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
109 
110       tfe_text_view_set_file (TFE_TEXT_VIEW (tv),  g_file_dup (files[i]));
111       gtk_text_buffer_set_text (tb, contents, length);
112       g_free (contents);
113       filename = g_file_get_basename (files[i]);
114       lab = gtk_label_new (filename);
115       gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
116       nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
117       g_object_set (nbp, "tab-expand", TRUE, NULL);
118       g_free (filename);
119     } else if ((filename = g_file_get_path (files[i])) != NULL) {
120         g_print ("No such file: %s.\n", filename);
121         g_free (filename);
122     } else
123         g_print ("No valid file is given\n");
124   }
125   if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) {
126     g_signal_connect (win, "close-request", G_CALLBACK (before_close), nb);
127     gtk_window_present (GTK_WINDOW (win));
128   } else
129     gtk_window_destroy (GTK_WINDOW (win));
130 }
131 
132 int
133 main (int argc, char **argv) {
134   GtkApplication *app;
135   int stat;
136 
137   app = gtk_application_new ("com.github.ToshioCP.tfe1", G_APPLICATION_HANDLES_OPEN);
138   g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
139   g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
140   stat =g_application_run (G_APPLICATION (app), argc, argv);
141   g_object_unref (app);
142   return stat;
143 }
  • 110: TfeTextView的GFile指针设置为files[i],这是一个用命令行参数创建的GFile。但是GFile稍后会被系统销毁。所以它需要在赋值之前被复制。g_file_dup复制GFile文件。
  • 126:“close-reques”信号连接到before_close处理程序。第四个参数被称为“用户数据”,它将是信号处理程序的第二个参数。因此,nb被赋给before_close作为第二个参数。

现在是时候编译和运行了。

$ cd src/tfe
$ comp tfe1
$ ./a.out taketori.txt`.

修改内容并关闭窗口。确保文件已被修改。

现在我们有了一个非常简单的编辑器。这并不明智。我们需要更多的功能,如打开,保存,保存,更改字体等。我们将在下一节和之后添加它们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java学习day71(乐友商城)购物车实现

今日目标&#xff1a; 1.实现未登录状态的购物车 2.实现登陆状态下的购物车 1.搭建购物车服务 1.1.创建module 1.2.pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi&…

软件测试~测试分类

目录 1.按照是否查看代码划分 ① 黑盒测试(Black-box Testing) ② 白盒测试(White-box Testing) ③ 灰盒测试(Gray-Box Testing) 2.按照开发阶段划分 ① 单元测试(Unit Testing) ② 集成测试(Integration Testing) ③ 系统测试(System Testing) ④ 验收测试(Acceptance…

kNN分类

一、 概述 kNN(k nearest neighbor,k近邻)是一种基础分类算法&#xff0c;基于“物以类聚”的思想&#xff0c;将一个样本的类别归于它的邻近样本。 ![在这里插入图片描述] 二、算法描述 1.基本原理 给定训练数据集 T{(x1,y1),(x2,y2),...,(xN,yN)}T\left\{ \left( x_1,y_1 …

17. XML

文章目录一、XML概念二、XML语法1、基础语法2、快速入门3、组成部分4、约束1. 约束概述2. 分类3. DTD4. Schema三、XML解析1、操作xml文档2、 解析xml的方式1. DOM2. SAX3. xml常见的解析器&#xff08;工具包&#xff09;4. Jsoup&#xff08;1&#xff09;快速入门&#xff0…

VUE3 学习笔记(一):环境配置、项目创建

一、首先需要安装node.jsnodejs官网&#xff1a;Node.js (nodejs.org)下载安装包&#xff1a;下载稳定版本即可&#xff0c;目前&#xff08;2023-01-07&#xff09;是18.13.0版本c. 检查当前版本&#xff08;CMD&#xff09;&#xff1a;至此&#xff0c;nodejs已经安装成功&a…

电力系统机组组合(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

EM算法学习总结

序 这个和我的硕士毕业论文的题目就有一定关系&#xff0c;我的导师让我按时向她汇报学习进度。然而我还在进行实习&#xff0c;还要准备自己明年的秋招&#xff0c;只能想办法游走于三者之间。 EM算法是一个常用的数据挖掘算法&#xff0c;想必从事数据挖掘的相关工作的同学…

机器人操作系统ROS/ROS2(1)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、ROS和ROS2是什么&#xff1f;二、ROS2安装步骤1.Ubuntu虚拟机安装2.ROS2系统安装3.验证ROS2安装成功4.小海龟仿真示例总结前言 提示&#xff1a;这里可以添…

LeetCode刷题模版:21 - 30

目录 简介21. 合并两个有序链表22. 括号生成23. 合并K个升序链表24. 两两交换链表中的节点25. K 个一组翻转链表26. 删除有序数组中的重复项27. 移除元素28. 找出字符串中第一个匹配项的下标29. 两数相除【未理解】30. 串联所有单词的子串【未理解】结语简介 Hello! 非常感谢您…

devops 是什么东东了

DevOps&#xff0c;字面意思是Development &Operations的缩写。 DevOps是从实践中逐步总结提炼出的方法论理念。近而创造了DevOps这个词。 DevOps概念的萌芽阶段&#xff1a; 2008年敏捷大会上&#xff0c;来自Patrick Debois发表了题为 《Agile Infrastructure & Op…

基于.NET技术的动漫管理系统,给各位二次元老司机提供的动漫管理平台,基于.NET的前后端框架Blazor,含安装教程及使用说明

介绍 老司机驾驶舱——给各位二次元老司机提供的动漫管理平台。 下载地址&#xff1a;基于.NET技术的动漫管理系统 编写本应用的目的主要是实践巨硬&#xff08;Microsoft&#xff09;的新一代前端框架Blazor与EntityFramework&#xff0c;本应用也给想要学习这套框架的童鞋提…

Ubuntu20.04 hyperledger fabric2.4基于Docker搭建blockchain-explorer

准备 启动fabric测试网络。   这里默认已经完成了Fabric测试网络搭建以及运行。   后续会出Fabric安装&#xff0c;现在不会的就先去看别的博客 配置 1.在test-network 文件夹下面建立explorer文件夹&#xff1a; mkdir explorer2. 配置文件 2.1下载配置文件 先进文件…

Isaac Sim 机器人仿真器介绍、安装与 Docker [1]

前言与参考 此文书写于&#xff1a; January 6, 2023&#xff0c; 更新于 January 6, 2023 &#xff1b;可能会随着时间的变化 此教程会有过时概念哦 Isaac Sim 相关参考链接&#xff1a; 官方文档地址官方docker image 镜像地址官方讨论 论坛链接&#xff0c;建议没啥事就可…

【JavaGuide面试总结】Java基础篇·中

【JavaGuide面试总结】Java基础篇中1.重载和重写有什么区别&#xff1f;2.什么是可变长参数&#xff1f;3.为什么浮点数运算的时候会有精度丢失的风险&#xff1f;4.如何解决浮点数运算的精度丢失问题&#xff1f;5.超过 long 整型的数据应该如何表示&#xff1f;6.基本类型和包…

JSP——标准标签库 (JSTL)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;JAVA开发者…

5G NR标准: 第19章 毫米波射频技术

第19章 毫米波射频技术 现有的 2G、3G 和 4G 移动通信 3GPP 规范适用于 6 GHz 以下的频率范围&#xff0c;相应的 RF 要求考虑了与 6 GHz 以下操作相关的技术方面。 NR 也在这些频率范围内运行&#xff08;标识为频率范围 1&#xff09;&#xff0c;但另外还将定义为在 24.25…

【人工智能原理自学】梯度下降和反向传播:能改

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;笔记来自B站UP主Ele实验室的《小白也能听懂的人工智能原理》。 &#x1f514;本文讲解梯度下降和反向传播&#xff1a;能改&#xff0c;一起卷起来叭&#xff01; 目录一、“挪…

Rollup Decentralization

1. 引言 当前的Rollup为中心化的&#xff0c;这并不是必须的&#xff0c;而是当前的选择。 2. 何为Rollup&#xff1f; Rollup与其它L2或侧链的主要区别在于&#xff1a; L1数据可用性 Rollup&#xff1a;只要L1的数据可用性存在&#xff0c;用户可重构L2状态&#xff0c;然…

SQL LIKE 操作符

LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。 SQL LIKE 操作符 LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。 SQL LIKE 语法 SELECT column1, column2, ... FROM table_name WHERE column LIKE pattern; 参数说明&#xff1a; column1, column2, ...&…

php://filter伪协议(总结)

文章目录php://filter伪协议总结php://filter伪协议介绍php://filter伪协议使用方法php://filter过滤器分类filter字符串过滤器string.rot13string.toupperstring.tolowerstring.strip_tagsfilter转换过滤器convert.base64-encodeconvert.base64-decodeconvert.quoted-printabl…