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

文章粗糙,邊學邊寫,不足之處請諒解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM