GTK   メニュー

ホーム   C/C++チュートリアル


メニュー


menu.c


#include <gtk/gtk.h>

int main(int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *menubar;
    GtkWidget *filemenu;
    GtkWidget *filemenuitem;
    GtkWidget *quitmenuitem;
    
    gtk_init(&argc, &argv);
    
    window  = gtk_window_new        (0);
    gtk_window_set_title            (GTK_WINDOW(window), "Menu");
    gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
    gtk_window_set_position         (GTK_WINDOW(window), 1);
    /* メニューに干渉しますのでボーターは設定しません
    gtk_container_set_border_width  (GTK_CONTAINER(window), 15);
    */
    
    vbox    = gtk_box_new           (1, 0);
    /* GTK_ORIENTATION_VERTICAL定数の値は 1 です */
    gtk_container_add               (GTK_CONTAINER(window), vbox);
    
    menubar = gtk_menu_bar_new      ();
    filemenu
            = gtk_menu_new          ();
    
    filemenuitem
            = gtk_menu_item_new_with_label("File");
    quitmenuitem
            = gtk_menu_item_new_with_label("Quit");
            
    gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                     filemenuitem);
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(filemenuitem),
                                     filemenu);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     quitmenuitem);
    
    gtk_box_pack_start              (GTK_BOX(vbox), menubar, 0, 0, 0);
    /* 第3引数と第4引数の 0 はFALSEという意味です */
    g_signal_connect                (quitmenuitem,  "activate",
                                     G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect                (window,        "destroy",
                                     G_CALLBACK(gtk_main_quit), NULL);
    
    gtk_widget_show_all             (window);
    gtk_main                        ();
    return                          0;
}
    

実行結果

コード説明

  1. menubar = gtk_menu_bar_new();
    gtk_menu_bar_new関数でメニューバーを作ります。引数はありません。
  2. filemenu = gtk_menu_new();
    gtk_menu_new関数でメニューを作ります。引数はありません。
  3. メニューバーもメニューもメニューアイテム(menu item、メニュー項目)を 容れるための容器(shell、貝殻)です。
  4. gtk_menu_shell_append(GTK_MENU_SHELL(menubar), filemenuitem);
    メニューバーという容器にファイルメニューアイテムを入れます。
  5. gtk_menu_item_set_submenu(GTK_MENU_ITEM(filemenuitem), filemenu);
    ファイルメニューアイテムにさらにファイルメニューという容器を設定します。
  6. gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quitmenuitem);
    ファイルメニューという容器にクィットメニューアイテムを入れます。
  7. g_signal_connect(quitmenuitem, "activate", G_CALLBACK(gtk_main_quit), NULL);
    メニューアイテムが選択された時のイベントは "activate" です。


サブメニュー

submenu.c


#include <gtk/gtk.h>

int main(int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *vbox;
    
    GtkWidget *menubar;
    GtkWidget *filemenu;
    GtkWidget *importmenu;
    GtkWidget *filemenuitem;
    GtkWidget *importmenuitem;
    GtkWidget *bookmenuitem;
    GtkWidget *mailmenuitem;
    GtkWidget *quitmenuitem;
    GtkWidget *separate;
    
    gtk_init(&argc, &argv);
    
    window  = gtk_window_new        (0);
    gtk_window_set_title            (GTK_WINDOW(window), "SubMenu");
    gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
    gtk_window_set_position         (GTK_WINDOW(window), 1);
    /* メニューに干渉しますのでウィンドウのボーダーは設定しません
    gtk_container_set_border_width  (GTK_CONTAINER(window), 15);
    */
    
    vbox    = gtk_box_new           (1, 0);
    /* GTK_ORIENTATION_VERTICAL定数の値は 1 です */
    gtk_container_add               (GTK_CONTAINER(window), vbox);
    
    menubar = gtk_menu_bar_new      ();
    filemenu
            = gtk_menu_new          ();
    importmenu
            = gtk_menu_new          ();
    
    filemenuitem
            = gtk_menu_item_new_with_label("ファイル");
    importmenuitem
            = gtk_menu_item_new_with_label("インポート");
    bookmenuitem
            = gtk_menu_item_new_with_label("ブックマークをインポート");
    mailmenuitem
            = gtk_menu_item_new_with_label("メールをインポート");
    quitmenuitem
            = gtk_menu_item_new_with_label("終了");
    separate
            = gtk_separator_menu_item_new ();
           
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(filemenuitem),
                                     filemenu);
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(importmenuitem),
                                     importmenu);
            
    gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                     filemenuitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     importmenuitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     separate);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     quitmenuitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(importmenu),
                                     bookmenuitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(importmenu),
                                     mailmenuitem);
    
    gtk_box_pack_start              (GTK_BOX(vbox), menubar, 0, 0, 0);
                            /* 第3引数と第4引数の 0 はFALSEという意味です */
    g_signal_connect                (quitmenuitem,  "activate",
                                     G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect                (window,        "destroy",
                                     G_CALLBACK(gtk_main_quit), NULL);
    
    gtk_widget_show_all             (window);
    gtk_main                        ();
    return                          0;
}
    

実行結果

コード説明

  1. importmenu = gtk_menu_new();
    今回はサブメニュー用に importmenu というシェル(容器)も作ります。
  2. separate = gtk_separator_menu_item_new ();
    セパレーター(区切り線)というメニューアイテム作っています。
  3. gtk_menu_item_set_submenu(GTK_MENU_ITEM(importmenuitem), importmenu);
    インポートメニューアイテムにインポートメニューというシェル(容器)を 設定しています。
  4. gtk_menu_shell_append(GTK_MENU_SHELL(importmenu), bookmenuitem);
    インポートメニューというシェル(容器)にブックメニューアイテムを容れます。 これによりサブメニューが実現します。


ショートカットとイメージ

shortcutmenu.c


#include <gtk/gtk.h>

void select_menuitem();

int main(int argc, char *argv[])
{
    GtkWidget       *window;
    GtkWidget       *vbox;
    GtkWidget       *menubar;
    GtkWidget       *filemenu;
    GtkWidget       *filemenuitem;
    GtkWidget       *newmenuitem;
    GtkWidget       *openmenuitem;
    GtkWidget       *savemenuitem;
    GtkWidget       *separator;
    GtkWidget       *quitmenuitem;
    GtkWidget       *label;
    GtkAccelGroup   *accel_group;
    
    gtk_init(&argc, &argv);

    window  = gtk_window_new        (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title            (GTK_WINDOW(window),
                                     "Shourtcut menu");
    gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
    gtk_window_set_position         (GTK_WINDOW(window), 1);
    
    vbox    = gtk_box_new           (GTK_ORIENTATION_VERTICAL, 0);
    gtk_container_add               (GTK_CONTAINER(window), vbox);
    
    menubar = gtk_menu_bar_new      ();
    filemenu
            = gtk_menu_new          ();
    filemenuitem
            = gtk_menu_item_new_with_label  ("File");
    newmenuitem
            = gtk_menu_item_new_with_label  ("New");
    openmenuitem
            = gtk_menu_item_new_with_label  ("Open");
    savemenuitem
            = gtk_menu_item_new_with_label  ("Save");
    separator
            = gtk_separator_menu_item_new   ();
    quitmenuitem
            = gtk_menu_item_new_with_label  ("Quit");
    
    label   = gtk_label_new                 ("未選択");        
    
    accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

    gtk_box_pack_start              (GTK_BOX(vbox), menubar, 0, 0, 0);
    gtk_box_pack_end                (GTK_BOX(vbox), label,   1, 0, 0);
    
    gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                     filemenuitem);
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(filemenuitem),
                                     filemenu);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     newmenuitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     openmenuitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     savemenuitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     separator);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     quitmenuitem);
    gtk_widget_add_accelerator  (newmenuitem,  "activate", accel_group,
                                 GDK_KEY_n, GDK_CONTROL_MASK,
                                 GTK_ACCEL_VISIBLE);
    gtk_widget_add_accelerator  (openmenuitem, "activate", accel_group,
                                 GDK_KEY_o, GDK_CONTROL_MASK,
                                 GTK_ACCEL_VISIBLE);
    gtk_widget_add_accelerator  (savemenuitem, "activate", accel_group,
                                 GDK_KEY_s, GDK_CONTROL_MASK, 
                                 GTK_ACCEL_VISIBLE);
    gtk_widget_add_accelerator  (quitmenuitem, "activate", accel_group,
                                 GDK_KEY_q, GDK_CONTROL_MASK,
                                 GTK_ACCEL_VISIBLE);
    g_signal_connect            (newmenuitem,   "activate",
                                 G_CALLBACK(select_menuitem),   label);
    g_signal_connect            (openmenuitem,  "activate",
                                 G_CALLBACK(select_menuitem),   label);
    g_signal_connect            (savemenuitem,  "activate",
                                 G_CALLBACK(select_menuitem),   label);
    g_signal_connect            (quitmenuitem,  "activate",
                                 G_CALLBACK(gtk_main_quit),     NULL);
    g_signal_connect            (window,        "destroy",
                                 G_CALLBACK(gtk_main_quit),     NULL);
    
    gtk_widget_show_all(window);
    gtk_main();
    return 0;
}

void select_menuitem(GtkWidget *widget, gpointer label)
{
    const gchar *name = gtk_menu_item_get_label(GTK_MENU_ITEM(widget));
    gtk_label_set_label(label, name);
}
    

実行結果

コード説明

  1. GtkAccelGroup *accel_group;
    GtkAccelGroupのaccel_groupというポインターを定義しています。 ウィジェットは アクセルグループに登録することによってショートカットキーが 有効になります。
  2. accel_group = gtk_accel_group_new ();
    gtk_accel_group_new()関数でアクセルグループを作ります。引数はありません。
  3. gtk_window_add_accel_group (GTK_WINDOW(window), accel_group);
    ウィンドウにアクセルグループを追加します。
  4. gtk_widget_add_accelerator 関数でウィジェットにアクセラレータを追加します。ショートカットキーのことを 正確にはアクセラレータと言います。引数は次のとおりです。
    1. 第1引数にアクセラレータを追加するウィジェットを指定します
    2. 第2引数にアクセラレータと結びつけるイベントを指定します
    3. 第3引数にアクセルグループを指定します
    4. 第4引数に登録するキーを指定します。これは定数になっていますが、 多くのキーは、GDK_KEY_キー という形で登録できるみたいです
    5. 第5引数にキーと組み合わせるモディファイキー(modify key、修飾キー) を指定します。登録できる修飾キーは次のとおりです
      GDK_SHIFT_MASKシフトキー
      GDK_CONTROL_MASKコントロールキー
      GDK_MOD1_MASKアルト(Alt)キー
    6. 第6引数にアクセラレータの状態を指定するみたいです。状態は次のとおりです
      GTK_ACCEL_VISIBLE見える状態
      GTK_ACCEL_LOCKED取り外しできない状態
      GGTK_ACCEL_MASKどういう状態か分かりません


    shortcutmenu2.c

    • 次は、ショートカットキーとイメージの両方がついたサンプルです。
    • このサンプルは、GTK3では、コンパイル時に警告が出ますが、実行に問題はありません。
    • ショートカットキー付きメニューは、すべてのプラットフォームで有効になります。
    • イメージ付きメニューは、Linux Mintだけで有効になります。
    • Linuxでは、gconf-editorというアプリケーションで、 desktop→gnome→interfaceのmenus_have_iconsをオンにすれば
      イメージ付きメニューが表示されるという説明もありますが、 試してみた限りでは Mint 以外では有効になりませんでした。
    
    #include <gtk/gtk.h>
    
    void on_new();
    
    int main(int argc, char *argv[])
    {
        GtkWidget *window;
        GtkWidget *vbox;
        
        GtkWidget *menubar;
        GtkWidget *filemenu;
        GtkWidget *editmenu;
        GtkWidget *filemenuitem;
        GtkWidget *editmenuitem;
        GtkWidget *newmenuitem;
        GtkWidget *openmenuitem;
        GtkWidget *savemenuitem;
        GtkWidget *separator1;
        GtkWidget *quitmenuitem;
        GtkWidget *undomenuitem;
        GtkWidget *redomenuitem;
        GtkWidget *separator2;
        GtkWidget *cutmenuitem;
        GtkWidget *copymenuitem;
        GtkWidget *pastemenuitem;
        
        GtkAccelGroup * accel_group = NULL;
        
        gtk_init(&argc, &argv);
        
        window  = gtk_window_new        (0);
        /* GTK_WINDOW_TOPLEVEL定数の値は 0 です */
        gtk_window_set_title            (GTK_WINDOW(window), "SubMenu");
        gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
        gtk_window_set_position         (GTK_WINDOW(window), 1);
        /* GTK_WIN_POS_CENTER定数の値は 1 です */
        gtk_container_set_border_width  (GTK_CONTAINER(window), 15);
        
        vbox    = gtk_box_new           (1, 0);
        /* GTK_ORIENTATION_VERTICAL定数の値は 1 です */
        gtk_container_add               (GTK_CONTAINER(window), vbox);
        
        menubar = gtk_menu_bar_new      ();
        filemenu
                = gtk_menu_new          ();
        editmenu
                = gtk_menu_new          ();
        accel_group
                = gtk_accel_group_new   ();
        gtk_window_add_accel_group      (GTK_WINDOW(window), accel_group);
        
        filemenuitem
        = gtk_menu_item_new_with_label("File");
        newmenuitem
        = gtk_image_menu_item_new_from_stock("gtk-new",  accel_group);
        openmenuitem
        = gtk_image_menu_item_new_from_stock("gtk-open", accel_group);
        savemenuitem
        = gtk_image_menu_item_new_from_stock("gtk-save", accel_group);
        separator1
        = gtk_separator_menu_item_new ();
        quitmenuitem
        = gtk_image_menu_item_new_from_stock("gtk-quit", accel_group);
        editmenuitem
        = gtk_menu_item_new_with_label("Edit");
        undomenuitem
        = gtk_image_menu_item_new_from_stock("gtk-undo", accel_group);
        redomenuitem
        = gtk_image_menu_item_new_from_stock("gtk-redo", accel_group);
        separator2
        = gtk_separator_menu_item_new ();
        cutmenuitem
        = gtk_image_menu_item_new_from_stock("gtk-cut",  accel_group);
        copymenuitem
        = gtk_image_menu_item_new_from_stock("gtk-copy", accel_group);
        pastemenuitem
        = gtk_image_menu_item_new_from_stock("gtk-paste",accel_group);
        
        gtk_menu_item_set_submenu       (GTK_MENU_ITEM(filemenuitem),
                                         filemenu);
        gtk_menu_item_set_submenu       (GTK_MENU_ITEM(editmenuitem),
                                         editmenu);
                
        gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                         filemenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                         editmenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                         newmenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                         openmenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                         savemenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                         separator1);
        gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                         quitmenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(editmenu),
                                         undomenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(editmenu),
                                         redomenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(editmenu),
                                         separator2);
        gtk_menu_shell_append           (GTK_MENU_SHELL(editmenu),
                                         cutmenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(editmenu),
                                         copymenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(editmenu),
                                         pastemenuitem);
        
        gtk_box_pack_start              (GTK_BOX(vbox), menubar, 0, 0, 0);
                                /* 第3引数と第4引数の 0 はFALSEという意味です */
        
        g_signal_connect                (newmenuitem,   "activate",
                                         G_CALLBACK(on_new),        NULL);
        g_signal_connect                (quitmenuitem,  "activate",
                                         G_CALLBACK(gtk_main_quit), NULL);
        g_signal_connect                (window,        "destroy",
                                         G_CALLBACK(gtk_main_quit), NULL);
        
        gtk_widget_show_all             (window);
        gtk_main                        ();
        return                          0;
    }
    
    void on_new()
    {
        printf("New\n");
    }
    	

    実行結果

    • 新しいGTKでは、gtk_image_menu_item_new_from_stock関数が 非推奨(deprecated)になっているため、コンパイル時に警告(warning)が出ますが、 問題なく動作します。
    • ショートカットキーの付いたメニューアイテムには イベントハンドラーを設定できますが、今回は、 「新規」と「終了」だけに処理を施しました。
    • 「新規」メニューを選ぶとターミナルに「New」と表示されます。

    コード説明

    1. GtkAccelGroup * accel_group = NULL;
      GtkAccelGroupのaccel_groupというポインターを定義しています。 メニューアイテムはアクセルグループに登録することによってショートカットキーが 有効になります。
    2. gtk_accel_group_new()関数でアクセルグループを作ります。引数はありません。
    3. gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
      ウィンドウにアクセルグループを登録します。
    4. gtk_image_menu_item_new_from_stock("gtk-new", accel_group);
      画像付きメニューを作っています。
      1. 第1引数の"gtk-new"で、イメージと メニューアイテムに各言語に合わせて表示される文字列を指定します
      2. 第2引数にアクセルグループを指定します。ここに NULL を指定するとショートカットキーは作られません。
    5. Undo(元に戻す) と Redo(やり直す)にはショートカットキーが付かないみたいです。


    ステータスバーとチェックメニュー

    • ステータスバーとチェックメニューを説明します。

    statusbar.c

    
    #include <gtk/gtk.h>
    
    void showmessage();
    
    int main(int argc, char *argv[])
    {
        GtkWidget *window;
        GtkWidget *vbox;
        
        GtkWidget *menubar;
        GtkWidget *viewmenuitem;
        GtkWidget *viewmenu;
        GtkWidget *showmenuitem;
        GtkWidget *statusbar;
        
        guint      statusbar_id;
        
        gtk_init(&argc, &argv);
        
        window  = gtk_window_new        (0);
        /* GTK_WINDOW_TOPLEVEL定数の値は 0 です */
        gtk_window_set_title            (GTK_WINDOW(window),
                                         "SatusBar");
        gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
        gtk_window_set_position         (GTK_WINDOW(window), 1);
        /* GTK_WIN_POS_CENTER定数の値は 1 です */
        gtk_container_set_border_width  (GTK_CONTAINER(window), 0);
        /* 今回はウィンドウのボーダー幅を 0 にしました */
        
        vbox    = gtk_box_new           (1, 0);
        /* GTK_ORIENTATION_VERTICAL定数の値は 1 です */
        gtk_container_add               (GTK_CONTAINER(window), vbox);
        
        menubar = gtk_menu_bar_new      ();
        viewmenu
                = gtk_menu_new          ();
        viewmenuitem
                = gtk_menu_item_new_with_label("View");
        showmenuitem
                = gtk_check_menu_item_new_with_label
                                        ("Show statusbar message");
        gtk_check_menu_item_set_active  (GTK_CHECK_MENU_ITEM(showmenuitem),
                                         TRUE);
        gtk_menu_item_set_submenu       (GTK_MENU_ITEM(viewmenuitem),
                                         viewmenu);
        gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                         viewmenuitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(viewmenu),
                                         showmenuitem);
        
        statusbar
                = gtk_statusbar_new     ();
        statusbar_id
                = gtk_statusbar_get_context_id
                                        (GTK_STATUSBAR(statusbar), "none");
        gtk_statusbar_push              (GTK_STATUSBAR(statusbar),
                                         statusbar_id,
                                         "This is a statusbar.");
        
        gtk_box_pack_start              (GTK_BOX(vbox), menubar,  0, 0, 0);
                                /* 第3引数と第4引数の 0 はFALSEという意味です */
        gtk_box_pack_end                (GTK_BOX(vbox), statusbar,0, 1, 0);
        
        g_signal_connect                (showmenuitem,  "activate",
                                         G_CALLBACK(showmessage),
                                                    statusbar);
        g_signal_connect                (window,        "destroy",
                                         G_CALLBACK(gtk_main_quit), NULL);
        
        gtk_widget_show_all             (window);
        gtk_main                        ();
        return                          0;
    }
    
    void showmessage(GtkWidget *menuitem, GtkWidget *statusbar)
    {
        guint id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar),
                                                "none");
        
        if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))
        {
            gtk_statusbar_push(GTK_STATUSBAR(statusbar), id,
                               "This is a statusbar.");
        } else {
            gtk_statusbar_push(GTK_STATUSBAR(statusbar), id,
                               "");
        }
    }
    	

    実行結果

    コード説明

    1. guint statusbar_id;
      guintはGTKで再定義された unsigned int です。 ステータスバーにメッセージを記述する時に id 番号を格納するために使います。
    2. gtk_container_set_border_width(GTK_CONTAINER(window), 0);
      GTKのメニューとステータスバーはウィンドウの中に配置されるので、 ボーターをなくしました。
    3. gtk_check_menu_item_new_with_label関数でチェックメニューアイテムを作ります 引数にメニューに表示される文字列を指定します。
    4. gtk_check_menu_item_set_active関数で チェックメニューアイテムにチェックをつけます。
      1. 第1引数に対象のチェックメニューアイテムを指定します
      2. 第2引数にTRUEを指定するとチェックメニューアイテムにチェックが付きます
    5. gtk_statusbar_new関数でステータスバーを作ります。引数はいりません。
    6. gtk_statusbar_get_context_id関数でステータスバーのIDを整数で取得します。
      1. 第1引数にステータスバーを指定します
      2. 第2引数に任意の文字列を指定します
    7. gtk_statusbar_push関数でステータスバーに文字列を表示します。
      1. 第1引数にステータスバーを指定します
      2. 第2引数に先ほど取得したIDを指定します
      3. 第3引数にステータスバーに表示する文字列を指定します
    8. gtk_check_menu_item_get_active関数で 引数で指定したチェックメニューアイテムがチェックされているかどうかを調べます。
      チェックされていればTRUE、されていなければFALSEが返されます。


    ポップアップメニュー

    • ポップアップメニューは、右クリックすると、その場所に現れるメニューです。
    • コンテキストメニューとも呼ばれます。

    popupmenu.c

    
    #include <gtk/gtk.h>
    
    void show_popup();
    
    int main(int argc, char *argv[])
    {
        GtkWidget *window;
        
        GtkWidget *menu;
        GtkWidget *ebox;
        GtkWidget *minimize;
        GtkWidget *quit;
        
        gtk_init(&argc, &argv);
        
        window  = gtk_window_new        (0);
        /* GTK_WINDOW_TOPLEVEL定数の値は 0 です */
        gtk_window_set_title            (GTK_WINDOW(window), "PopupMenu");
        gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
        gtk_window_set_position         (GTK_WINDOW(window), 1);
        /* GTK_WIN_POS_CENTER定数の値は 1 です */
        //gtk_container_set_border_width  (GTK_CONTAINER(window), 15);
        /* ボーダーは今回設定しません */
        
        ebox    = gtk_event_box_new     ();
        gtk_container_add               (GTK_CONTAINER(window), ebox);
        
        menu    = gtk_menu_new          ();
        minimize
                = gtk_menu_item_new_with_label
                                        ("Minimize");
        quit    = gtk_menu_item_new_with_label
                                        ("Quit");
        gtk_menu_shell_append           (GTK_MENU_SHELL(menu), minimize);
        gtk_menu_shell_append           (GTK_MENU_SHELL(menu), quit);
        gtk_widget_show                 (minimize);
        gtk_widget_show                 (quit);
        
        g_signal_connect_swapped        (minimize,  "activate",
                                         G_CALLBACK(gtk_window_iconify),
                                                                  window);
        g_signal_connect                (quit,      "activate",
                                         G_CALLBACK(gtk_main_quit), NULL);
        g_signal_connect                (window,    "destroy",
                                         G_CALLBACK(gtk_main_quit), NULL);
        g_signal_connect_swapped        (ebox,      "button-press-event",
                                         G_CALLBACK(show_popup),    menu);
        
        gtk_widget_show_all             (window);
        gtk_main                        ();
        return                          0;
    }
    
    void show_popup(GtkWidget *widget, GdkEvent *event)
    {
        const gint RIGHT_CLICK = 3;
        
        GdkEventButton *eventbutton = (GdkEventButton *)event;
        if (eventbutton->button == RIGHT_CLICK)
        {
            gtk_menu_popup_at_pointer(GTK_MENU(widget), event);
        }
    }
    	

    実行結果


    ウィンドウ内を右クリックすると、その場所にメニューが現れます。Minimize を選択すると、ウィンドウが最小化します。

    コード説明

    1. gtk_event_box_new関数でイベントボックス(GtkEventBox)を作っています。
      イベントボックスは目には見えないウィジェットで ウィンドウなどに新しいイベントを追加するために使います。
    2. gtk_container_add(GTK_CONTAINER(window), ebox);
      イベントボックスをウィンドウ一杯に配置しています。
    3. gtk_widget_show(minimize);
      minimizeとquitは個別にshowしておかなければならないみたいです。 ウィンドウに所属していないから? と考えれば良いのでしょうか。
    4. g_signal_connect_swapped(minimize,"activate", G_CALLBACK(gtk_window_iconify), window);
      g_signal_connect関数では、イベントを発生したウィジェットが イベントハンドラーの第1引数に渡されます。
      g_signal_connect_swapped関数では、第4引数のウィジェットが イベントハンドラーの第1引数に渡されます。
    5. g_signal_connect(quit,"activate", G_CALLBACK(gtk_main_quit), NULL);
      gtk_main_quit関数は引数を取りませんので g_signal_connect関数とg_signal_connect_swapped関数のどちらでも 正常に動作します。
    6. g_signal_connect_swapped(ebox, "button-press-event", G_CALLBACK(show_popup), menu);
      g_signal_connect_swapped関数を使っていますので show_popupイベントハンドラーの第1引数に渡されるのはイベントボックスではなく menu ウィジェットです。
      ボタンが押された時のイベントは "button-press-event" です。
    7. void show_popup(GtkWidget *widget, GdkEvent *event)
      イベントボックスで発生したイベントでは、イベントハンドラーの第2引数として、 イベントそのものが自動的に渡されます。
    8. const gint RIGHT_CLICK = 3;
      マウスの右ボタンは 3 という数値で表されています。 後で分かりやすいように定数化しています。そして続けて次の処理をします。
      1. GdkEventButton *eventbutton = (GdkEventButton *)event;
        プレスされた(押された)ボタンを eventbutton ポインターに入れます。
      2. if (eventbutton->button == RIGHT_CLICK)
        そして、ボタンが 3 (右ボタン)だったら
      3. gtk_menu_popup_at_pointer(GTK_MENU(widget), event);
        gtk_menu_popup_at_pointer関数でポップアップメニューを表示します。 第1引数にメニューを、第2引数にイベントを渡します。


    ツールバー

    • GTK3対応のサンプルを作りました。
    • ただし、今の段階では、UbuntuとMint以外では ツールバーのアイコンが正しく表示されないみたいです。

    toolbar.c

    
    #include <gtk/gtk.h>
    #include <stdlib.h>     // for atoi
    #include <string.h>     // for strcmp
    
    GtkToolItem *undo;
    GtkToolItem *redo;
    
    void undo_redo();
    
    int main(int argc, char *argv[])
    {
        GtkWidget *window;
        GtkWidget *vbox;
        
        GtkWidget *toolbar;
        GtkWidget *undo_icon;
        GtkWidget *redo_icon;
        GtkWidget *quit_icon;
        GtkToolItem *sep;
        GtkToolItem *quit;
        GtkWidget *label;
            
        gtk_init(&argc, &argv);
        
        window  = gtk_window_new        (0);
        gtk_window_set_title            (GTK_WINDOW(window), "ToolBar");
        gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
        gtk_window_set_position         (GTK_WINDOW(window), 1);
        
        vbox    = gtk_box_new           (1, 0);
        /* 1 はGTK_ORIENTATION_VERTICAL定数の値です */
        gtk_container_add               (GTK_CONTAINER(window), vbox);
        
        toolbar = gtk_toolbar_new       ();
        /*
        gtk_toolbar_set_style           (GTK_TOOLBAR(toolbar),
                                         GTK_TOOLBAR_ICONS);
        設定しなくてもデフォルトでアイコン表示(GTK_TOOLBAR_ICONS)みたいです
        */
        gtk_container_set_border_width  (GTK_CONTAINER(toolbar), 0);
        
        undo_icon
                = gtk_image_new_from_icon_name("gtk-redo",
                                         GTK_ICON_SIZE_LARGE_TOOLBAR);
        undo    = gtk_tool_button_new   (undo_icon, "Undo");
        gtk_widget_set_name             (GTK_WIDGET(undo), "undo");
        /*
        この関数は必須ではありませんが、後でツールボタンを区別するために名前を使います */
        gtk_toolbar_insert              (GTK_TOOLBAR(toolbar), undo, -1);
        
        redo_icon
                = gtk_image_new_from_icon_name("gtk-redo",
                                         GTK_ICON_SIZE_LARGE_TOOLBAR);
        redo    = gtk_tool_button_new   (redo_icon, "Redo");
        gtk_widget_set_name             (GTK_WIDGET(redo), "redo");
        gtk_toolbar_insert              (GTK_TOOLBAR(toolbar), redo, -1);
        gtk_widget_set_sensitive        (GTK_WIDGET(redo), FALSE);
        
        sep     = gtk_separator_tool_item_new();
        gtk_toolbar_insert              (GTK_TOOLBAR(toolbar), sep,  -1);
        
        quit_icon
                = gtk_image_new_from_icon_name("gtk-quit",
                                         GTK_ICON_SIZE_LARGE_TOOLBAR);
        quit    = gtk_tool_button_new   (quit_icon, "Quit");
        gtk_toolbar_insert              (GTK_TOOLBAR(toolbar), quit, -1);
        
        label   = gtk_label_new         ("4");
        
        gtk_box_pack_start              (GTK_BOX(vbox), toolbar, 0, 0, 0);
        gtk_box_pack_end                (GTK_BOX(vbox), label,   1, 0, 0);
        
        g_signal_connect                (undo,      "clicked",
                                         G_CALLBACK(undo_redo),    label);
         
         g_signal_connect               (redo,      "clicked",
                                         G_CALLBACK(undo_redo),    label);
        
        g_signal_connect                (quit,      "clicked",
                                         G_CALLBACK(gtk_main_quit), NULL);
        
        g_signal_connect                (window,    "destroy",
                                         G_CALLBACK(gtk_main_quit), NULL);
        
        gtk_widget_show_all             (window);
        gtk_main                        ();
        return                          0;
    }
    
    void undo_redo(GtkWidget *widget, GtkLabel *label)
    {
        const gchar *text = gtk_label_get_text(label);
        const gchar *name = gtk_widget_get_name(widget);
        guchar       num  = atoi(text);
        
        if (strcmp(name, "undo") == 0)
        /* g_strcmp0 という関数もあります */
        {
            num--;
            switch (num)
            {
            case 0:
                gtk_widget_set_sensitive(GTK_WIDGET(undo), FALSE);
                break;
            case 3:
                gtk_widget_set_sensitive(GTK_WIDGET(redo), TRUE);
                break;
            }
        } else {
            num++;
            switch (num)
            {
            case 1:
                gtk_widget_set_sensitive(GTK_WIDGET(undo), TRUE);
                break;
            case 4:
                gtk_widget_set_sensitive(GTK_WIDGET(redo), FALSE);
                break;
            }
        }
        gchar *str = g_strdup_printf("%d", num);
        gtk_label_set_text(label, str);
        g_free(str);
    }
        

    実行結果

    コード説明

    1. #include <stdlib.h> // for atoi
      #include <string.h> // for strcmp
      環境によっては記述しないと警告が出る場合があります。
    2. GtkToolItem *undo;
      GtkToolItem *redo;
      g_signal_connect関数では イベントハンドラーに渡せる引数は、イベントを発したウィジェット以外に 追加で一つだけです。そのため undoとredoはグローバル変数にしました。 ツールボタンはGtkToolItem型になります。
    3. void undo_redo();
      GTKでは、プロトタイプ(関数前方宣言)には、引数の明記は必要ないみたいです。 気になる方は、引数も記述してください。
    4. toolbar = gtk_toolbar_new();
      ツールバーを作っています。引数はありません。
    5. gtk_toolbar_set_styleGTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
      ツールバーのスタイルを設定するのにこの関数を使います。 ただしツールバーはデフォルトでTOOL_BAR_ICONS(アイコン表示)に設定されています。
    6. gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
      この関数でツールバーの周りのボーダー(border、枠)の幅を設定できます。
    7. undo_icon = gtk_image_new_from_icon_name("gtk-undo", GTK_ICON_SIZE_LARGE_TOOLBAR);
      アイコンを作ります。引数は次のとおりです。
      1. 第1引数にアイコンの名前を "gtk-undo" という形で指定します
      2. 第2引数にアイコンのサイズを指定します。次のような定数があります
        GTK_ICON_SIZE_INVALID無効なサイズ
        GTK_ICON_SIZE_MENUメニューに適したサイズ(16px)
        GTK_ICON_SIZE_SMALL_TOOLBAR 小さなツールバーに適したサイズ(16px)
        GTK_ICON_SIZE_LARGE_TOOLBAR 大きなツールバーに適したサイズ(24px)
        GTK_ICON_SIZE_BUTTON ボタンに適したサイズ(16px)
        GTK_ICON_SIZE_DND ドラッグ&ドロップに適したサイズ(32px)
        GTK_ICON_SIZE_DIALOG ダイアログに適したサイズ(48px)
    8. redo = gtk_tool_button_new(redo_icon, "Redo");
      ツールボタンを作っています。第1引数にアイコンウィジェットを、 第2引数にツールボタンに表示する文字列を指定します。
    9. gtk_widget_set_name(GTK_WIDGET(redo), "redo");
      ウィジェットには、ウィジェットを識別するための名前を付けられます。 この名前は後で使います。
    10. gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);
      ツールバーにツールアイテムを挿入しています。引数は次のとおりです。
      1. 第1引数に挿入されるツールバーを指定します。ツールバーが一つとは限りません
      2. 第2引数に挿入するツールアイテムを指定します
      3. 第3引数に挿入する位置を指定します。挿入順序は左から 0、1、 2 になります。-1 を指定すると最後に追加されます。
    11. gtk_widget_set_sensitive(GTK_WIDGET(redo), FALSE);
      ウィジェットの使用が可能かどうかを設定しています。
      1. 第1引数に対象のウィジェットを指定します
      2. 第2引数にTRUEを指定した場合は、対象のウィジェットは使用可能になります。 FALSEを指定した場合は、対象のウィジェットは仕様不可能になります。
    12. sep = gtk_separator_tool_item_new();
      ツールバーのセパレーター(縦線)を作っています。
    13. g_signal_connect(undo, "clicked", G_CALLBACK(undo_redo), label);
      ツールボタンが押された時のイベントは "clicked" です。
    14. const gchar *name = gtk_widget_get_name(widget);
      先ほど設定したウィジェットの名前を取得しています。
    15. guchar num = atoi(text);
      atoiは C の関数です。引数の文字列を整数に変換した新たな値を作ります。 guchar は、GTKで再定義された unsigned char です。
    16. if (strcmp(name, "undo") == 0)
      strcmpも C の関数です。第1引数と第2引数の文字列を比較して、 第1引数が大きい場合は 1 を、第2引数が大きい場合は -1 を、 第1引数と第2引数が同じ場合は 0 を返します。
      文字の大小は辞書の順序になります。辞書の末尾の方が大きい文字列になります。 GTKには同じ働きをする g_strcmp0 という関数もあります。
    17. gchar *str = g_strdup_printf("%d", num);
      g_strdup_printf関数は、変換指定子で変換された新しい文字列を作ります。
    18. gtk_label_set_text(label, str);
      この関数でラベルに表示される文字列を設定します。
      1. 第1引数に対象となるラベルを指定します
      2. 第2引数に設定する文字列を指定します
    19. g_free(str);
      g_strdup_printf関数で作った文字列は、g_free関数で 文字列に割り当てられたメモリーを解放しておいた方が良いです。


    toolbar2.c

    • 念のために、GTK2対応のサンプルも残しておきます。
    • このサンプルもmacOSではツールバーのアイコンは正しく表示されません。
    
    #include <gtk/gtk.h>
    
    GtkToolItem *undo;
    GtkToolItem *redo;
    
    void undo_redo();
    
    int main(int argc, char *argv[])
    {
        GtkWidget *window;
        GtkWidget *vbox;
        
        GtkWidget *toolbar;
        GtkToolItem *sep;
        GtkToolItem *quit;
        GtkWidget *label;
            
        gtk_init(&argc, &argv);
        
        window  = gtk_window_new        (0);
        /* GTK_WINDOW_TOPLEVEL定数の値は 0 です */
        gtk_window_set_title            (GTK_WINDOW(window), "ToolBar");
        gtk_window_set_default_size     (GTK_WINDOW(window), 320, 240);
        gtk_window_set_position         (GTK_WINDOW(window), 1);
         /* 1 はGTK_WIN_POS_CENTER定数の値です */
        //gtk_container_set_border_width  (GTK_CONTAINER(window), 15);
        /* メニューやツールバーを設定する場合はボーダーはない方が良いです。 */
        
        vbox    = gtk_box_new           (1, 0);
        /* 1 はGTK_ORIENTATION_VERTICAL定数の値です */
        gtk_container_add               (GTK_CONTAINER(window), vbox);
        
        toolbar = gtk_toolbar_new       ();
        //gtk_toolbar_set_style           (GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
        /* 設定しなくてもデフォルトでアイコン表示(GTK_TOOLBAR_ICONS)みたいです */
        gtk_container_set_border_width  (GTK_CONTAINER(toolbar), 0);
        
        undo    = gtk_tool_button_new_from_stock("gtk-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-redo");
        gtk_widget_set_name             (GTK_WIDGET(redo), "redo");
        /* この関数は必須ではありませんが、後でツールボタンを区別するために名前を使います */
        gtk_toolbar_insert              (GTK_TOOLBAR(toolbar), redo, -1);
        gtk_widget_set_sensitive        (GTK_WIDGET(redo), FALSE);
        
        sep     = gtk_separator_tool_item_new();
        gtk_toolbar_insert              (GTK_TOOLBAR(toolbar), sep,  -1);
        
        quit    = gtk_tool_button_new_from_stock("gtk-quit");
        gtk_toolbar_insert              (GTK_TOOLBAR(toolbar), quit, -1);
        
        label   = gtk_label_new         ("4");
        
        gtk_box_pack_start              (GTK_BOX(vbox), toolbar, 0, 0, 0);
        /* gtk_box_pack_start関数の第3引数と第4引数の 0 はFALSEという意味です */
        gtk_box_pack_end                (GTK_BOX(vbox), label,   1, 1, 0);
        /* gtk_box_pack_start関数の第3引数と第4引数の 1 はTRUEという意味です  */
        
        g_signal_connect                (undo,      "clicked",
                                         G_CALLBACK(undo_redo),    label);
         
         g_signal_connect               (redo,      "clicked",
                                         G_CALLBACK(undo_redo),    label);
        
        g_signal_connect                (quit,      "clicked",
                                         G_CALLBACK(gtk_main_quit), NULL);
        
        g_signal_connect                (window,    "destroy",
                                         G_CALLBACK(gtk_main_quit), NULL);
        
        gtk_widget_show_all             (window);
        gtk_main                        ();
        return                          0;
    }
    
    void undo_redo(GtkWidget *widget, GtkLabel *label)
    {
        const gchar *text = gtk_label_get_text(label);
        const gchar *name = gtk_widget_get_name(widget);
        guchar       num  = atoi(text);
        
        if (strcmp(name, "undo") == 0)
        /* g_strcmp0 という関数もあります */
        {
            num--;
            switch (num)
            {
            case 0:
                gtk_widget_set_sensitive(GTK_WIDGET(undo), FALSE);
                break;
            case 3:
                gtk_widget_set_sensitive(GTK_WIDGET(redo), TRUE);
                break;
            }
        } else {
            num++;
            switch (num)
            {
            case 1:
                gtk_widget_set_sensitive(GTK_WIDGET(undo), TRUE);
                break;
            case 4:
                gtk_widget_set_sensitive(GTK_WIDGET(redo), FALSE);
                break;
            }
        }
        gchar *str = g_strdup_printf("%d", num);
        gtk_label_set_text(label, str);
        g_free(str);
    }
    	

    実行結果

    コード説明

    1. GtkToolItem *undo;
      GtkToolItem *redo;
      g_signal_connect関数では イベントハンドラーに追加で一つまでしか引数を渡せないので、undoとredoは グローバル変数にしました。ツールボタンはGtkToolItem型で受けます。
    2. toolbar = gtk_toolbar_new();
      ツールバーを作っています。引数はありません。
    3. //gtk_toolbar_set_styleGTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
      ツールバーのスタイルを設定するのにこの関数を使います。 ただしツールバーはデフォルトでTOOL_BAR_ICONS(アイコン表示)に設定されています。
    4. gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
      この関数でツールバーの周りのボーダー(border、枠)の幅を設定できます。
    5. undo = gtk_tool_button_new_from_stock("gtk-undo");
      この関数はGTK3では、非推奨(deprecated)になっています。 "gtk-undo"がストックアイコンになります。
    6. gtk_widget_set_name(GTK_WIDGET(redo), "redo");
      ウィジェットには、ウィジェットを識別するための名前を付けられます。
    7. gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);
      この関数でツールバーにツールアイテムを挿入します。
      1. 第1引数に挿入されるツールバーを指定します。ツールバーが一つとは限りません
      2. 第2引数に挿入するツールアイテムを指定します
      3. 第3引数に挿入する位置を指定します。挿入順序は左から 0、1、 2 になります。-1 を指定すると最後に追加されます。
      4. gtk_widget_set_sensitive(GTK_WIDGET(redo), FALSE);
        この関数でウィジェットの使用が可能かどうかを設定できます。
        1. 第1引数に対象のウィジェットを指定します
        2. 第2引数にTRUEを指定した場合は、対象のウィジェットは使用可能になります。 FALSEを指定した場合は、対象のウィジェットは仕様不可能になります。
      5. sep = gtk_separator_tool_item_new();
        この関数でツールバーのセパレーター(縦線)を作ります。
      6. g_signal_connect(undo, "clicked", G_CALLBACK(undo_redo), label);
        ツールボタンが押された時のイベントは "clicked" です。
      7. const gchar *name = gtk_widget_get_name(widget);
        この関数で引数に指定したウィジェットの名前を取得できます。
      8. guchar num = atoi(text);
        atoiは C の関数です。引数の文字列を整数に変換した新たな値を作ります。 guchar は、GTKで再定義された unsigned char です。
      9. if (strcmp(name, "undo") == 0)
        strcmpも C の関数です。第1引数と第2引数の文字列を比較して、 第1引数が大きい場合は 1 を、第2引数が大きい場合は -1 を、 第1引数と第2引数が同じ場合は 0 を返します。
        文字の大小は辞書の順序になります。辞書の末尾の方が大きい文字列になります。 GTKには同じ働きをする g_strcmp0 という関数もあります。
      10. gchar *str = g_strdup_printf("%d", num);
        g_strdup_printf関数は、変換指定子で変換された新しい文字列を作ります。
      11. gtk_label_set_text(label, str);
        この関数でラベルに表示される文字列を設定します。
        1. 第1引数に対象となるラベルを指定します
        2. 第2引数に設定する文字列を指定します
      12. g_free(str);
        g_strdup_printf関数で作った文字列は、g_free関数で 文字列に割り当てられたメモリーを解放しておいた方が良いです。



64320 visits
Posted: Dec. 21, 2019
Update: Jan. 01, 2020

ホーム   C/C++チュートリアル   目次