“菜單”(menubar)和“工具欄”(toolbars)

“菜單”(menubar)和“工具欄”(toolbars)

“菜單” (menubar)和“工具欄”(toolbars)

在這個部分的GTK+程序設計教程中,我們使用“菜單”和“工具欄”。

“菜單”( menubar) 是GUI程序中最爲常見的部分之一。各種各樣的命令和功能都可以藉以“菜單”來實現。 當我們習慣在終端(console)中啓動應用程序的時候,必須要記得很多複雜的命令和參數 ,在本章節中我們將 這一切都轉化爲可見的操作。菜單和工具欄中標準化的操作,將讓你擺脫學習新軟件所耗費的大量時間和精力。

簡單的菜單示列

在我們的第一個例子中,我們將生成一個含有文件菜單的菜單欄。文件菜單將只有一個菜單條(menu item)。如果點擊這個菜單條程序將退出。

#include <gtk/gtk.h>


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *quit;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  file = gtk_menu_item_new_with_label("File");
  quit = gtk_menu_item_new_with_label("Quit");

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

生成一個菜單欄的確會讓人有點疑惑。我們要牢記的是一個菜單欄和一個菜單都是源屬於同一個構件的,也就是菜單外殼(menu shell)。菜單選項(menu items )是一個只對菜單有效的子構件。他們通常用來實現子菜單。

menubar = gtk_menu_bar_new();
filemenu = gtk_menu_new();

在上面的代碼中我們生成了一個菜單欄構件(menubar)和一個菜單構件(menu)。

gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);

上面的代碼就會生成一個名爲“文件”的菜單。這也就是說其實菜單欄就是一個菜單外殼。很顯然這裏的文件菜單也是一個菜單外殼。這就是爲什麼我們把文件菜單稱爲子菜單或者說是一個子外殼。

gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);

菜單選項由函數 gtk_menu_shell_append() f來實現。然後一般情況下菜單選項被填加進菜單外殼裏。 在我們的這個例子中,“quit”菜單選項是被填加進“file”菜單欄裏,然後類似的是“file”菜單選項被填加進菜單中(menubar)。

g_signal_connect(G_OBJECT(quit), "activate",
      G_CALLBACK(gtk_main_quit), NULL);

當你單擊“quit”菜單按鈕,程序就會退出。


Simple menu
Figure: Simple menu

圖象菜單, mnemonics &accelerators

在接下來的這個例子中,我們將更進一步的去探索,在GTK+系統中我們可以應用的功能。Accelerators 是快捷鍵的意思,用來方便的用鍵盤上的組合鍵激活一個菜單選項。 Mnemonics也是是快捷鍵用於用於GUI的基礎。他們的具體表現都爲帶有下畫線的字符。(譯者:似乎說的不清楚啊,呵呵。具體的區別還是請參見下面的具體代碼實現以及對應的程序效果圖)

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *new;
  GtkWidget *open;
  GtkWidget *quit;

  GtkWidget *sep;

  GtkAccelGroup *accel_group = NULL;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);

  file = gtk_menu_item_new_with_mnemonic("_File");
  new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
  open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
  sep = gtk_separator_menu_item_new();
  quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);

  gtk_widget_add_accelerator(quit, "activate", accel_group, 
      GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 


  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), new);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

在上面的整個代碼程序中,向大家展示了是如何向一個菜單選項中去填加一個圖象的。當然也包括瞭如何使用accelerator 以及 mnemonics 。

accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
...
quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
gtk_widget_add_accelerator(quit, "activate", accel_group, 
    GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 

一個 accelerator 組有好多個accelerators快捷鍵。這裏我們生成了一個“Ctrl + q” accelerators快捷鍵。

file = gtk_menu_item_new_with_mnemonic("_File");

我們需要調用函數 gtk_menu_item_new_with_mnemonic() 來生成一個mnemonic快捷鍵。你可以按下鍵盤上的“ Alt + F”就可以看到. mnemonic快捷鍵的效果。

new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);

在上面的代碼中我們生成了兩個帶有圖象的菜單選項。在所調用的函數的第二個參數中,我們設置爲NULL,這樣達到的效果是我們自動生成了accelerators快捷鍵。我們也爲菜單選項分別提供了圖象與文字。

sep = gtk_separator_menu_item_new();

菜單選項能夠被一個水平的分割線隔開。這樣的話我們就可以從邏輯上把一些菜單選項給區分開來。


Menu example
Figure: Menu example

選擇(Check)菜單選項 (menu item)

GtkCheckMenuItem 便是一個可以生成帶有選擇的菜單選項。

#include <gtk/gtk.h>


void toggle_statusbar(GtkWidget *widget, gpointer statusbar) 
{
  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
    gtk_widget_show(statusbar);
  } else {
    gtk_widget_hide(statusbar);
  }
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *viewmenu;
  GtkWidget *view;
  GtkWidget *tog_stat;
  GtkWidget *statusbar;
  

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "view statusbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  viewmenu = gtk_menu_new();

  view = gtk_menu_item_new_with_label("View");
  tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");
  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(tog_stat), TRUE);

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewmenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(viewmenu), tog_stat);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), view);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  statusbar = gtk_statusbar_new();
  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(tog_stat), "activate", 
        G_CALLBACK(toggle_statusbar), statusbar);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

在我們的代碼示例中,我們展示瞭如何去製造一個帶有選擇框的菜單選項。具體的功能是:如果選擇框被選中則“狀態欄”就會顯示出來,反之則不會顯示。

tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");

函數gtk_check_menu_item_new_with_label() 可以生成一個新的帶有選擇框的菜單選項。

if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
  gtk_widget_show(statusbar);
} else {
  gtk_widget_hide(statusbar);
}

如果選擇框被選中則“狀態欄”就會顯示出來,反之則不會顯示。


Check menu item
Figure: Check menu item

工具欄(A toolbar)

菜單欄爲我們編程時實現某種功能提供了方便與快捷。在接下來的章節中,我們將爲你展示一種在特定情況下可以更加便捷的的方法——製造一個“工具欄。”

#include <gtk/gtk.h>


int main( int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *vbox;
  
  GtkWidget *toolbar;
  GtkToolItem *new;
  GtkToolItem *open;
  GtkToolItem *save;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "toolbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

  open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), open, -1);

  save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), save, -1);

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

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

以上的代碼中,我們製作了一個簡單的工具欄實現。

toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS)

從上面的兩行代碼中你應該可以看出來,我們生成了一個“嶄新”的工具欄:)。我們還特地使他們都用圖片來顯示,沒有包含文字。

new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

從 stock中我們生成了一個新的工具欄按鈕。要想把工具欄按鈕插入到工具欄中,很簡單!只需要調用函數 gtk_toolbar_insert() 就可以搞定。

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

上面的代碼,我們生成了一個分割線把工具欄按鈕們分開(只是邏輯上的分組需要)。


Toolbar
Figure: Toolbar

功能失效(Undo redo)

在接下來的例子中,我們將展示一個神奇的功能:使工具欄中的一個按鈕功能失效(讓他變成陰影)。這在GUI設計中是一個常見的技巧。 舉個例子:當我們把一片文章單擊保存後,那個保存按鈕就會變成陰影狀,也就是功能失效了。就是來提示你:保存功能已經執行過了,不需要再執行保存功能了。

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


void undo_redo(GtkWidget *widget,  gpointer item) 
{
  static int count = 2;
  const char *name = gtk_widget_get_name(widget);

  if ( strcmp(name, "undo") ) {
    count++;
  } else {
    count--;
  }
 
  if (count < 0) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  } 

  if (count > 5) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  }
}


int main( int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *toolbar;
  GtkToolItem *undo;
  GtkToolItem *redo;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "undoredo");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  undo = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
  gtk_widget_set_name(GTK_WIDGET(undo), "undo");
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);

  redo = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), redo, -1);

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

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

  g_signal_connect(G_OBJECT(undo), "clicked", 
        G_CALLBACK(undo_redo), redo);

  g_signal_connect(G_OBJECT(redo), "clicked", 
        G_CALLBACK(undo_redo), undo);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

我們的例子中用到了 GTK+ stock 來實現是 “功能有效”還是“功能失效”。當你單擊幾下 按鈕後,那個按鈕就會變成陰影狀,也就是說他從“功能有效”變成了“功能失效”。

if (count < 0) {
   gtk_widget_set_sensitive(widget, FALSE);
   gtk_widget_set_sensitive(item, TRUE);
} 

if (count > 5) {
   gtk_widget_set_sensitive(widget, FALSE);
   gtk_widget_set_sensitive(item, TRUE);
}

gtk_widget_set_sensitive() 是被用來告訴計算機是否要擊活一個工具欄按鈕。


Undo redo
Figure: Undo redo
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章