(四)、一步一步学GTK+之多窗口
一、按照一定的逻辑去切割代码并实现多窗口
上一篇中完成了一个简单的软件界面,考虑到这个软件主要的目的是在软件中容纳很多控件,一个窗口中不可能全部包含进去的。所以这篇内容是我们一起来学下多窗口。既然我们要实现多窗口,那么就不能把所有代码放到一个文件中,这样会很混乱的,一个文件来放一个窗口,然后用main.c的入口文件来调度它们。使用我先把文件的结构分为:
main.c——入口文件
window_main.c——主窗口文件
window_test.c——测试用的子窗口文件
common_func.c——各个窗口调用的公共函数文件
既然涉及到公共函数会被其他窗口调用,我们还需要建立一些头文件,在其中加入一些宏来防止重复包含文件。
common_func.h
#ifndef __COMMON_FUNC_H__ #define __COMMON_FUNC_H__ #include <gtk/gtk.h> GdkPixbuf *create_pixbuf(const gchar * filename); void toggle_display(GtkWidget *widget, gpointer oneofwidget); #endif // __COMMON_FUNC_H__
common.c
#include "common_func.h" /* @Description: 从一个图片中获取信息得到pixbuf @param: gchar filename */ GdkPixbuf *create_pixbuf(const gchar * filename) { GdkPixbuf *pixbuf; GError *error = NULL; /* * 函数gdk_pixbuf_new_from_file() 从一个图片文件中加载图象数据,从而生成一个新的 pixbuf, * 至于文件中包含图象的格式,是由系统自动检测的。如果该函数返回值是NULL 的话,程序就会出现错误。 */ pixbuf = gdk_pixbuf_new_from_file(filename, &error); if(!pixbuf) { fprintf(stderr, "%s\n", error->message); g_error_free(error); } return pixbuf; } //根据菜单栏的选择(check)控制一些构件的显示和隐藏 void toggle_display(GtkWidget *widget, gpointer oneofwidget) { if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { gtk_widget_show(oneofwidget); } else { gtk_widget_hide(oneofwidget); } }
以上只是把原来的两个函数提取出来,并添加了头文件。
window_main.h
#ifndef __WINDOW_MAIN_H__ #define __WINDOW_MAIN_H__ #include <gtk/gtk.h> #include "common_func.h" GtkWidget *create_main_window(); #endif // __WINDOW_MAIN_H__
window_main.c
#include "common_func.h" #include "window_main.h" GtkWidget *create_main_window() {//创建主窗口 GtkWidget *window; GtkWidget *vbox; //盒装容器 GtkWidget *menubar; //菜单栏 GtkWidget *menutoggle, *menu_tog_toggle,*menu_tog_toolbar, *menu_tog_statusbar; //界面开关菜单 //GtkWidget *menu_about, *menu_about_us; //帮助菜单 GtkWidget *toolbar; //工具栏 GtkToolItem *tool_exit, *tool_sep,*tool_about; GtkWidget *statusbar; //状态栏 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "一步一步学GTK+ DEMO"); gtk_window_set_default_size(GTK_WINDOW(window), 500, 400); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); /*函数gtk_window_set_icon() 是为窗口设置图标用的,函数create_pixbuf是我们自定义的,目的是从一个图片中获取信息得到pixbuf。*/ gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("./images/bear.png")); /*创建一个盒装容器并添加到窗口中*/ vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(window), vbox); /*创建菜单*/ menubar = gtk_menu_bar_new(); //代表整个菜单,是一个menu shell menutoggle = gtk_menu_new(); //这里代表第一列菜单toggle ,也是一个menu shell menu_tog_toggle = gtk_menu_item_new_with_label("View"); menu_tog_toolbar = gtk_check_menu_item_new_with_label("show Toolbar"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_tog_toolbar),TRUE); menu_tog_statusbar = gtk_check_menu_item_new_with_label("show Statusbar"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_tog_statusbar),TRUE); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tog_toggle), menutoggle); //widget toggle菜单加入 menutoggle menu shell gtk_menu_shell_append(GTK_MENU_SHELL(menutoggle), menu_tog_toolbar); gtk_menu_shell_append(GTK_MENU_SHELL(menutoggle), menu_tog_statusbar); gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menu_tog_toggle); //创建工具栏 toolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); //设置工具栏样式为图标 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0); //工具栏边框大小 tool_exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT); //工具栏中的 “退出” 按钮 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_exit, -1); tool_sep = gtk_separator_tool_item_new(); //工具栏中按钮之间的分割线 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_sep, -1); tool_about = gtk_tool_button_new_from_stock(GTK_STOCK_HELP); //工具栏中的“关于” 按钮 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_about, -1); statusbar = gtk_statusbar_new(); /*把菜单加入盒子容器*/ gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 1); /*把工具栏加入盒子容器*/ gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); /*把状态栏加入盒子最下面*/ gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1); /***********************************以下是信号处理部分************************************/ /*关闭窗口时退出主循环*/ g_signal_connect_swapped(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(tool_exit), "clicked",G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(menu_tog_toolbar), "activate",G_CALLBACK(toggle_display), toolbar); g_signal_connect(G_OBJECT(menu_tog_statusbar), "activate",G_CALLBACK(toggle_display), statusbar); /***********************************以下是显示控件部分************************************/ /*开始显示窗口*/ //gtk_widget_show_all(window); //gtk_main(); //return 0; return window; }
这段也只是原来的代码中,把显示窗口的部分放在了一个函数中(create_main_window()),并把显示窗口语句gtk_widget_show_all(window); 注释掉,放在入口文件中调用,并注释掉gtk_main()主循环,主循环我们应该放在入口文件中,让这个函数返回创建的窗口, return window;
window_test.h
#ifndef __WINDOW_TEST_H__ #define __WINDOW_TEST_H__ #include <gtk/gtk.h> #include "common_func.h" GtkWidget *create_test_window(); #endif // __WINDOW_TEST_H__
window_test.c
#include "window_test.h" GtkWidget *create_test_window() { //创建一个测试窗口 GtkWidget *window_test; window_test = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_show(window_test); //gtk_main(); //return 0; return window_test; }
这里只用了个函数创建了一个空白的测试窗口,并返回窗口。
main.c
#include <gtk/gtk.h> #include "common_func.h" #include "window_main.h" #include "window_test.h" int main( int argc, char *argv[]) { GtkWidget *main_window ,*test_window; gtk_init(&argc, &argv); main_window=create_test_window(); //创建主窗口 test_window=create_main_window(); //创建测试窗口 gtk_widget_show_all(main_window); //显示主窗口 gtk_widget_show_all(test_window); //显示测试窗口 gtk_main(); return 0; }
在入口文件中,我们开始执行创建窗口并显示它们,这样看起来就很简洁,如果还有其他窗口我们用同样的方法去增加就好了。在main.c中同一控制其他窗口的调度是很方便的。这样分开后,代码结构清晰,审查代码也很方便了。
以上的代码仅仅是显示了2个窗口,但点击任意窗口右上角的X就会全部关闭.下面我们来实现2个窗口的交互。
二、主窗口与子窗口的交互
在主窗口中的2个按钮分别控制子窗口的显示和隐藏(没有使用摧毁和重新创建的方法),并在点击子窗口的按钮,改变主窗口的label控件中的文字。顺便也简单介绍下 按钮(GtkButton)和标签控件(GtkLabel).以及非盒装容器GtkFixed。
注意:由于本人能力有限,且暂时不想引入在结构体中包含函数指针的方法,姑且采用几个全局变量,这样其他文件中可通过extern修饰符进行引用。
修改main.c
#include <gtk/gtk.h> #include "common_func.h" #include "window_main.h" #include "window_test.h" //Global GtkWidget *window_test = NULL; //子窗口本身 GtkWidget *label = NULL; //主窗口中的label int main( int argc, char *argv[]) { GtkWidget *window_main; //主窗口不会在子窗口中使用,故不用声明为全局变量 gtk_init(&argc, &argv); window_main=create_main_window(); //创建主窗口 window_test=create_test_window(); //创建测试窗口 gtk_widget_show_all(window_main); //显示主窗口 //gtk_widget_show_all(window_test); //显示测试窗口 gtk_main(); return 0; }
window_main.c
#include "common_func.h" #include "window_main.h" extern GtkWidget *window_test; extern GtkWidget *label; //显示子窗口 void show_test_window(GtkWidget *widget) { gchar *msg="子窗口打开了"; gtk_label_set_text(GTK_LABEL(label),msg); gtk_widget_show_all(window_test); } //隐藏子窗口 void hide_test_window(GtkWidget *widget) { gchar *msg="子窗口关闭了"; gtk_label_set_text(GTK_LABEL(label),msg); gtk_widget_hide_all(window_test); } GtkWidget *create_main_window() {//创建主窗口 GtkWidget *window; GtkWidget *vbox; //盒装容器 GtkWidget *menubar; //菜单栏 GtkWidget *menutoggle, *menu_tog_toggle,*menu_tog_toolbar, *menu_tog_statusbar; //界面开关菜单 //GtkWidget *menu_about, *menu_about_us; //帮助菜单 GtkWidget *toolbar; //工具栏 GtkToolItem *tool_exit, *tool_sep,*tool_about; GtkWidget *statusbar; //状态栏 GtkWidget *fixed; GtkWidget *test_button; GtkWidget *test2_button; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "一步一步学GTK+ DEMO"); gtk_window_set_default_size(GTK_WINDOW(window), 500, 400); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); /*函数gtk_window_set_icon() 是为窗口设置图标用的,函数create_pixbuf是我们自定义的,目的是从一个图片中获取信息得到pixbuf。*/ gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("./images/bear.png")); /*创建一个盒装容器并添加到窗口中*/ vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(window), vbox); /*创建菜单*/ menubar = gtk_menu_bar_new(); //代表整个菜单,是一个menu shell menutoggle = gtk_menu_new(); //这里代表第一列菜单toggle ,也是一个menu shell menu_tog_toggle = gtk_menu_item_new_with_label("View"); menu_tog_toolbar = gtk_check_menu_item_new_with_label("show Toolbar"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_tog_toolbar),TRUE); menu_tog_statusbar = gtk_check_menu_item_new_with_label("show Statusbar"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_tog_statusbar),TRUE); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_tog_toggle), menutoggle); //widget toggle菜单加入 menutoggle menu shell gtk_menu_shell_append(GTK_MENU_SHELL(menutoggle), menu_tog_toolbar); gtk_menu_shell_append(GTK_MENU_SHELL(menutoggle), menu_tog_statusbar); gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menu_tog_toggle); //创建工具栏 toolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); //设置工具栏样式为图标 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0); //工具栏边框大小 tool_exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT); //工具栏中的 “退出” 按钮 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_exit, -1); tool_sep = gtk_separator_tool_item_new(); //工具栏中按钮之间的分割线 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_sep, -1); tool_about = gtk_tool_button_new_from_stock(GTK_STOCK_HELP); //工具栏中的“关于” 按钮 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_about, -1); statusbar = gtk_statusbar_new(); fixed = gtk_fixed_new(); /*把菜单加入盒子容器*/ gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 1); /*把工具栏加入盒子容器*/ gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); /*把fixed加入主工作区*/ gtk_box_pack_start(GTK_BOX(vbox), fixed, FALSE, FALSE, 0); /*在fixed中放入一些按钮和label*/ test_button=gtk_button_new_with_label("打开测试窗口"); gtk_fixed_put(GTK_FIXED(fixed), test_button, 150, 50); gtk_widget_set_size_request(test_button, 150, 35); test2_button=gtk_button_new_with_label("关闭测试窗口"); gtk_fixed_put(GTK_FIXED(fixed), test2_button, 300, 50); gtk_widget_set_size_request(test2_button, 80, 35); label = gtk_label_new("这里的文字会通过子窗口的操作来改变。"); gtk_fixed_put(GTK_FIXED(fixed), label, 100, 100); gtk_widget_set_size_request(label, 300, 100); /*把状态栏加入盒子最下面*/ gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1); /***********************************以下是信号处理部分************************************/ /*关闭窗口时退出主循环*/ g_signal_connect_swapped(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(tool_exit), "clicked",G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(menu_tog_toolbar), "activate",G_CALLBACK(toggle_display), toolbar); g_signal_connect(G_OBJECT(menu_tog_statusbar), "activate",G_CALLBACK(toggle_display), statusbar); g_signal_connect(G_OBJECT(test_button), "clicked",G_CALLBACK(show_test_window), NULL); g_signal_connect(G_OBJECT(test2_button), "clicked",G_CALLBACK(hide_test_window), NULL); return window; }
window_test.c
#include "window_test.h" // extern GtkWidget *window_test; extern GtkWidget *label; static gint delete_event() { /* 如果你的 "delete_event" 信号处理函数返回 FALSE,GTK 会发出 "destroy" 信号。 * 返回 TRUE,你不希望关闭窗口。 * 当你想弹出“你确定要退出吗?”对话框时它很有用。*/ gtk_widget_hide_all(window_test); return TRUE; //注意必须为TRUE,否则子窗口点击关闭按钮后,就摧毁了,而不是隐藏了。 } static void change_label_text() { char *text="我是子窗口的动作改变的"; gtk_label_set_text(GTK_LABEL(label),text); } GtkWidget *create_test_window() { //创建一个测试窗口 GtkWidget *window_test; GtkWidget *fixed; GtkWidget *button; window_test = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(window_test), 300, 200); fixed = gtk_fixed_new(); button=gtk_button_new_with_label("改变主窗口label文字"); gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50); gtk_widget_set_size_request(button, 80, 65); gtk_container_add(GTK_CONTAINER(window_test), fixed); //gtk_widget_show(window); g_signal_connect(G_OBJECT(window_test),"delete_event",G_CALLBACK(delete_event), NULL); //注意这里不是“destroy” 事件 g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(change_label_text), NULL); //gtk_main(); //return 0; return window_test; }
运行如下:
点击“打开测试窗口”——子窗口打开了,点击关闭测试窗口——子窗口关闭了(隐藏了),并主窗口中有对应的文字显示(label)
点击子窗口中的按钮,主窗口中的label标签文字改变了
说明:本方法只是测试多窗口的交互而已,是很拙劣的方法。在我看来如果能实现下面这种就更好了,可我不会。
1.入口文件中,通过其他2个文件创建并返回了2个窗口。
2.那么如果能在入口文件中操作其他所有窗口以及窗口中的控件和信号就好了。
文章粗糙,边学边写,不足之处请谅解。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步