45、网络编程与GNOME/GTK+编程入门

网络编程与GNOME/GTK+编程入门

数据报(Datagrams)

在网络编程中,通常使用面向连接的TCP套接字连接来开发与客户端保持连接的应用程序。但在某些情况下,建立和维护套接字连接的开销是不必要的。例如之前 getdate.c 中使用的日间服务,为了获取日期,需要创建套接字、建立连接、读取单个响应并关闭连接,操作较为繁琐。

而UDP协议提供的日间服务则简单得多,使用数据报(Datagrams),只需向服务发送单个数据报,就能收到包含日期和时间的单个数据报作为响应。UDP服务通常适用于客户端向服务器发起简短查询并期望获得单个简短响应的场景。如果处理时间成本足够低,服务器可以逐个处理客户端请求,操作系统会将传入请求放入队列,这简化了服务器的编码。

不过,UDP不是有保证的服务,数据报或响应可能会丢失。因此,如果数据很重要,需要仔细编写UDP客户端代码来检查错误并在必要时重试。实际上,在局域网中,UDP数据报是非常可靠的。

要访问UDP提供的服务,仍需使用 socket close 系统调用,但不再使用套接字的 read write ,而是使用两个特定于数据报的系统调用: sendto recvfrom

以下是一个修改后的 getdate.c 版本,通过UDP数据报服务获取日期:

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    char *host;
    int sockfd;
    int len, result;
    struct sockaddr_in address;
    struct hostent *hostinfo;
    struct servent *servinfo;
    char buffer[128];
    if(argc == 1)
        host = "localhost";
    else
        host = argv[1];
    /*  Find the host address and report an error if none is found.  */
    hostinfo = gethostbyname(host);
    if(!hostinfo) {
        fprintf(stderr, "no host: %s\n", host);
        exit(1);
    }
    /*  Check that the daytime service exists on the host.  */
    servinfo = getservbyname("daytime", "udp");
    if(!servinfo) {
        fprintf(stderr,"no daytime service\n");
        exit(1);
    }
    printf("daytime port is %d\n", ntohs(servinfo -> s_port));
    /*  Create a UDP socket.  */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    /*  Construct the address for use with sendto/recvfrom...  */
    address.sin_family = AF_INET;
    address.sin_port = servinfo -> s_port;
    address.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list;
    len = sizeof(address);
    result = sendto(sockfd, buffer, 1, 0, (struct sockaddr *)&address, len);
    result = recvfrom(sockfd, buffer, sizeof(buffer), 0, 
                      (struct sockaddr *)&address, &len);
    buffer[result] = '\0';
    printf("read %d bytes: %s", result, buffer);
    close(sockfd);
    exit(0);
}

可以看到,所需的更改非常小。像之前一样使用 getservbyname 查找日间服务,但通过请求UDP协议指定数据报服务。使用 socket 函数并传入 SOCK_DGRAM 参数创建数据报套接字。和之前一样设置目标地址,但现在需要发送数据报而不是仅仅从套接字读取。

由于没有与UDP服务建立显式连接,需要有一种方式让服务器知道你希望接收响应。在这个例子中,向服务发送一个数据报(这里从要接收响应的缓冲区发送一个字节),服务器会以日期和时间作为响应。

sendto 系统调用的原型如下:

int sendto(int sockfd, void *buffer, size_t len, int flags, 
           struct sockaddr *to, socklen_t tolen);

在正常使用中, flags 参数可以设为0。

recvfrom 系统调用的原型如下:

int recvfrom(int sockfd, void *buffer, size_t len, int flags, 
             struct sockaddr *from, socklen_t *fromlen);

同样,在正常使用中, flags 参数可以设为0。

为了简化示例,省略了错误处理。 sendto recvfrom 在发生错误时会返回 -1 并设置 errno 。可能的错误如下表所示:
| Errno Value | Description |
| ---- | ---- |
| EBADF | 传入了无效的文件描述符 |
| EINTR | 发生了信号 |

除非使用 fcntl 将套接字设置为非阻塞(就像之前处理TCP连接时那样),否则 recvfrom 调用会无限期阻塞。不过,套接字可以与 select 和超时一起使用,以确定是否有数据到达,就像之前基于连接的服务器那样。或者,可以使用闹钟信号来中断接收操作。

X窗口系统简介

在Linux编程中,为应用程序添加图形用户界面(GUI)是很重要的一部分。而所有Linux的GUI库都基于底层的X窗口系统(通常称为X11或X)。

X是一个开源图形系统,其最具创新性但也令人困扰的特点是严格遵循“机制而非策略”的原则,即X不定义用户界面,而是提供创建界面的手段。这意味着可以自由创建自己的整个桌面环境,但也在很长一段时间内阻碍了Linux和UNIX用户界面的发展。在此背景下,GNOME和KDE两个桌面项目成为Linux用户的首选。

X窗口系统分为硬件级和应用级组件,即X服务器和X客户端,它们通过X协议进行通信。

  • X服务器 :运行在用户的本地机器上,负责在屏幕上绘制图形的底层操作。虽然名为“服务器”,但它运行在桌面PC上。X客户端可以运行在本地PC上,也可以运行在网络中的其他系统(包括服务器)上。由于X服务器直接与显卡通信,必须使用特定于显卡的X服务器,并进行适当的分辨率、刷新率、颜色深度等配置。配置文件名为 xorg.conf Xfree86Config 。过去,需要手动编辑配置文件才能使X正常工作,不过现代Linux发行版可以自动检测正确的设置。X服务器通过鼠标和键盘监听用户输入,并将按键和鼠标点击事件传递给X客户端应用程序,这些消息称为事件,是GUI编程的关键元素。
  • X客户端 :任何使用X窗口系统作为GUI的程序都是X客户端,如 xterm xcalc 以及更高级的应用程序 Abiword 等。X客户端通常等待X服务器发送的用户事件,并通过向服务器发送重绘消息来响应。X客户端不必与X服务器在同一台机器上。
  • X协议 :X客户端和X服务器通过X协议进行通信,这使得客户端和服务器可以通过网络分离。例如,可以在互联网上的远程计算机或加密的虚拟专用网络(VPN)上运行X客户端应用程序。对于绝大多数个人Linux系统,X客户端和X服务器运行在同一系统上。

Xlib是X客户端间接用于生成X协议消息的库,它提供了非常底层的API,允许客户端在X服务器上绘制基本元素并响应最简单的输入。但要使用Xlib创建一个简单的菜单都需要数百行代码,因此GUI程序员通常不会直接使用Xlib,而是需要一个能简化GUI元素(如菜单、按钮和下拉列表)创建的API,这就是工具包(Toolkits)的作用。

工具包是X客户端用来大大简化窗口、菜单、按钮等创建的GUI库。使用工具包,可以通过单个函数调用创建按钮、菜单、框架等,这些GUI元素通常称为小部件(Widgets)。

选择工具包是应用程序设计中的重要决策,需要考虑以下因素:
- 应用程序的目标用户是谁?
- 用户是否安装了工具包库?
- 工具包是否有其他流行操作系统的版本?
- 工具包使用的软件许可证是什么,是否与预期用途兼容?
- 工具包是否支持所使用的编程语言?
- 工具包是否具有现代的外观和感觉?

历史上,最流行的工具包是Motif、OpenLook和Xt,但现在它们大多已被技术更先进的GTK+和Qt工具包所取代,这两个工具包分别是GNOME和KDE桌面环境的基础。

窗口管理器(Window Managers)是X窗口系统的最后一部分,负责在屏幕上定位窗口,通常支持将桌面划分为多个“工作区”,增加可交互的区域。窗口管理器还负责为每个窗口添加装饰,如框架和带有最大化、最小化和关闭图标的标题栏,它提供了桌面的部分外观和感觉。常见的窗口管理器包括:
- Metacity:GNOME桌面的默认窗口管理器。
- KWin:KDE桌面的默认窗口管理器。
- Openbox:旨在节省资源,适合在较旧、较慢的系统上运行。
- Enlightenment:能显示出色的图形和效果。

除了基于X窗口系统的GUI开发方式,还有一些跨平台的创建GUI的方法:
- Java语言 :使用Swing和较旧的AWT API支持GUI编程。Java GUI的外观和感觉并非人人都喜欢,在旧机器上界面可能会显得笨重和无响应。但Java的一大优势是,一旦编译了Java代码,它可以在任何带有Java虚拟机的平台上运行,包括Linux、Windows、Mac OS和移动设备。
- C# :与Java非常相似的编程语言。在Linux上,C#的公共语言运行时(CLR)平台来自Mono项目。Mono平台上的C#支持Windows Forms(也用于Windows)以及与GTK+工具包的特殊绑定Gtk#。
- Tcl/Tk :一种脚本语言,非常适合快速开发GUI,可在X、Windows和Mac OS上工作,适合快速原型开发或小型实用程序。
- Python :也是一种脚本语言,可以使用Tcl/Tk的Tk部分,也可以使用Python GTK+绑定,用Python编写GTK+程序。
- Perl :常见的Linux脚本语言,可以使用Tcl/Tk的Tk部分,即Perl/Tk。

这些跨平台语言带来了平台独立性,但也有代价,例如与本地应用程序共享信息(如使用“拖放”)很困难,保存配置通常需要采用专有方式而非桌面标准方式。有些Java软件供应商会通过提供特定于平台的扩展来解决这些问题。

GTK+工具包介绍

GTK+最初是流行的GNU图像处理程序(The GIMP)的一部分,因此得名(The Gimp ToolKit)。GIMP程序员很有远见地将GTK+作为一个独立项目进行开发,如今它已发展成为最强大和流行的工具包之一。

GTK+是一个库,通过提供一组现成的组件(小部件),并使用简单的函数调用将它们与应用程序逻辑结合起来,大大简化了GUI的创建。

GTK+是一个GNU项目,但它遵循更宽松的LGPL(Lesser General Public License)许可协议,允许软件(包括闭源专有软件)使用GTK+进行开发,无需支付费用、版税或受到其他限制。这与它的竞争对手Qt不同,Qt的GPL许可协议禁止使用Qt开发商业软件(在这种情况下必须购买商业Qt许可证)。

GTK+完全用C语言编写,大多数GTK+软件也是用C编写的。不过,有许多语言绑定允许在喜欢的语言(如C++、Python、PHP、Ruby、Perl、C#或Java)中使用GTK+。

GTK+基于多个其他库构建,包括:
- GLib :提供底层数据结构、类型、线程支持、事件循环和动态加载。
- GObject :在不使用C++的情况下在C中实现面向对象系统。
- Pango :支持文本渲染和布局。
- ATK :帮助创建可访问的应用程序,允许用户使用屏幕阅读器和其他辅助工具运行应用程序。
- GDK(GIMP Drawing Kit) :在Xlib之上处理底层图形渲染。
- GdkPixbuf :帮助在GTK+程序中处理图像。
- Xlib :在Linux和UNIX系统上提供底层图形支持。

GLib类型系统为了实现跨平台开发,GTK+基于GLib和GObject这两个C可移植性库定义了一些数据类型。如果浏览过GTK+代码,会看到很多以字母 g 开头的C数据类型,如 gint gchar gshort ,以及像 gint32 gpointer 这样不熟悉的类型。这些类型是标准C类型的替代品,用于提高一致性和可读性,并保证在所有平台上的字节长度。

GLib还定义了一些方便的常量:

#include <glib/gmacros.h>
#define FALSE  0
#define TRUE  !FALSE

额外的数据类型主要包括:
- gint guint gchar guchar glong gulong gfloat gdouble :为保持一致性,是标准C类型的简单替代。
- gpointer :等同于 (void *)
- gboolean :用于表示布尔值,是 int 的包装。
- gint8 guint8 gint16 guint16 gint32 guint32 :有保证字节长度的有符号和无符号类型。

使用GLib和GObject几乎是透明的,GLib在GTK+中被广泛使用,如果GTK+设置正常,会发现GLib已经安装。在编写GTK+程序时,甚至不需要显式包含 glib.h 头文件。

GTK+对象系统虽然GTK+完全用C语言编写,但通过GObject库支持对象和面向对象编程。该库使用宏支持对象继承和多态。

以GTK+ API文档中的 GtkWindow 对象层次结构为例:

GObject
+----GInitiallyUnowned
+----GtkObject
+----GtkWidget
+----GtkContainer
+----GtkBin
+----GtkWindow

这表明 GtkWindow GtkBin 的子类,因此任何可以用 GtkBin 调用的函数都可以用 GtkWindow 调用。同样, GtkWindow 继承自 GtkContainer ,而 GtkContainer 继承自 GtkWidget

为了方便起见,所有小部件创建函数都返回 GtkWidget 类型。例如:

GtkWidget*  gtk_window_new (GtkWindowType type);

假设创建了一个 GtkWindow ,并想将返回的值传递给一个期望 GtkContainer 的函数,如 gtk_container_add

void gtk_container_add (GtkContainer *container, GtkWidget *widget);

可以使用 GTK_CONTAINER 宏在 GtkWidget GtkContainer 之间进行转换:

GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_add(GTK_CONTAINER(window), awidget);

宏存在于各种可能的类型转换中。如果对这些不太清楚也不用担心,不需要详细了解面向对象编程就能掌握GNOME/GTK+,实际上,这是在C语言的舒适区中学习面向对象编程思想和好处的无痛方式。

GNOME项目介绍

GNOME是1997年由在GNU图像处理程序(The GIMP)上工作的程序员发起的项目,旨在为Linux创建一个统一的桌面。当时,普遍认为Linux作为桌面平台的采用受到缺乏连贯策略的阻碍,Linux桌面就像“狂野西部”,没有总体标准或约定俗成的做法,程序员的心态是“一切皆可”。由于没有一个总体的组织来控制桌面菜单、一致的外观和感觉、文档、翻译等,新手在桌面上的体验最多是令人困惑,最坏的情况下甚至无法使用。

GNOME团队致力于创建一个完全遵循GPL许可的Linux桌面,以统一和一致的风格开发实用程序和配置程序,同时推广应用程序间通信、打印、会话管理以及GUI应用程序编程最佳实践的标准。如今,GNOME是Fedora、Red Hat、Ubuntu和openSUSE等发行版的默认Linux桌面基础。

GNOME最初代表GNU网络对象模型环境,反映了早期引入类似Microsoft OLE的对象框架到Linux的目标,以便在文字处理文档中嵌入电子表格等。现在,GNOME指的是完整的桌面环境,包括用于启动应用程序的面板、一套程序和实用工具、编程库以及开发者支持功能。

在开始编程之前,需要确保安装了所有相关的库。

安装GNOME/GTK+开发库

完整的GNOME桌面及其标准应用程序和GNOME/GTK+开发库涉及60多个软件包,因此手动或从源代码从头安装GNOME是一项艰巨的任务。幸运的是,现代Linux发行版有优秀的软件包管理工具,使得安装GNOME/GTK+和开发库变得轻松。

  • Red Hat和Fedora Linux :通过点击左上角的“Applications”菜单按钮,选择“Add/Remove Software”打开软件包管理工具。确保“GNOME Software Development”复选框被选中,在“Development”区域查找此设置。在本章中,将使用GNOME/GTK+ 2,因此要确保安装包含2.x版本的库。对于使用RPM软件包的发行版,至少应安装以下RPM软件包:
  • gtk2-2.10.11-7.fc7.rpm
  • gtk2-devel-2.10.11-7.fc7.rpm
  • gtk2-engines-2.10.0-3.fc7.rpm
  • libgnome-2.18.0-4.fc7.rpm
  • libgnomeui-2.18.1-2.fc7.rpm
  • libgnome-devel-2.18.0-4.fc7.rpm
  • libgnomeui-devel-2.18.1-2.fc7.rpm
    文件名中的 fc7 参考的是Fedora 7 Linux发行版,在自己的系统上可能会看到略有不同的名称。
  • Debian或基于Debian的系统(如Ubuntu) :可以使用 apt-get 从各种镜像源安装GNOME/GTK+软件包,具体细节可参考http://www.gnome.org的链接。

还可以尝试GTK+演示应用程序 gtk-demo ,它展示了所有小部件的精美效果。每个小部件都有“Info”标签和“Source”标签,“Source”标签显示了使用该小部件的实际C源代码,提供了很好的示例。

简单的GtkWindow示例

下面通过一个最简单的GUI程序来开始GTK+编程:显示一个窗口。

  1. 编写以下程序并保存为 gtk1.c
#include <gtk/gtk.h>
int main (int argc, char *argv[])
{
    GtkWidget *window;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_show(window);
    gtk_main ();
    return 0;
}
  1. 编译 gtk1.c
$ gcc gtk1.c -o gtk1 `pkg-config --cflags --libs gtk+-2.0`

注意要输入反引号(`)而不是单引号(’),反引号是告诉shell执行并追加其中命令的输出。

运行程序:

$ ./gtk1

运行后,窗口会弹出,可以移动、调整大小、最小化和最大化窗口。

该程序的工作原理如下:
- 使用 #include <gtk/gtk.h> 语句包含必要的GTK+及相关库的头文件(包括GLib)。
- 声明 window 为指向 GtkWidget 的指针。
- 调用 gtk_init 初始化GTK+库,传入命令行参数 argc argv ,让GTK+有机会解析它需要了解的任何命令行参数。注意,必须始终以这种方式初始化GTK+。
- 调用 gtk_window_new 创建一个窗口,其原型为 GtkWidget* gtk_window_new (GtkWindowType type) type 可以取两个值:
- GTK_WINDOW_TOPLEVEL :标准带边框的窗口。
- GTK_WINDOW_POPUP :适合对话框的无框窗口。
- 调用 gtk_widget_show 显示窗口。
- 调用 gtk_main 进入GTK+主循环,等待事件发生。

网络编程与GNOME/GTK+编程入门

GTK+小部件与信号机制

GTK+提供了丰富的小部件(Widgets),这些小部件是构建GUI的基础元素。除了前面提到的 GtkWindow ,还有按钮、标签、文本框等。下面以按钮为例,介绍如何使用GTK+小部件以及信号机制。

#include <gtk/gtk.h>

// 按钮点击事件的回调函数
static void activate(GtkApplication *app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *button_box;

    // 创建一个窗口
    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Button Example");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    // 创建一个按钮盒
    button_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
    gtk_container_add(GTK_CONTAINER(window), button_box);

    // 创建一个按钮
    button = gtk_button_new_with_label("Click me!");
    // 连接按钮的点击信号到回调函数
    g_signal_connect(button, "clicked", G_CALLBACK(activate), NULL);
    gtk_container_add(GTK_CONTAINER(button_box), button);

    // 显示所有小部件
    gtk_widget_show_all(window);
}

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

    // 创建一个GTK应用程序
    app = gtk_application_new("org.gtk.example", 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;
}

上述代码展示了如何创建一个带有按钮的窗口,并为按钮的点击事件连接了一个回调函数。信号机制是GTK+中处理用户交互的重要方式,当用户触发某个事件(如点击按钮)时,会发出相应的信号,然后调用预先绑定的回调函数进行处理。

信号连接的一般形式为:

g_signal_connect(GObject *object, const gchar *name, GCallback func, gpointer data);
  • object :发出信号的对象,如按钮。
  • name :信号的名称,如 "clicked"
  • func :回调函数。
  • data :传递给回调函数的用户数据。
GNOME特定小部件与菜单

GNOME在GTK+的基础上提供了一些特定的小部件和菜单,用于构建更符合GNOME桌面风格的应用程序。下面是一个简单的示例,展示如何创建一个带有菜单的窗口。

#include <gtk/gtk.h>

static void activate(GtkApplication *app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *menubar;
    GtkWidget *file_menu;
    GtkWidget *file_item;
    GtkWidget *quit_item;

    // 创建一个窗口
    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Menu Example");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    // 创建菜单栏
    menubar = gtk_menu_bar_new();

    // 创建文件菜单
    file_menu = gtk_menu_new();
    file_item = gtk_menu_item_new_with_label("File");
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_menu);

    // 创建退出菜单项
    quit_item = gtk_menu_item_new_with_label("Quit");
    g_signal_connect_swapped(quit_item, "activate", G_CALLBACK(gtk_widget_destroy), window);
    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit_item);

    // 将文件菜单项添加到菜单栏
    gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file_item);

    // 创建一个垂直布局容器
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
    gtk_container_add(GTK_CONTAINER(window), vbox);

    // 显示所有小部件
    gtk_widget_show_all(window);
}

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

    app = gtk_application_new("org.gtk.example", 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;
}

这个示例创建了一个带有菜单栏和文件菜单的窗口,文件菜单中有一个退出菜单项。当点击退出菜单项时,会调用 gtk_widget_destroy 函数销毁窗口。

对话框的使用

在GUI应用程序中,对话框是与用户进行交互的重要方式,用于显示信息、获取用户输入等。GTK+提供了多种类型的对话框,如消息对话框、文件选择对话框等。下面是一个消息对话框的示例。

#include <gtk/gtk.h>

static void show_dialog(GtkWidget *widget, gpointer data) {
    GtkWidget *dialog;
    dialog = gtk_message_dialog_new(GTK_WINDOW(data),
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_INFO,
                                    GTK_BUTTONS_OK,
                                    "This is a message dialog!");
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}

static void activate(GtkApplication *app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *button;

    // 创建一个窗口
    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Dialog Example");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    // 创建一个按钮
    button = gtk_button_new_with_label("Show Dialog");
    g_signal_connect(button, "clicked", G_CALLBACK(show_dialog), window);
    gtk_container_add(GTK_CONTAINER(window), button);

    // 显示所有小部件
    gtk_widget_show_all(window);
}

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

    app = gtk_application_new("org.gtk.example", 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;
}

当点击按钮时,会弹出一个消息对话框,显示一条信息。对话框使用 gtk_message_dialog_new 函数创建,然后使用 gtk_dialog_run 函数显示并等待用户响应,最后使用 gtk_widget_destroy 函数销毁对话框。

开发CD数据库GUI应用

接下来,我们将使用GNOME/GTK+开发一个简单的CD数据库GUI应用,展示如何综合运用前面所学的知识。

#include <gtk/gtk.h>

// 全局变量,用于存储窗口和列表视图
GtkWidget *window;
GtkWidget *tree_view;

// 创建列表视图
static void create_tree_view() {
    GtkListStore *list_store;
    GtkTreeViewColumn *column;
    GtkCellRenderer *renderer;

    // 创建列表存储
    list_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);

    // 创建列表视图
    tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));

    // 创建单元格渲染器
    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes("Title", renderer, "text", 0, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes("Artist", renderer, "text", 1, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);

    // 添加一些示例数据
    GtkTreeIter iter;
    gtk_list_store_append(list_store, &iter);
    gtk_list_store_set(list_store, &iter, 0, "Album 1", 1, "Artist 1", -1);
    gtk_list_store_append(list_store, &iter);
    gtk_list_store_set(list_store, &iter, 0, "Album 2", 1, "Artist 2", -1);
}

// 主函数
int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);

    // 创建窗口
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "CD Database");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);

    // 创建列表视图
    create_tree_view();

    // 创建一个垂直布局容器
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_box_pack_start(GTK_BOX(vbox), tree_view, TRUE, TRUE, 0);
    gtk_container_add(GTK_CONTAINER(window), vbox);

    // 显示所有小部件
    gtk_widget_show_all(window);

    // 进入GTK+主循环
    gtk_main();

    return 0;
}

这个示例创建了一个简单的CD数据库GUI应用,使用列表视图显示CD的标题和艺术家信息。首先创建列表存储和列表视图,然后添加列和示例数据,最后将列表视图添加到窗口中显示。

总结

通过以上内容,我们学习了网络编程中的UDP数据报使用,了解了X窗口系统的组成和工作原理,深入学习了GTK+工具包和GNOME桌面环境的开发。从简单的窗口显示到复杂的菜单、对话框和数据库应用的开发,我们逐步掌握了GNOME/GTK+编程的基本技巧。

在实际开发中,可以根据需求选择合适的小部件和信号机制,创建出功能丰富、界面友好的GUI应用程序。同时,要注意错误处理和资源管理,确保程序的稳定性和可靠性。随着不断的学习和实践,能够开发出更加复杂和强大的应用程序。

下面是一个简单的流程图,展示了GTK+应用程序的基本开发流程:

graph TD;
    A[初始化GTK+] --> B[创建窗口];
    B --> C[创建小部件];
    C --> D[连接信号与回调函数];
    D --> E[显示小部件];
    E --> F[进入主循环];

这个流程图清晰地展示了GTK+应用程序的开发步骤,从初始化到创建窗口、小部件,再到连接信号和进入主循环,每个步骤都是必不可少的。通过遵循这个流程,可以有条不紊地开发出GTK+应用程序。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值