1. 使用 glade 新增对象及信号
前端:使用 glade 修改布局与信号。例如,在 glade 中新建对象 GtkTextBuffer,在将其 id 命名为 button2_data,内容为 “你好,中国!”。
设置构件按钮2点击信号的回调函数为 print_hello2,且在调用该函数时传入 GtkTextBuffer 对象类型的 button2_data 参数。
2. 实现代码与 glade 新增对象及信号的链接
后端:修改代码,实现前端修改的样式。理想结果为,点击按钮2,控制台出现“你好,中国!”。具体修改流程为:
1)绑定 glade 中设置的信号需要借用 gmodule 库的函数。前往 msys/mingw64.exe,使用如下代码查询 gmodule 包名称。
pkg-config --list-all | grep gmodule
2)修改配置文件,引入 gmodule 库。
cmake_minimum_required(VERSION 3.17)
get_filename_component(ProjectId ${CMAKE_CURRENT_SOURCE_DIR} NAME)
string(REPLACE " " "_" ProjectId ${ProjectId})
project(${ProjectId} C)
set(CMAKE_C_STANDARD 11)
## 使用 Cmake 找到 GTK 框架位置
FIND_PACKAGE(PkgConfig REQUIRED) # 声明使用pkg去找
PKG_CHECK_MODULES(GTK3 REQUIRED gtk+-3.0) # pkg 找到 gtk+-3.0的框架,并将其命名为 GTK3
# 引入 GTK 框架的头文件
INCLUDE_DIRECTORIES(${GTK3_INCLUDE_DIRS})
# 引入 GTK 框架的链接库
LINK_DIRECTORIES(${GTK3_LIBRARY_DIRS})
# 引入 GTK 编译时所需要的参数
ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER})
include_directories("include")
## 引入 gmodule
FIND_PACKAGE(PkgConfig REQUIRED) # 声明使用pkg去找
PKG_CHECK_MODULES(GMODULE REQUIRED gmodule-export-2.0) # pkg 找到 gmodule-export-2.0 的框架,并将其命名为 GMODULE
# 引入 gmodule 框架的头文件
INCLUDE_DIRECTORIES(${GMODULE_INCLUDE_DIRS})
# 引入 gmodule 框架的链接库
LINK_DIRECTORIES(${GMODULE_LIBRARY_DIRS})
## 编译.c
file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
foreach(file ${files})
get_filename_component(name ${file} NAME)
add_executable(${name} ${file})
# 将可执行程序与链接库进行链接(放置一起)
target_link_libraries(${name} ${GTK3_LIBRARIES})
target_link_libraries(${name} ${GMODULE_LIBRARIES}) # 将 GMODULE编译的可执行程序与链接库放置一起
endforeach()
3)修改相应代码,使代码能够获取到 glade 修改的 ui 文件中的布局及信号。
#include <gtk/gtk.h>
/*
* 定义一个打印函数,在点击按钮时调用该函数
* @param widget: 相当于GtkContainer 容器类的父类
* @param data: 用户点击按钮时传入的数据
*/
static void print_hello(GtkWidget *widget, char const *data){
g_print("hello: %s\n", data);
}
// 传入 GTKTextBuffer 类型数据
// 加 G_MODULE_EXPORT 相当于将函数名暴露出去,让 glade 可以识别到
G_MODULE_EXPORT void print_hello2(GtkWidget *widget, GtkTextBuffer *buffer){
// 获取 GtkTextBuffer 内容,可以选定获取buffer中某一段字符串
// 设置字符串起始位置
GtkTextIter start;
gtk_text_buffer_get_start_iter(buffer, &start);
// 获取字符串末尾位置
GtkTextIter end;
gtk_text_buffer_get_end_iter(buffer, &end);
// 获取 Buffer 内容,gtk_text_buffer_get_text 最后一个参数判断是否有隐藏字符
g_print("hello2: %s\n", gtk_text_buffer_get_text(buffer, &start, &end, FALSE));
}
// 给定 ui 文件,进行 helloworld 程序编写
int main(int argc, char **argv){
gtk_init(&argc, &argv);
// 1. 加载布局文件 builder.ui
// 新建builder
GtkBuilder *builder = gtk_builder_new();
// 利用 builder 加载布局文件
GError *error = NULL;
if(gtk_builder_add_from_file(builder, "builder2.ui", &error) == 0){ // 如果加载失败
g_printerr("Error loading files: %s\n", error->message);
g_clear_error(&error); // 退出之前不要忘记释放相应内存
return -1;
}
// 2. 获取布局文件中各构件,利用 ui 文件中的各构件 id 进行获取
// 获取窗口对象,其在 ui 文件中的 id 为 "window"
GObject *window = gtk_builder_get_object(builder, "window");
// 获取按钮1,按钮2,以及退出按钮
GObject *button1 = gtk_builder_get_object(builder, "button1");
GObject *button2 = gtk_builder_get_object(builder, "button2");
GObject *quit = gtk_builder_get_object(builder, "quit");
// 3. 对各构件进行事件的绑定
// 对窗口进行事件绑定
// destroy 相当于点击窗体右上角的 x 按钮,不绑定的情况下,点击 x 窗口消失,但主程序并不会退出,仍在后台运行
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 对按钮进行事件绑定
g_signal_connect(button1, "clicked", G_CALLBACK(print_hello), "button1");
g_signal_connect(quit, "clicked", G_CALLBACK(gtk_main_quit), NULL); // 绑定退出函数
// 绑定 glade 中设置的信号 (glade 设置的信号全部保存在了 ui 文件中)
// 第二个参数含义为传给 ui 文件中所有信号的一个参数,不同信号按理说应该接收的不一样,故此处设置为NULL
gtk_builder_connect_signals(builder, NULL);
// 4. 运行该窗口程序
gtk_main();
// 5. builder 解引用,让其可以释放内存。可以解引用的原因是已经利用builder加载了布局,获取了各构件
g_object_unref(builder);
return 0;
}
4)执行程序,验证修改结果: