GTK3   メニュー

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


メニュー


menu.c


#include <gtk/gtk.h>

void activate();

int main(int argc, char **argv)
{
    GtkApplication  *app;
    int             status;
    
    app     =   gtk_application_new ("jp.vivacocoa.menu"  ,     0   );
    g_signal_connect                (app,       "activate",
                                     G_CALLBACK( activate),     NULL);
    status  =   g_application_run   (G_APPLICATION(app), argc,  argv);
    g_object_unref                  (app);
    return      status;
}

void activate(GtkApplication *app, gpointer data)
{
    GtkWidget   *window;
    GtkWidget   *vbox;
    GtkWidget   *menubar;
    GtkWidget   *fileitem;
    GtkWidget   *filemenu;
    GtkWidget   *quititem;
    
    /*  Window          */
    window  =
    gtk_application_window_new      (app);
    gtk_window_set_title            (GTK_WINDOW(window),    "Menu"  );
    gtk_window_set_default_size     (GTK_WINDOW(window),    300, 200);
    gtk_window_set_position         (GTK_WINDOW(window),    1       );
    
    /*  Container       */
    vbox    =   gtk_box_new         (GTK_ORIENTATION_VERTICAL,  0   );
    gtk_container_add               (GTK_CONTAINER(window), vbox    );
    
    /*  Menu creation   */
    menubar
    =   gtk_menu_bar_new            ();
    fileitem
    =   gtk_menu_item_new_with_label("File");
    filemenu
    =   gtk_menu_new                ();
    quititem
    =   gtk_menu_item_new_with_label("Quit");
    
    /*  Menu settings   */
    gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                     fileitem   );
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(fileitem),
                                     filemenu   );
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     quititem   );
    
    /*  Pack to box     */
    gtk_box_pack_start              (GTK_BOX(vbox), menubar, 0, 0, 0);
    
    /*  Signal          */
    g_signal_connect_swapped        (quititem,      "activate",
                                     G_CALLBACK(gtk_widget_destroy  ),
                                     window);
    
    gtk_widget_show_all             (window);
}
    

実行結果

コード説明

  1. menubar = gtk_menu_bar_new();
    gtk_menu_bar_new( )関数でメニューバーを作ります。引数はありません。
  2. fileitem = gtk_menu_item_new_with_label("File");
    gtk_menu_item_new_labeL( )関数でメニューアイテムを作ります。 引数にメニューアイテムに表示される文字列を指定します。
  3. filemenu = gtk_menu_new();
    gtk_menu_new( )関数でメニューを作ります。引数はありません。
  4. メニューバーもメニューもメニューアイテム(menu item、メニュー項目)を 入れるための容器(shell、貝殻)です。
  5. gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileitem);
    メニューバーという容器にファイルメニューアイテムを入れます。
  6. gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileitem), filemenu);
    ファイルメニューアイテムにファイルメニューという容器を設定します。
  7. gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quititem);
    ファイルメニューという容器にクィットメニューアイテムを入れます。
  8. g_signal_connect_swapped(quititem, "activate", G_CALLBACK(gtk_widget_destroy ), window);
    クィッとメニューアイテムのイベントをgtk_widget_destroy関数と結びつけています。 引数は次のとおりです。
    • 第1引数にイベントを発生するウィジェットを指定します
    • 第2引数にイベントの種類を指定します。 メニューアイテムが選択された時のイベントは "activate" です。
    • 第3引数にG_CALLBACK( )マクロで囲んでイベントハンドラーを指定します。 gtk_widget_destroy関数は、第1引数と受け取ったウィジェットを破棄します
    • 第4引数にイベントハンドラーに引数として渡すウィジェット指定します。 これはg_signal_connect関数の種類により次のように変わります
      g_signall_connect イベントハンドラーの最後の引数になります
      g_siganll_connect_swapped イベントハンドラーの第1引数になります


サブメニュー

submenu.c


#include <gtk/gtk.h>

void activate();

int main(int argc, char **argv)
{
    GtkApplication  *app;
    int             status;
    
    app     =   gtk_application_new ("jp.vivacocoa.submenu",    0   );
    g_signal_connect                (app,       "activate",
                                     G_CALLBACK( activate),     NULL);
    status  =   g_application_run   (G_APPLICATION(app), argc,  argv);
    g_object_unref                  (app);
    return  status;
}

void activate(GtkApplication *app, gpointer data)
{
    GtkWidget   *window;
    GtkWidget   *vbox;
    GtkWidget   *menubar;
    GtkWidget   *filemenu;
    GtkWidget   *importmenu;
    GtkWidget   *fileitem;
    GtkWidget   *importitem;
    GtkWidget   *bookitem;
    GtkWidget   *mailitem;
    GtkWidget   *separator;
    GtkWidget   *quititem;
    
    /*  Window          */
    window  =
    gtk_application_window_new      (app);
    gtk_window_set_title            (GTK_WINDOW(window),
                                     "SubMenu");
    gtk_window_set_default_size     (GTK_WINDOW(window),    300, 200);
    gtk_window_set_position         (GTK_WINDOW(window),    1       );
    
    /*  Container       */
    vbox    =   gtk_box_new         (GTK_ORIENTATION_VERTICAL,  0   );
    gtk_container_add               (GTK_CONTAINER(window),     vbox);
    
    /*  Menu creation   */
    menubar
            =   gtk_menu_bar_new    ();
    filemenu
            =   gtk_menu_new        ();
    importmenu
            =   gtk_menu_new        ();
    fileitem
    =   gtk_menu_item_new_with_label("File");
    importitem
    =   gtk_menu_item_new_with_label("Import");
    bookitem
    =   gtk_menu_item_new_with_label("Bookmark..");
    mailitem
    =   gtk_menu_item_new_with_label("Mail...");
    separator
    =   gtk_separator_menu_item_new ();
    quititem
    =   gtk_menu_item_new_with_label("Quit");
     
    /*  Menu settings   */
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(fileitem),
                                     filemenu);
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(importitem),
                                     importmenu);
    gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                     fileitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     importitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     separator);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     quititem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(importmenu),
                                     bookitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(importmenu),
                                     mailitem);
    
    /*  Pack to box     */
    gtk_box_pack_start              (GTK_BOX(vbox), menubar, 0, 0, 0);
    
    /*  Signal          */
    g_signal_connect_swapped        (quititem,      "activate",
                                     G_CALLBACK(gtk_widget_destroy),
                                     window);
    
    gtk_widget_show_all             (window);
}
    

実行結果

コード説明

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


ショートカットメニュー

shortcutmenu.c


#include <gtk/gtk.h>

void activate();
void select_item();

int main(int argc, char **argv)
{
    GtkApplication  *app;
    int             status;
    
    app     =   gtk_application_new ("jp.vivacocoa.shortcutmenu", 0 );
    g_signal_connect                (app,       "activate",
                                     G_CALLBACK( activate),     NULL);
    status  =   g_application_run   (G_APPLICATION(app), argc,  argv);
    g_object_unref                  (app);
    return  status;
}

void activate(GtkApplication *app, gpointer data)
{
    GtkWidget       *window;
    GtkWidget       *vbox;
    GtkAccelGroup   *accel_group;
    GtkWidget       *menubar;
    GtkWidget       *filemenu;
    GtkWidget       *fileitem;
    GtkWidget       *newitem;
    GtkWidget       *openitem;
    GtkWidget       *saveitem;
    GtkWidget       *separator;
    GtkWidget       *quititem;
    GtkWidget       *label;
    
    /*  Window          */
    window  =
    gtk_application_window_new      (app);
    gtk_window_set_title            (GTK_WINDOW(window),
                                     "ShortCutMenu");
    gtk_window_set_default_size     (GTK_WINDOW(window),    300, 200);
    gtk_window_set_position         (GTK_WINDOW(window),    1       );
    
    /*  Container       */
    vbox    =   gtk_box_new         (GTK_ORIENTATION_VERTICAL,  0   );
    gtk_container_add               (GTK_CONTAINER(window),     vbox);
    
    /*  Menu creation   */
    menubar
            =   gtk_menu_bar_new    ();
    filemenu
            =   gtk_menu_new        ();
    fileitem
    =   gtk_menu_item_new_with_label("File");
    newitem
    =   gtk_menu_item_new_with_label("New");
    openitem
    =   gtk_menu_item_new_with_label("Open");
    saveitem
    =   gtk_menu_item_new_with_label("Save");
    separator
    =   gtk_separator_menu_item_new ();
    quititem
    =   gtk_menu_item_new_with_label("Quit");
    
    /*  Menu settings   */
    gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                     fileitem);
    gtk_menu_item_set_submenu       (GTK_MENU_ITEM(fileitem),
                                     filemenu);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     newitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     openitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     saveitem);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     separator);
    gtk_menu_shell_append           (GTK_MENU_SHELL(filemenu),
                                     quititem);
    /*  Label           */
    label   =   gtk_label_new       ("Unselected");
    gtk_label_set_justify           (GTK_LABEL(label),
                                     GTK_JUSTIFY_CENTER);
                                      
    /*  Pack to box     */
    gtk_box_pack_start              (GTK_BOX(vbox), menubar, 0, 0, 0);
    gtk_box_pack_end                (GTK_BOX(vbox), label  , 1, 0, 0);
    
    /*  Accel group     */
    accel_group
    =   gtk_accel_group_new         ();
    gtk_window_add_accel_group      (GTK_WINDOW(window), accel_group);
    
    /*  Add accelerator */
    gtk_widget_add_accelerator      (newitem, "activate", accel_group,
                                     GDK_KEY_n, GDK_CONTROL_MASK,
                                     GTK_ACCEL_VISIBLE);
    gtk_widget_add_accelerator      (openitem, "activate", accel_group,
                                     GDK_KEY_o, GDK_CONTROL_MASK,
                                     GTK_ACCEL_VISIBLE);
    gtk_widget_add_accelerator      (saveitem, "activate", accel_group,
                                     GDK_KEY_s, GDK_CONTROL_MASK,
                                     GTK_ACCEL_VISIBLE);
    gtk_widget_add_accelerator      (quititem, "activate", accel_group,
                                     GDK_KEY_q, GDK_CONTROL_MASK,
                                     GTK_ACCEL_VISIBLE);
    
    /*  Signals         */
    g_signal_connect                (newitem,   "activate",
                                     G_CALLBACK(select_item),   label);
    g_signal_connect                (openitem,  "activate",
                                     G_CALLBACK(select_item),   label);
    g_signal_connect                (saveitem,  "activate",
                                     G_CALLBACK(select_item),   label);
    g_signal_connect_swapped        (quititem,  "activate",
                                     G_CALLBACK(gtk_widget_destroy),
                                     window);
    
    gtk_widget_show_all             (window);
}

void select_item(GtkWidget *widget, gpointer label)
{
    const gchar *name = gtk_menu_item_get_label(GTK_MENU_ITEM(widget));
    gtk_label_set_text(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取り外しできない状態
      GTK_ACCEL_MASKどういう状態か分かりません


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

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

    statusbar.c

    
    #include <gtk/gtk.h>
    
    void activate();
    void showmessage();
    
    int main(int argc, char **argv)
    {
        GtkApplication  *app;
        int             status;
        
        app     =   gtk_application_new ("jp.vivacocoa.statusbar",  0   );
        g_signal_connect                (app,       "activate",
                                         G_CALLBACK( activate),     NULL);
        status  =   g_application_run   (G_APPLICATION(app), argc,  argv);
        g_object_unref                  (app);
        return  status;
    }
    
    void activate(GtkApplication *app, gpointer data)
    {
        GtkWidget   *window;
        GtkWidget   *vbox;
        GtkWidget   *menubar;
        GtkWidget   *viewitem;
        GtkWidget   *viewmenu;
        GtkWidget   *showitem;
        GtkWidget   *statusbar;
        guint       statusbar_id;
        
        /*  Window          */
        window  =
        gtk_application_window_new      (app);
        gtk_window_set_title            (GTK_WINDOW(window), "StatusBar");
        gtk_window_set_default_size     (GTK_WINDOW(window),    300, 200);
        gtk_window_set_position         (GTK_WINDOW(window),    1       );
        
        /*  Container       */
        vbox    =   gtk_box_new         (GTK_ORIENTATION_VERTICAL,  0   );
        gtk_container_add               (GTK_CONTAINER(window),     vbox);
        
        /*  Menu            */
        menubar =   gtk_menu_bar_new    ();
        viewmenu
                =   gtk_menu_new        ();
        viewitem
        =   gtk_menu_item_new_with_label("View");
        showitem
        =   gtk_check_menu_item_new_with_label
                                        ("Show statusbar message");
        gtk_check_menu_item_set_active  (GTK_CHECK_MENU_ITEM(showitem),
                                         TRUE);
        gtk_menu_item_set_submenu       (GTK_MENU_ITEM(viewitem),
                                         viewmenu);
        gtk_menu_shell_append           (GTK_MENU_SHELL(menubar),
                                         viewitem);
        gtk_menu_shell_append           (GTK_MENU_SHELL(viewmenu),
                                         showitem);
        
        /*  StatusBar       */
        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.");
        
        /*  Pack to box     */
        gtk_box_pack_start              (GTK_BOX(vbox), menubar,  0, 0, 0);
        gtk_box_pack_end                (GTK_BOX(vbox), statusbar,0, 1, 0);
        
        /*  Signal          */
        g_signal_connect                (showitem,      "activate",
                                         G_CALLBACK(showmessage),
                                         statusbar);
        
        gtk_widget_show_all             (window);
    }
    
    void showmessage(GtkWidget *menuitem, gpointer statusbar)
    {
        guint id = gtk_statusbar_get_context_id(statusbar, "none");
        
        if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))
            gtk_statusbar_push(statusbar, id, "This is a statusbar.");
        else
            gtk_statusbar_push(statusbar, id, "");
    }
    	

    実行結果

    macOSでチェックボタン、ラジオボタン、チェックメニューを使った場合、 実行中に次のような警告が出ますが、実行には問題ないようです。

    
    (a.out:2365): Gtk-WARNING **: 17:36:05.780: Could not load a pixbuf
    from /org/gtk/libgtk/theme/Adwaita/assets/check-symbolic.svg.
    This may indicate that pixbuf loaders or the mime database could not be 
    found.
        

    コード説明

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


    ポップアップメニュー

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

    popupmenu.c

    
    #include <gtk/gtk.h>
    
    void activate();
    void show_popup();
    
    int main(int argc, char **argv)
    {
        GtkApplication  *app;
        int             status;
        
        app     =   gtk_application_new ("jp.vivacocoa.popupmenu",  0   );
        g_signal_connect                (app, "activate",
                                         G_CALLBACK(activate),      NULL);
        status  =   g_application_run   (G_APPLICATION(app), argc,  argv);
        g_object_unref                  (app);
        return  status;
    }
    
    void activate(GtkApplication *app, gpointer data)
    {
        GtkWidget *window;
        GtkWidget *ebox;
        GtkWidget *menu;
        GtkWidget *minimize;
        GtkWidget *quit;
        
        /*  Window          */
        window  =
        gtk_application_window_new      (app);
        gtk_window_set_title            (GTK_WINDOW(window), "PopupMenu");
        gtk_window_set_default_size     (GTK_WINDOW(window),    300, 200);
        gtk_window_set_position         (GTK_WINDOW(window),    1       );
        
        /*  Event box       */
        ebox    =   gtk_event_box_new   ();
        gtk_container_add               (GTK_CONTAINER(window),     ebox);
        
        /*  Menu            */
        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);
        
        /*  Signals         */
        g_signal_connect_swapped        (minimize,  "activate",
                                         G_CALLBACK(gtk_window_iconify),
                                         window);
        g_signal_connect_swapped        (quit,      "activate",
                                         G_CALLBACK(gtk_widget_destroy),
                                         window);
        g_signal_connect_swapped        (ebox,      "button-press-event",
                                         G_CALLBACK(show_popup),    menu);
        
        gtk_widget_show_all             (window);
    }
    
    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_swapped(quit,"activate", G_CALLBACK(gtk_widget_destroy), window);
      gtk_widget_destroy関数は第1引数に破棄(destroy)するウィジェットを取ります。 スワップ(swap、交換)しているので、windowが第1引数として渡されます。
    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引数にイベントを渡します。


    ツールバー

    • CentOS 7、Ubuntu、Mint以外では ツールバーのアイコンは正しく表示されません。

    toolbar.c

    
    #include <gtk/gtk.h>
    #include <stdlib.h>     // for atoi
    #include <string.h>     // for strcmp
    
    GtkToolItem *undo;
    GtkToolItem *redo;
    
    void activate();
    void undo_redo();
    
    int main(int argc, char **argv)
    {
        GtkApplication  *app;
        int             status;
        
        app     =   gtk_application_new ("jp.vivacocoa.toolbar",    0   );
        g_signal_connect                (app,   "activate",
                                         G_CALLBACK(activate),      NULL);
        status  =   g_application_run   (G_APPLICATION(app), argc,  argv);
        g_object_unref                  (app);
        return  status;
    }
    
    void activate(GtkApplication *app, gpointer data)
    {
        GtkWidget   *window;
        GtkWidget   *vbox;
        GtkWidget   *toolbar;
        GtkWidget   *undo_icon;
        GtkWidget   *redo_icon;
        GtkWidget   *quit_icon;
        GtkToolItem *sep;
        GtkToolItem *quit;
        GtkWidget   *label;
        
        /*  Window          */
        window  =
        gtk_application_window_new      (app);
        gtk_window_set_title            (GTK_WINDOW(window), "ToolBar"  );
        gtk_window_set_default_size     (GTK_WINDOW(window),    300, 200);
        gtk_window_set_position         (GTK_WINDOW(window),    1       );
        
        /*  Container       */
        vbox    =   gtk_box_new         (GTK_ORIENTATION_VERTICAL,  0   );
        gtk_container_add               (GTK_CONTAINER(window),     vbox);
        
        /*  ToolBar         */
        toolbar =   gtk_toolbar_new     ();
        gtk_container_set_border_width  (GTK_CONTAINER(toolbar),    0   );
        
        undo_icon
        =   gtk_image_new_from_icon_name("gtk-undo",
                                          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           */
        label   =   gtk_label_new       ("4");
        
        /*  Pack to box     */
        gtk_box_pack_start              (GTK_BOX(vbox), toolbar, 0, 0, 0);
        gtk_box_pack_end                (GTK_BOX(vbox), label  , 1, 0, 0);
        
        /*  Signals         */
        g_signal_connect                (undo,      "clicked",
                                         G_CALLBACK(undo_redo), label   );
        g_signal_connect                (redo,      "clicked",
                                         G_CALLBACK(undo_redo),  label   );
        g_signal_connect_swapped        (quit,      "clicked",
                                         G_CALLBACK(gtk_widget_destroy),
                                         window);
        
        gtk_widget_show_all             (window);
    }
    
    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関数で 文字列に割り当てられたメモリーを解放しておいた方が良いです。



40949 visits
Posted: Jan. 07, 2020
Update: Jan. 07, 2020

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