(四)、一步一步学GTK+之多窗口

(四)、一步一步学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.那么如果能在入口文件中操作其他所有窗口以及窗口中的控件和信号就好了。

文章粗糙,边学边写,不足之处请谅解。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值