GTK+3.0实战:用C语言打造你的第一个跨平台GUI应用(附完整代码)

图形界面编程:使用C语言开发GUI应用 在代码中,我们通过`gtk_builder_get_object()`函数获取窗口和按钮对象,并使用`g_signal_connect()`函数将按钮的点击事件连接到`gtk_main_quit()`函数,以实现点击按钮时退出应用的功能。请注意,这只是一个简单的示例,以演示使用GtkBuilder创建GUI界面的基本原理。在实际开发中,您可以通过UI文件来设计更复杂的界面,使用更多的控件和布局容器,以满足您的需求。- GTK+:跨平台的GUI库,提供了丰富的功能和易于使用的API。 阅读详情

GTK+3.0实战:用C语言打造你的第一个跨平台GUI应用(附完整代码)

如果你已经熟悉了C语言的命令行世界,看着那些黑底白字的终端输出,是否曾想过用自己熟悉的语言,为程序披上一件图形化的“外衣”?在很多人印象里,C语言似乎总是与底层系统、高性能计算绑定,而图形界面(GUI)开发似乎是Python、Java甚至JavaScript的专属领域。这种看法其实是一种误解。C语言凭借其卓越的性能和跨平台潜力,配合成熟的GUI工具包,完全能够构建出高效、稳定且界面美观的桌面应用。今天,我们就来亲手打破这层隔阂,聚焦于GTK+ 3.0这个久经沙场的跨平台GUI库,从零开始,一步步用C语言搭建起你的第一个图形窗口。这不仅是一次编程实践,更是一次思维模式的拓展——你会发现,用C语言操控像素和事件,其乐趣和成就感丝毫不亚于解决一个复杂的算法问题。

我们的目标非常明确:写一个真正能跑起来的、跨Linux和Windows双平台的GTK应用。我不会只给你一个孤零零的“Hello World”窗口代码就结束,而是会带你走完从环境搭建、代码编写、编译运行到解决跨平台兼容性问题的完整闭环。过程中,你会遇到一些典型的“坑”,比如库的链接、路径问题、运行时依赖等,别担心,这正是实战的意义所在。我们将一起填平这些坑,让你最终收获的不仅是一段可以运行的代码,更是一套可复用的、解决实际问题的开发方法论。

1. 环境准备:为跨平台开发铺平道路

在动手写第一行代码之前,一个稳定且配置正确的开发环境是成功的基石。与使用自带GUI库的高级语言不同,C语言需要我们自己引入外部库。GTK+是一套庞大的工具包,包含了窗口、按钮、文本框等众多控件,以及处理用户输入、绘制图形的事件系统。因此,第一步就是把它“请”到我们的开发机器上。

1.1 Linux环境下的GTK+安装

对于大多数Linux发行版,安装GTK+开发库是最简单直接的。得益于强大的包管理系统,我们通常只需要几条命令。这里以Ubuntu/Debian系和Fedora/RHEL系为例。

Ubuntu 22.04 LTS / Debian 11 及更新版本: 打开终端,执行以下命令来安装GTK+3的开发文件、编译工具和pkg-config(一个帮助我们自动获取编译和链接参数的小工具):

sudo apt update
sudo apt install build-essential libgtk-3-dev pkg-config

build-essential 提供了GCC编译器、make等基础构建工具。libgtk-3-dev 则是GTK+3库的开发包,包含了头文件和链接库。安装完成后,你可以通过 pkg-config --modversion gtk+-3.0 命令来验证安装的GTK版本。

Fedora 36 / RHEL 9 及类似系统: 使用dnf包管理器进行安装:

sudo dnf install gcc make gtk3-devel pkg-config

过程与Debian系类似,只是包名略有不同。

提示:如果你使用的是Arch Linux,可以使用 sudo pacman -S gtk3 base-devel 来安装。不同发行版的包管理器命令和包名是第一个需要适应的小差异,但这恰恰体现了Linux世界的多样性。

1.2 Windows环境下的GTK+安装

在Windows上配置GTK+环境,传统方法稍显复杂,但如今有了更便捷的选择。我们不推荐手动下载dll和头文件进行配置,那容易导致路径混乱。这里介绍两种主流方法:

方法一:使用MSYS2(推荐) MSYS2提供了一个类Unix的Shell环境和强大的pacman包管理器,是Windows上进行C/C++开发,尤其是涉及开源库时的利器。

  1. 访问 MSYS2官网 下载并安装。
  2. 打开 MSYS2 UCRT64 终端(这个环境对64位应用支持最好)。
  3. 更新包数据库并安装GTK+3:
    pacman -Syu
    pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-gtk3 mingw-w64-ucrt-x86_64-pkg-config
    
    这样,编译器、GTK库和pkg-config就一次性装好了。后续的编译命令将在MSYS2 UCRT64终端中执行。

方法二:使用vcpkg包管理器 如果你已经在使用Visual Studio进行C++开发,vcpkg是一个优秀的跨平台C++库管理工具,它也完美支持纯C项目和GTK。

  1. 克隆并安装vcpkg(如果尚未安装)。
  2. 在PowerShell或CMD中,安装GTK3:
    .\vcpkg install gtk:x64-windows
    
  3. vcpkg会自动处理依赖,并生成一个triplet文件指导CMake或Visual Studio进行集成。对于命令行编译,你需要使用vcpkg提供的工具链文件。

为了直观对比两种Windows配置方式的优劣,可以参考下表:

特性MSYS2vcpkg
环境独立性高,自包含的类Unix环境中,需与VS或CMake集成
安装便捷性简单,几条命令即可中等,需先安装vcpkg本身
与原生Windows集成较低,主要在MSYS2终端内工作高,可直接用于Visual Studio项目
包管理范围非常广泛,涵盖大量开源Unix工具专注于C/C++库,生态庞大
推荐适用场景希望获得接近Linux开发体验,快速上手已在使用Visual Studio,希望统一管理项目依赖

对于纯粹的初学者,我强烈建议从MSYS2开始,它的学习曲线更平缓,能让你更专注于GTK和C语言本身,而不是纠缠于Windows特有的构建系统。

2. 第一个GTK窗口:从“Hello World”到理解事件循环

环境就绪,让我们立刻开始创造。第一个程序的目标是显示一个窗口。别小看这个简单的窗口,它背后蕴含着GUI程序的核心运行机制——事件驱动模型。

2.1 编写基础窗口代码

创建一个名为 first_window.c 的文件,并输入以下代码:

#include <gtk/gtk.h>

/* 回调函数:当窗口接收到“destroy”信号时被调用 */
static void on_window_closed(GtkWidget *widget, gpointer user_data) {
    g_print("窗口即将关闭,退出主循环。\n");
    gtk_main_quit(); // 退出GTK主循环,程序将结束
}

int main(int argc, char *argv[]) {
    GtkWidget *window; // 声明一个指向GtkWidget的指针,它将代表我们的窗口

    /* 初始化GTK库。
     * 必须在使用任何其他GTK函数前调用,它会解析命令行参数(如果有)。
     */
    gtk_init(&argc, &argv);

    /* 创建一个新的顶层窗口。
     * GTK_WINDOW_TOPLEVEL 表示这是一个标准的、可管理、带边框和标题栏的窗口。
     */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    /* 设置窗口标题 */
    gtk_window_set_title(GTK_WINDOW(window), "我的第一个GTK+ 3.0应用");

    /* 设置窗口的默认大小(宽度,高度),单位是像素 */
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);

    /* 连接信号(Signal)与回调函数(Callback)。
     * 这是GTK事件驱动的核心。
     * “destroy”信号在窗口被销毁时发出(例如用户点击了关闭按钮)。
     * G_CALLBACK宏确保我们的函数以正确的格式被调用。
     * NULL是传递给回调函数的用户数据,这里我们不需要。
     */
    g_signal_connect(window, "destroy", G_CALLBACK(on_window_closed), NULL);

    /* 显示窗口及其包含的所有子控件。
     * 在调用这个函数前,窗口在内存中创建但不可见。
     */
    gtk_widget_show_all(window);

    /* 启动GTK主循环。
     * 这个函数会阻塞在这里,持续监听用户输入(鼠标、键盘)、窗口系统事件,
     * 并调用相应的回调函数进行处理,直到gtk_main_quit()被调用。
     */
    gtk_main();

    return 0;
}

这段代码虽然短小,但五脏俱全。我们来拆解几个关键点:

  • gtk_init: 所有GTK应用的起点,负责内部初始化。
  • gtk_window_new: 创建窗口对象。在GTK中,几乎所有你能看到的东西(窗口、按钮、标签)都是一个GtkWidget或其子类。
  • g_signal_connect: GUI编程的精髓。用户的操作(点击、关闭、输入)或系统的状态变化,都被抽象为“信号”。我们通过这个函数告诉GTK:“当这个窗口发出‘destroy’信号时,请调用我的on_window_closed函数”。这种模式将界面与逻辑解耦,非常清晰。
  • gtk_main: 程序的心脏。它启动了一个无限循环,等待并分发事件。你的应用在此时才真正“活”起来,开始响应用户。

2.2 编译与运行:跨越平台的第一道命令

编写完代码,下一步是把它变成可执行文件。这里就需要用到之前安装的pkg-config工具了。它就像一个智能的配置助手,能自动为你生成正确的编译器(--cflags)和链接器(--libs)参数。

在Linux或MSYS2终端中,执行编译命令:

gcc -o first_window first_window.c `pkg-config --cflags --libs gtk+-3.0`

分解一下这个命令:

  • gcc: C语言编译器。
  • -o first_window: 指定输出的可执行文件名为 first_window。
  • first_window.c: 我们的源代码文件。
  • 反引号 `pkg-config --cflags --libs gtk+-3.0`: 这是关键。Shell会先执行反引号内的命令,并将其输出(即GTK+3所需的编译链接参数)替换到原命令中。你可以单独运行 pkg-config --cflags --libs gtk+-3.0 看看它输出了什么,通常是一系列 -I(指定头文件路径)和 -l(指定链接库)参数。

编译成功后,会在当前目录生成 first_window(Linux/MSYS2)或 first_window.exe(Windows)文件。直接在终端运行它:

./first_window  # Linux/MSYS2
first_window.exe # Windows CMD (需在MSYS2 UCRT64终端外运行时,确保GTK DLL在PATH中)

一个标题为“我的第一个GTK+ 3.0应用”、大小为400x300的窗口应该会弹出来。点击关闭按钮,终端会打印“窗口即将关闭,退出主循环。”,然后程序结束。

注意:在Windows上,如果你在MSYS2 UCRT64终端外直接双击exe运行,可能会报错“找不到libgtk-3-0.dll”。这是因为可执行文件需要GTK的动态链接库。解决方法是将MSYS2中GTK的bin目录(例如 C:\msys64\ucrt64\bin)添加到系统的PATH环境变量中,或者将必要的dll复制到exe同级目录。在MSYS2终端内运行则无此问题,因为终端环境已配置好PATH。

3. 构建交互式界面:按钮、布局与用户反馈

一个只有关闭功能的窗口显然不够。让我们为其添加一些交互元素:一个按钮和一个标签,点击按钮,标签的文字会改变。这涉及到两个新概念:控件(Widget) 和布局容器(Container)。

3.1 使用GtkBox进行垂直布局

GTK使用容器来管理子控件的排列。GtkBox是一种简单的线性布局容器,可以水平或垂直排列子控件。我们来创建一个垂直排列的界面。

新建文件 interactive_app.c:

#include <gtk/gtk.h>

/* 声明全局指针,以便在回调函数中访问标签控件。
 * 在实际大型项目中,应使用更优雅的数据传递方式(如user_data参数)。
 */
static GtkWidget *label;

/* 按钮点击的回调函数 */
static void on_button_clicked(GtkWidget *widget, gpointer user_data) {
    // 修改标签的文本
    gtk_label_set_text(GTK_LABEL(label), "你好,世界!按钮已被点击!");
    g_print("按钮被点击,标签已更新。\n");
}

static void on_window_closed(GtkWidget *widget, gpointer user_data) {
    gtk_main_quit();
}

int main(int argc, char *argv[]) {
    GtkWidget *window;
    GtkWidget *vbox; // 垂直布局容器
    GtkWidget *button;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "交互式示例");
    gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
    g_signal_connect(window, "destroy", G_CALLBACK(on_window_closed), NULL);

    /* 创建一个垂直的GtkBox容器,设置间距为6像素。
     * TRUE 表示子控件在容器内均匀分配空间(同质)。
     * 这里我们设为TRUE,让标签和按钮能适当拉伸。
     */
    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    // 将容器添加到窗口作为其子控件。在GTK+3中,窗口本身就是一个容器。
    gtk_container_add(GTK_CONTAINER(window), vbox);

    /* 创建一个标签控件,显示初始文本 */
    label = gtk_label_new("等待按钮点击...");
    // 设置标签在容器内的对齐方式(水平居中,垂直居中)
    gtk_widget_set_halign(label, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
    // 将标签添加到vbox容器中。FALSE, FALSE参数表示不扩展、不填充。
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

    /* 创建一个带文本的按钮控件 */
    button = gtk_button_new_with_label("点击我!");
    // 连接按钮的“clicked”信号到我们的回调函数
    g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), NULL);
    // 将按钮也添加到vbox容器中
    gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);

    /* 显示所有控件 */
    gtk_widget_show_all(window);

    gtk_main();
    return 0;
}

编译并运行:

gcc -o interactive_app interactive_app.c `pkg-config --cflags --libs gtk+-3.0`
./interactive_app

现在你会看到一个窗口,上方是标签“等待按钮点击...”,下方是一个按钮“点击我!”。点击按钮,标签的文字立刻改变。这个简单的例子演示了:

  1. 创建控件:gtk_label_new, gtk_button_new_with_label。
  2. 使用容器布局:gtk_box_new 创建盒子,gtk_box_pack_start 将控件装入盒子。
  3. 信号连接的完整流程:用户点击按钮(发出clicked信号)→ 触发on_button_clicked回调函数 → 函数内部调用gtk_label_set_text改变另一个控件的状态。

3.2 更复杂的布局:使用GtkGrid

GtkBox适合线性排列,但对于更复杂的表单、网格状界面,GtkGrid是更好的选择。它允许你将控件放置在一个灵活的网格中。

假设我们要创建一个简单的登录界面,包含“用户名”、“密码”标签和输入框,以及“登录”和“取消”按钮。使用GtkGrid可以轻松对齐这些元素。

// ... (省略头文件和关闭回调函数)

int main(int argc, char *argv[]) {
    GtkWidget *window, *grid;
    GtkWidget *label_user, *entry_user;
    GtkWidget *label_pass, *entry_pass;
    GtkWidget *btn_login, *btn_cancel;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "登录界面示例");
    gtk_window_set_default_size(GTK_WINDOW(window), 350, 200);

    g_signal_connect(window, "destroy", G_CALLBACK(on_window_closed), NULL);

    /* 创建一个4行2列的网格,设置行和列之间的间距 */
    grid = gtk_grid_new();
    gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
    gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
    // 设置网格内容与窗口边框的边距
    gtk_container_set_border_width(GTK_CONTAINER(grid), 15);
    gtk_container_add(GTK_CONTAINER(window), grid);

    // 第一行:用户名标签和输入框
    label_user = gtk_label_new("用户名:");
    gtk_widget_set_halign(label_user, GTK_ALIGN_END); // 右对齐
    gtk_grid_attach(GTK_GRID(grid), label_user, 0, 0, 1, 1); // 列0, 行0, 占1列1行

    entry_user = gtk_entry_new();
    gtk_grid_attach(GTK_GRID(grid), entry_user, 1, 0, 1, 1); // 列1, 行0

    // 第二行:密码标签和输入框
    label_pass = gtk_label_new("密码:");
    gtk_widget_set_halign(label_pass, GTK_ALIGN_END);
    gtk_grid_attach(GTK_GRID(grid), label_pass, 0, 1, 1, 1);

    entry_pass = gtk_entry_new();
    gtk_entry_set_visibility(GTK_ENTRY(entry_pass), FALSE); // 设置为密码不可见模式
    gtk_grid_attach(GTK_GRID(grid), entry_pass, 1, 1, 1, 1);

    // 第三行:留空或分隔,第四行:按钮
    // 创建一个水平盒子来容纳两个按钮,并让它们右对齐
    GtkWidget *button_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
    gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_END);
    gtk_grid_attach(GTK_GRID(grid), button_box, 0, 3, 2, 1); // 跨两列

    btn_cancel = gtk_button_new_with_label("取消");
    btn_login = gtk_button_new_with_label("登录");
    gtk_container_add(GTK_CONTAINER(button_box), btn_cancel);
    gtk_container_add(GTK_CONTAINER(button_box), btn_login);

    // 为按钮连接信号(这里省略具体的登录逻辑)
    g_signal_connect_swapped(btn_cancel, "clicked", G_CALLBACK(gtk_widget_destroy), window);
    // g_signal_connect(btn_login, "clicked", G_CALLBACK(on_login_clicked), NULL);

    gtk_widget_show_all(window);
    gtk_main();
    return 0;
}

gtk_grid_attach 函数是布局的关键,其参数为 (grid, child, column, row, width, height)。通过精细控制每个控件占据的网格位置,你可以构建出几乎任何复杂的界面布局。GTK还提供了GtkFixed(绝对定位)、GtkPaned(可调整分割区域)等多种容器,足以应对各种UI设计需求。

4. 进阶实战:构建一个简易文本编辑器

现在,让我们综合运用所学,打造一个功能更丰富的应用——一个具备打开、编辑、保存基本功能的简易文本编辑器。这个项目将涉及更多控件:菜单栏、工具栏、多行文本视图(GtkTextView)、文件对话框(GtkFileChooserDialog)以及状态栏。

4.1 设计应用结构与Glade界面文件

对于复杂的界面,手动用代码创建每一个控件并设置其属性会非常繁琐且难以维护。GTK社区提供了 Glade 这个优秀的界面设计器。你可以通过拖拽的方式设计界面,保存为一个.glade或.ui的XML文件,然后在代码中用GtkBuilder加载它。这实现了界面与逻辑的彻底分离。

不过,为了深入理解控件间的关联,我们先完全用代码构建。我们的编辑器将包含以下区域:

  1. 主窗口 (GtkApplicationWindow)
  2. 顶部的菜单栏和工具栏 (GtkHeaderBar 或 GtkBox + GtkMenuButton)
  3. 中央的文本编辑区域 (GtkScrolledWindow 内嵌 GtkTextView)
  4. 底部的状态栏 (GtkStatusbar)

由于代码较长,我们聚焦于核心架构和关键函数。首先,我们使用更现代的GtkApplication框架,它更好地处理了应用生命周期、多实例等平台相关特性。

#include <gtk/gtk.h>
#include <string.h>

typedef struct {
    GtkWidget *window;
    GtkWidget *text_view;
    GtkWidget *statusbar;
    GtkTextBuffer *buffer;
    gchar *current_file; // 记录当前打开的文件路径
} EditorData;

static void update_statusbar(EditorData *data, const gchar *msg) {
    guint context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(data->statusbar), "editor_status");
    gtk_statusbar_push(GTK_STATUSBAR(data->statusbar), context_id, msg);
}

static void on_open_clicked(GtkWidget *widget, gpointer user_data) {
    EditorData *data = (EditorData *)user_data;
    GtkWidget *dialog;
    GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
    gint res;

    dialog = gtk_file_chooser_dialog_new("打开文件",
                                          GTK_WINDOW(data->window),
                                          action,
                                          "_取消",
                                          GTK_RESPONSE_CANCEL,
                                          "_打开",
                                          GTK_RESPONSE_ACCEPT,
                                          NULL);

    res = gtk_dialog_run(GTK_DIALOG(dialog));
    if (res == GTK_RESPONSE_ACCEPT) {
        GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
        gchar *filename = gtk_file_chooser_get_filename(chooser);
        gchar *contents = NULL;
        gsize length = 0;
        GError *error = NULL;

        if (g_file_get_contents(filename, &contents, &length, &error)) {
            gtk_text_buffer_set_text(data->buffer, contents, -1);
            g_free(data->current_file);
            data->current_file = g_strdup(filename);
            gchar *status_msg = g_strdup_printf("已打开: %s", filename);
            update_statusbar(data, status_msg);
            g_free(status_msg);
            g_free(contents);
        } else {
            GtkWidget *err_dialog = gtk_message_dialog_new(GTK_WINDOW(data->window),
                                                            GTK_DIALOG_MODAL,
                                                            GTK_MESSAGE_ERROR,
                                                            GTK_BUTTONS_OK,
                                                            "无法打开文件: %s",
                                                            error->message);
            gtk_dialog_run(GTK_DIALOG(err_dialog));
            gtk_widget_destroy(err_dialog);
            g_error_free(error);
        }
        g_free(filename);
    }
    gtk_widget_destroy(dialog);
}

static void on_save_clicked(GtkWidget *widget, gpointer user_data) {
    EditorData *data = (EditorData *)user_data;
    if (data->current_file == NULL) {
        // 如果未打开过文件,则弹出“另存为”对话框
        // 为简洁起见,这里省略“另存为”的实现,逻辑与打开类似
        update_statusbar(data, "请使用‘另存为’功能指定文件名。");
        return;
    }

    GtkTextIter start, end;
    gchar *text;
    GError *error = NULL;

    gtk_text_buffer_get_bounds(data->buffer, &start, &end);
    text = gtk_text_buffer_get_text(data->buffer, &start, &end, FALSE);

    if (g_file_set_contents(data->current_file, text, -1, &error)) {
        update_statusbar(data, "文件保存成功。");
    } else {
        GtkWidget *err_dialog = gtk_message_dialog_new(GTK_WINDOW(data->window),
                                                        GTK_DIALOG_MODAL,
                                                        GTK_MESSAGE_ERROR,
                                                        GTK_BUTTONS_OK,
                                                        "保存失败: %s",
                                                        error->message);
        gtk_dialog_run(GTK_DIALOG(err_dialog));
        gtk_widget_destroy(err_dialog);
        g_error_free(error);
    }
    g_free(text);
}

// ... 其他回调函数,如新建、另存为、关于等

static void activate(GtkApplication *app, gpointer user_data) {
    EditorData *data = g_new0(EditorData, 1); // 分配并清零内存

    // 创建应用主窗口
    data->window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(data->window), "简易文本编辑器 - 未命名");
    gtk_window_set_default_size(GTK_WINDOW(data->window), 800, 600);

    // 创建垂直主容器
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_container_add(GTK_CONTAINER(data->window), vbox);

    // --- 创建工具栏 ---
    GtkWidget *toolbar = gtk_toolbar_new();
    gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);

    GtkToolItem *open_item = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
    g_signal_connect(open_item, "clicked", G_CALLBACK(on_open_clicked), data);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), open_item, -1);

    GtkToolItem *save_item = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
    g_signal_connect(save_item, "clicked", G_CALLBACK(on_save_clicked), data);
    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), save_item, -1);

    gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);

    // --- 创建文本视图和滚动窗口 ---
    GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
                                   GTK_POLICY_AUTOMATIC,
                                   GTK_POLICY_AUTOMATIC);
    gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);

    data->text_view = gtk_text_view_new();
    data->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data->text_view));
    gtk_container_add(GTK_CONTAINER(scrolled_window), data->text_view);

    // --- 创建状态栏 ---
    data->statusbar = gtk_statusbar_new();
    gtk_box_pack_end(GTK_BOX(vbox), data->statusbar, FALSE, FALSE, 0);
    update_statusbar(data, "就绪");

    gtk_widget_show_all(data->window);
}

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;

    // 创建一个GtkApplication,传入唯一标识符
    app = gtk_application_new("com.example.simpleeditor", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app); // 清理

    return status;
}

这个编辑器示例虽然精简,但已经具备了真实应用的基本骨架。它演示了:

  • GtkApplication的使用:管理应用实例和生命周期。
  • 复杂控件组合:GtkToolbar、GtkScrolledWindow、GtkTextView、GtkStatusbar。
  • 文件操作:使用GtkFileChooserDialog选择文件,用GLib的g_file_get_contents和g_file_set_contents读写文件。
  • 错误处理:使用GError捕获并显示文件操作错误。
  • 内存管理:使用g_new0分配结构体,注意在程序退出前应释放data->current_file等动态内存(为简化代码未在示例中展示完整释放逻辑)。

编译这个编辑器需要链接GLib库:

gcc -o simple_editor simple_editor.c `pkg-config --cflags --libs gtk+-3.0` -lgio-2.0

-lgio-2.0 是GtkApplication所依赖的GLib I/O库。

4.2 跨平台编译与打包注意事项

当你分别在Linux和Windows(MSYS2)上成功编译并运行了这个编辑器后,可能会思考如何将其分享给没有安装GTK开发环境的用户。

  • Linux:通常用户系统已安装GTK运行库。你可以通过创建.desktop文件、指定图标等方式制作一个标准的桌面应用快捷方式。对于分发,可以制作成DEB、RPM或Flatpak/Snap包,它们能声明依赖关系,自动安装所需的GTK库。
  • Windows:这是挑战所在。你需要将程序依赖的所有GTK+ DLL(以及其依赖的GLib、Cairo、Pango等库的DLL)与你的可执行文件一起分发。一个相对简单的方法是使用MSYS2环境下的mingw-w64-ucrt-x86_64-gtk3包,它已经将大部分依赖打包好了。你可以在MSYS2终端中使用 ldd your_program.exe(或MSYS2的ntldd)来查看依赖的DLL,然后将它们从 /ucrt64/bin/ 目录复制到你的可执行文件目录。更专业的做法是使用NSIS、Inno Setup等工具制作安装程序,在安装过程中自动部署这些运行时库。

注意:GTK+3.0应用在Windows上的默认主题可能与原生Windows应用风格不同。你可以通过安装和设置Windows主题(如mingw-w64-ucrt-x86_64-gtk3-themes包中的win32主题)来改善视觉效果,只需在程序启动前调用 gtk_theme_name = "win32"; 并设置环境变量或通过代码配置。

从第一个空白窗口到一个具备基本功能的文本编辑器,我们一步步探索了GTK+3.0的核心概念:初始化、窗口、控件、容器、信号、事件循环、对话框以及应用框架。用C语言写GUI并非遥不可及,它要求你对程序的结构有更清晰的规划,对内存和资源管理有更细致的把控。这种控制力带来的回报是极高的运行效率和深度的平台集成潜力。当你看到自己用C语言编写的应用在不同操作系统上以原生姿态流畅运行时,那种跨越平台的成就感,正是底层语言编程的独特魅力所在。

GTK+3.0入门:用C语言打造你的第一个跨平台GUI应用(附完整代码) 本文是GTK+3.0的入门指南,详细介绍了如何使用C语言开发跨平台GUI应用。文章从环境搭建讲起,涵盖Linux、macOS和Windows三大平台,并深入解析GTK+的对象、信号与容器等核心概念。通过构建一个功能完整的计算器项目,手把手演示了界面创建、事件处理及逻辑实现,并对比了纯代码与GtkBuilder两种开发方式,最后提供了编译打包及跨平台部署的实用建议。 阅读详情

相关推荐

在Windows下使用GTK+开发GUI应用程序

// 所有原创文章转载请注明作者及链接// blackboycpp(AT)gmail.com// QQ群: 135202158  What is GTK+?GTK+ is a highly usable, feature rich toolkit for creating graphical user interfaces

blackboy的技术博客 6759

图神经网络的介绍

该文献中,介绍了 多视图 的 图神经网络的学习;以及多视图 图神经网络的 对比学习,需要阅读;

chumingqian的博客 620

C语言的图形用户界面

图形用户界面(Graphical User Interface, GUI)是指以图形和视觉方式呈现给用户的操作界面。相较于传统的命令行界面(CLI),GUI提供了一种更为直观和易用的交互方式。对于使用C语言开发图形用户界面的程序员而言,掌握相关知识与技术是必要的。这篇文章将对C语言的GUI开发进行全面的探讨,包括其历史背景、主要工具和库、编程实践及未来发展方向等内容。

2501_90260896的博客 1651

【技术解析】多任务自指导微调与图神经网络融合:大语言模型在安全漏洞检测中的创新实践

本文深入解析了将多任务自指导微调与图神经网络融合的创新方法,以提升大语言模型在安全漏洞检测中的能力。该方法通过让LLM学习漏洞分析的思维链对话,并结合GNN对代码控制流图的结构化理解,显著增强了模型对复杂漏洞的检测与解释能力,为构建下一代智能代码审计工具提供了实践路径。

ik67890123的博客 421

基于图神经网络的LLM关系评测

在自然语言处理领域,图神经网络(GNN)和语言模型(LLM)都是非常重要的概念。GNN是一种基于图结构的数据处理模型,能够在图结构中有效地捕捉节点和边之间的关系。而LLM则是一种能够对自然语言进行建模的模型,广泛应用于机器翻译、文本生成、问答系统等任务。然而,在LLM中,关系评测是一个关键问题,涉及到如何准确识别和判断句子中的实体关系。在本节中,我们将介绍基于图神经网络的LLM关系评测系统的总体设计思路、问题和解决方案。在自然语言处理领域中,关系评测是一个关键任务。

AI大模型应用之禅 809

GTK+ 2.0 入门完美教程:从零构建跨平台GUI应用

GTK+(GIMP Toolkit)最初为GIMP图像处理软件开发,现已演变为一个成熟、稳定且高度可移植的图形用户界面框架。它采用C语言编写,支持面向对象的设计模式,通过 GObject 系统实现类与继承机制,为开发者提供灵活的组件扩展能力。GtkTable通过行列索引定位控件,创建时需指定初始行列数:// 3行2列,不均等分配使用添加控件:参数说明如下:参数含义列索引范围(左闭右开)行索引范围(上闭下开)水平/垂直方向的扩展与填充选项(如GTK_FILL。

weixin_42599908的博客 863

如何用C,编写简单的图形用户界面(GUI)程序

本文将介绍如何使用C,编写一个简单的图形用户界面(GUI)程序,该程序使用WindowsForms框架创建。我们将展示如何设计窗口添加按钮事件处理以及实现文本框的交互功能。通过本教程,您将了解如何在C,中创建和管理图形用户界面的基本概念。

void9main的博客 408

GTK+基础教程:跨平台GUI开发入门

GTK+是一个历史悠久且功能强大的图形库,最初由GNU项目支持,旨在为Linux平台的GNU Image Manipulation Program (GIMP) 提供一个用户界面。随着其不断的发展和优化,GTK+已经演变成一个跨平台的工具包,支持Unix-like系统、Windows甚至嵌入式系统。GTK+组件库还支持开发者进行自定义扩展,以实现特定需求。如通过CSS进行样式自定义,或利用信号和槽机制实现更复杂的交互行为。

weixin_34511754的博客 1361

分享一个简单的基于C语言嵌入式GUI界面切换代码

最近在用LVGL写一个简单的UI界面,需要进行几个页面的切换,所以就自己写了一个简单页面切换代码,方便进行页面切换,同时使UI代码结构更加清晰。这个结构主要用于嵌入式MCU上使用,所以结构比较简单。

洛神殇的博客 1934

Keil5编译报错:ARM Compiler Version 5缺失的完整解决方案

本文提供了Keil5编译报错“ARM Compiler Version 5缺失”的完整解决方案。详细介绍了手动安装ARM Compiler Version 5的步骤,包括下载安装包、配置编译器路径及常见问题排查,帮助开发者快速恢复老项目的编译环境。

bb456的博客 945

【流复制环境PostgreSQL-14.1到PostgreSQL-16.1大版本升级】

PostgreSQL大版本会定期添加新特性,这些新特性通常会改变系统表的布局,但内部数据存储格式很少改变。pg_upgrade通过创建新的系统表和重用旧的用户数据文件来执行快速升级。pg_upgrade升级主要有三种用法:1、使用pg_upgrade拷贝升级。2、使用pg_upgrade链接升级(带有- -link选项),- -link较快,但是启动新版本后修改了数据文件,再启动旧版本可能数据损坏,回滚较麻烦,所以尽量避免使用link参数进行升级。

小怪兽的博客 2926

C语言图形界面编程:GTK+ 桌面应用开发实战

本文摘要: 《图形界面编程:GTK+桌面应用开发实战》系统介绍了GTK+图形界面开发的核心技术。主要内容包括:1) GTK+环境搭建与验证方法,支持Linux/Windows/Mac多平台;2) 基础控件使用(窗口、按钮、文本框等)和布局管理(盒子容器、网格容器);3) 信号与回调机制实现交互逻辑;4) 实战案例开发完整桌面应用。文章详细讲解了GTK+的事件驱动模型和工作原理,强调通过容器控件组织界面元素实现跨平台兼容性,并提供了代码示例和环境配置指导,帮助开发者快速掌握GTK+桌面应用开发技能。

今天比昨天更努力一点! 9635

【深度学习】手把手教你在远程服务器上跑 YOLOv3 相关代码(含环境配置)

这篇文章主要记录了如何配置项目环境以及如何在远程服务器上跑 YOLOv3 模型。

作者正在煮茶中... 2383

GTK+:GTK+的简介、安装、使用方法之详细攻略

​ GTK+:GTK+的简介、安装、使用方法之详细攻略 目录 GTK+的简介 GTK+的安装 GTK+的使用方法 GTK+的简介 GTK+(GIMP Toolkit)是一套源码以LGPL许可协议分发、跨平台的图形工具包。最初是为GIMP写的,已成为一个功能强大、设计灵活的一个通用图形库,是GNU/Linux下开发图形界面的应用程序的主流开发工具之一。并且,GTK+也有Windows版本和Mac OS X版。 GTK+ 是一种图形用户界面(GUI)工

头部AI社区如有邀博主AI主题演讲请私信—心比天高,仗剑走天涯,保持热爱,奔赴向梦想!低调,专注,谦虚,自律,反思,成长,还算比较正能量的博主,公益免费传播…内心特别想在AI界做出一些可以推进历史进程影响力的技术(兴趣使然,有点小情怀,也有点使命感呀 6万+

中国农业银行——轻量式云原生应用平台(轻云平台)

2021年10月,中国人民银行等联合发布了《关于规范金融业开源技术应用与发展的意见》(银办发〔2021〕146 号),规范金融机构合理应用开源技术,提高应用水平和自主可控能力,促进开源技术健康可持续发展。前期,为助力成员单位做好开源技术应用与发展工作,北京金融科技产业联盟开源专委会组织了金融业开源技术应用、创新等方面的案例征集,现对部分优秀案例进行宣传,发挥先进典型示范引领作用。申报单位:中国农业银行股份有限公司技术领域:云原生、无服务器架构(Serverless)、DevOps。

银行信息系统应用架构详解 2392

YOLO 系列专栏(二十九)YOLO26 × StarNet 轻量化涨点方案(CVPR 2024)轻量化、主干改进篇 | 星星卷积虽小,却蕴藏着巨大的高效潜力

StarNet 出自 CVPR 2024 轻量化主干网络工作,核心创新是,依靠逐元素乘法实现高阶特征融合,在几乎不增加参数量与 FLOPs 的前提下,大幅提升特征表达能力,天然适合等多任务统一建模。本文将 StarNet 完整替换 YOLO26 原有主干,保留 YOLO26 无 NMS 检测头与轻量化结构,实现,并提供。

qq_36130719的博客 410

C语言图形编程与GUI开发:ncurses库、GTK+、SDL、OpenGL深度解析

C语言图形编程与GUI开发技术概览 本文介绍了C语言中四种主要的图形编程与GUI开发技术: ncurses库 - 终端图形编程库,提供创建文本界面的API,适合开发命令行图形界面应用。文章详细讲解了初始化、输出输入函数以及避坑指南。 GTK+ - Linux平台的图形用户界面库,支持跨平台GUI开发。重点介绍了窗口、按钮等组件使用,以及信号处理机制和常见问题规避方法。 SDL - 跨平台的图形、音频、输入库,主要用于游戏和多媒体应用开发。涵盖了窗口创建、渲染器、纹理加载等核心功能。 OpenGL - 跨平台

编程世界如山海,探索不止。 9870

国科大学习资料--高级软件工程-复习题简答题答案

国科大学习资料–高级软件工程(罗铁坚 魏峻 刘丹)-复习题简答题答案 def fib3 n (1…n).inject([0,1]){|mem| puts mem[0]+mem[1]; [mem[1],mem[0]+mem[1]]} end =====================================================================================================================================

无名何许人的博客 1397

【GTK+3.0】C语言图形库GTK+3.0在Windows Cygwin下的环境配置(更新时间:2016-04-21)

【GTK+3.0】C语言图形库GTK+3.0在Windows Cygwin下的环境配置 经历了从下午一点开始到现在的环境配置,Doge终于成功地实现了GTK+的图形环境设置,并且成功的”Hello World!” 引言 aaaa首先简单介绍一下GTK吧,Doge也是今天下午想开始写数据库作业的时候想给自己的程序写一个图形界面。之前在上课的过程中接触到过TC环境下的graphics.h(又让我默

KuroNeko黑猫 3774

【Zephyr存储专题】09_LittleFs文件系统操作

本文档旨在为开发者介绍Zephyr下Littlef's文件系统相关配置使用指南

zero的博客 3246

【教程4>第8章>第44节】本章整体思维导图与学习总结

本教程系列提供FPGA/MATLAB/Simulink学习资源,包含《MATLAB入门100例》、《FPGA入门100例》、《Simulink入门60例》等基础教程,以及《FPGA/MATLAB/Simulink联合开发》进阶教程和《人工智能基础应用研究》专题,适合不同层次的学习者系统掌握相关技术。

FPGA/MATLAB学习教程/源码/项目合作开发 448

libui-ng完全指南:打造跨平台C语言GUI应用的终极选择

libui-ng是一个功能强大的跨平台C语言GUI库,它让开发者能够使用简单的C API创建原生外观的桌面应用程序。无论你是C语言初学者还是经验丰富的开发者,libui-ng都能帮助你快速构建Windows、macOS和Linux平台上的图形界面应用。这个轻量级库提供了原生控件支持,让你的应用在每个平台上都拥有原生的外观和体验。 ## 🎯 为什么选择libui-ng? 在众多GUI开发选项中

gitblog_00215的博客 868

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

让AI Agent替你操作网页:qiaomu-opencli-skills接入Claude Code智能体工作流实战

🤖 **qiaomu-opencli-skills** 是一套专为 **Claude Code** 打造的 AI Agent 技能包:基于 OpenCLI 浏览器自动化,把 Bilibili、知乎、Twitter/X、YouTube、小红书、微博、HackerNews 等 79+ 个网站变成命令行接口,让你的 AI 智能体直接查热门、搜内容、发推文、下载视频。本文手把手带你完成从安装到实战的完整

gitblog_00072的博客 1019

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

基于c语言GUI界面的简单学生管理系统

基于c语言的GUI(gtk)的简单学生管理系统 welcome hello world! 这是第一次写博客,同时将本次课上实训的小作业展示出来 ,请多多关照。 GUI Graphics User Interface) 图形用户界面,是计算机与使用者之间的对话接口,是计算机重要的组成部分,比如说咱们使用电脑或手机看到的Windows的桌面或wps软件显示的窗口界面等都是GUI,都是图形界面开发出来的...

sf9090的博客 9458

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

GTK+:GTK+的简介、下载、安装、使用方法的详细攻略

GTK+(GIMP Toolkit)是一个用于创建图形用户界面(GUI)的跨平台工具包,广泛应用于 Linux、Windows 和 macOS 等操作系统。本文将详细介绍 GTK+ 的简介、安装方法及使用方法,帮助你快速掌握这一强大的 GUI 开发工具。

科技改变人类,技术成就未来 2485

gtk c语言 gui 窗口 window,◆C语言函数库简介系列◆1.1 GTK+

该楼层疑似违规已被系统折叠隐藏此楼查看此楼1C与GUI用C写GUI似乎是一件非常痛苦的事情。比如WIN32SDK,比如XLIB……我初学编程时,为了找一个合适的GUI库--既强大又比较简单,花费了不知多少时间。初见GTK+,就在我苦苦查找GUI库的时候。但那个时候的GTK+让我难以理解,或者说,那个时候的我还不能理解GTK+,轻而易举地放弃了这个优秀的GUI库。众里寻她千百度。找不到。我于是...

weixin_34646207的博客 708
上一篇: 项目管理中的八大绩效域:如何用它们提升团队效率与项目成功率
下一篇: 从Flux.1到Flux.1-Kontext:原生多模态编辑如何重塑AI图像工作流
linux6sysadmin
博客等级 码龄4年 59粉丝 1615原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值