Gtk+/GNOME開発メモ

Gtk+/GNOMEに関するいろいろ。

文書

Glade 3 で GUI 開発

Windowsネイティブなgtk2アプリケーションの開発

gtk+クラス図 (gtk 1.2 to gtk3) gtk+ 1.2 のクラスが残っているかどうか.

覚え書き

1999.07.26 epingle

1999.02.14 gtk+のC++ラッパー

Gtk+ tips

gtk-list MLにおける私の発言から。

  • GtkButtonのラベルを取得するには?
  • GtkEntryで,日本語入力を禁止するには?
  • GtkOptionMenuで,ユーザーがどの項目を選択しているか知るのはどうするの?
  • GtkOptionMenuでユーザーが選択したことを知るのはどうするの?
  • どのハンドラで描画すればいい?
  • ウィンドウのデザインを手早くするツールってある?
  • X特有の機能を使いたいときは?
  • シグナルによる標準の動作を止めさせたい。
  • メニューが表示される直前で発生するシグナルは?
  • [Shift] + [Space]などで日本語モードにすると,kinput2のウィンドウがちらつく
  • ウィンドウを閉じる直前に発生するイベント(シグナル)は?

Q: GtkButtonのラベルを取得するには?

A: GtkButtonはGtkBinのサブクラスなので,GTK_BIN(button)->child

(1999.08.24)


Q: GtkEntryで,日本語入力を禁止するには?

A: "focus_in_event"シグナルをafterで接続して,ハンドラでgdk_im_end()すればよい。

gint on_focus_in_event(GtkWidget*widget, GdkEventFocus* event, void* )
{
    gdk_im_end();
    return FALSE;
}

    gtk_signal_connect_after(GTK_OBJECT(entry), "focus_in_event",
                        GTK_SIGNAL_FUNC(on_focus_in_event), NULL);

 日本語テキストが貼り付けられる場合があるので,エントリに日本語が入らないようにする場合は,さらに工夫が必要。

(1999.08.20)


Q: GtkOptionMenuで,ユーザーがどの項目を選択しているか知るのはどうするの?

A: 次のようにして求める。

int getSelectedIndex(GtkOptionMenu* option_menu)
{
    GtkWidget* menu = gtk_option_menu_get_menu(option_menu);
    return g_list_index(GTK_MENU_SHELL(menu)->children,
                    option_menu->menu_item);
}
(1999.07.26)
Q: GtkOptionMenuでユーザーが選択したことを知るのはどうするの?

A: 次のようにする。GtkOptionMenuではなく,構成しているメニューの方に接続するのがミソ。

void on_selection_done(GtkMenuShell* menu_shell, void* )
{
    ...
}

    GtkWidget* menu = gtk_option_menu_get_menu(
                GTK_OPTION_MENU(option_menu));
    gtk_signal_connect(GTK_OBJECT(menu), "selection-done",
                GTK_SIGNAL_FUNC(on_selection_done), NULL);
(1999.07.26)
Q: どのハンドラで描画すればいい?

A: "expose_event"のハンドラで描画するのが基本。再描画の必要が生じた場合も直接このハンドラを呼ぶのではなく,gtk_widget_queue_draw_area()で領域が無効になったことを示し,gtk+にコールバックさせる。

(1999.07.07)


Q: ウィンドウのデザインを手早くするツールってある?

A: gladeがお勧め。

(1999.06.26)


Q: X特有の機能を使いたいときは?

A: gdkx.hを参照してください。gtk+がクロスプラットフォームを志向しているといっても,全く不完全なので,無理にXの機能を避けるより,積極的に使って問題ない。

#include <gdk/gdkx.h>
(1999.04.27)
Q: シグナルによる標準の動作を止めさせたい。

A: シグナルのハンドラでTRUEを返しても標準の動作が行われる場合や,戻り値がない場合は,gtk_signal_emit_stop_by_name()関数を使います。

void onDaySelected(GtkCalendar* calen, void* data)
    // GtkCalendar::day_selected
{
    if (mouse_button == MENU_BUTTON) {
        gtk_signal_emit_stop_by_name(GTK_OBJECT(calen), "day_selected");
        return;
    }
    ...
}
(1999.03.21)
Q: メニューが表示される直前で発生するシグナルは?

A: "map"シグナルを調べてみましょう。

    GtkWidget* menu = gtk_item_factory_get_widget(
                        frame->menuFactory, "/ウィンドウ(W)");
    gtk_signal_connect(GTK_OBJECT(menu), "map",
                        GTK_SIGNAL_FUNC(onMap), 2);
 GtkItemFactoryを使わない場合は,GtkMenu派生オブジェクトに"map"シグナルを接続します。次の場合だと,o1ではなく,o2の方です。
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(o1), o2);
(1999.02.08)
Q: [Shift] + [Space]などで日本語モードにすると,kinput2のウィンドウがちらつく

A: ウィンドウ・マネージャがWindowMakerなら,~/GNUstep/Defaults/WMWindowAttributesファイルに次の内容を加える。twmなら回避できない。

 IMサーバーにQ's Nicolatterを使う,という選択でも良い。

Kinput2 = {
    Unfocusable = Yes;
};
(1999.01.09)
Q: ウィンドウを閉じる直前に発生するイベント(シグナル)は?

A: "delete_event"イベント。このハンドラでFALSEを返すと標準の動作となり,ウィンドウが"destroy"される。

(1999.01.04)


ウィンドウ形式

GTK_WINDOW_TOPLEVEL
GTK_WINDOW_TOPLEVELを指定したときのウィンドウ  タイトルバーとコントロールメニューボックスが付く。
GTK_WINDOW_DIALOG
GTK_WINDOW_DIALOGを指定したときのウィンドウ  GTK_WINDOW_TOPLEVELと同様にタイトルバーがあるが,コントロールメニューボックスがない。
GTK_WINDOW_POPUP
ウィンドウにタイトルバーが付かない

ラベルwidgetの位置決め

#include <gtk/gtk.h>

int main(int argc, char* argv[])
{
    gtk_set_locale();
    gtk_init(&argc, &argv);

    GtkWidget* top = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    GtkMisc* label = GTK_MISC(gtk_label_new("MMM TEST MMM"));
    gtk_widget_show(GTK_WIDGET(label));
    gtk_container_add(GTK_CONTAINER(top), GTK_WIDGET(label));

    gtk_widget_show(top);
    gtk_main();
    return 0;
}

 これで,ウィンドウ一杯にラベルが表示される(ラベルの大きさにウィンドウが調節される)。ウィンドウの大きさを変えると,ラベルは中央に表示される。

gtk_misc_set_alignment(label, 0.0, 0.5);  // 左寄せ

すると,ウィンドウの大きさを変えたときに,左端,上下方向は中央に表示されるようになる。 詰め物を入れたラベル

gtk_misc_set_padding(label, 30, 10);
を追加すると,ラベルの外側に詰め物が入る。ただしラベルwidgetの大きさは詰め物を含むから,gtk_widget_set_usize()のときに詰め物の幅を考慮する必要がある。この例ではウィンドウを広げるとラベルは左寄りになる。詰め物の幅は親widgetの大きさに左右されず,詰め物とラベル文字列の間の領域が変化する。

メニュー

GtkItemFactory --->  GtkMenu
            ^gtk_item_factory_get_widget()

 ブランチを利用不可にしたいときは, gtk_item_factory_get_widget() + gtk_menu_get_attach_widget() でMenuItemを取得して,それを操作。


メニュー項目

 GtkMenuにGtkMenuItemを

  • 追加するとき,gtk_menu_append()
  • 削除するとき,gtk_container_remove()

 GtkContainer::remove()ってpure virtualになってる。

┌────────────┐
│GtkContainer {abstract} │
└────────────┘
      △
      │
┌─────┴──────┐     children  *┌───┐
│GtkMenuShell {abstract} │◆───────┤void* │MenuItemが入っている
└────────────┘             └───┘
    △
    ├────┐
┌──┴──┐┌┴───┐
│GtkMenuBar││GtkMenu │GtkMenuはポップアップして,ウィンドウになれる。
└─────┘└────┘

MenuItemのラベルを変更するとき

 GtkMenuItemはGtkBinから派生していて,ラベルはGtkBin::childに入っている。GtkLabelにキャストして,ラベル文字列を変更すればよい。

┌─────────┐    child ┌─────┐
│GtkBin {abstract} │◆────┤GtkWidget │MenuItemのときラベルが入ってる
└─────────┘          └─────┘
     △
     │
┌────┴────┐
│GtkItem {abstract}│
└─────────┘
    △
    │
┌──┴───┐
│GtkMenuItem │
└──────┘

widgetをコンテナに並べる

  • リサイズ不可 or 自動 ---> box
  • 手動でリサイズ ---> paned
┌────────────┐
│GtkContainer {abstract} │
└────────────┘
        △
        ├──────────┐
┌────┴────┐┌────┴─────┐   child1 ┌─────┐
│GtkBox {abstract} ││GtkPaned {abstract} │◆────┤GtkWidget │
└─────────┘└──────────┘          └─────┘
  ◆                               ◆ child2 ┌─────┐
  │* children                     └────┤GtkWidget │
┌─┴─┐                                     └─────┘
│void* │GtkBoxChild
└───┘

gtk+のbox model

    gtk_container_border_width(aContainer, ...);
は,親widgetとaContainerとの間に空白を作る。aContainerの内側に空白を作るときは,
    GtkWidget* textBox = gtk_hbox_new(FALSE, 0);
    gtk_widget_show(textBox);
    gtk_container_add(aContainer, textBox);
    gtk_container_set_border_width(GTK_CONTAINER(textBox), 5);
みたいに,単に空白のためだけのboxを用意してやる必要がある。(中身がコンテナの時は,そのコンテナのgtk_container_border_width()でよい。)

 gtk_container_set_border_width()はコンテナwidgetの外側に詰め物を入れるが,widgetの大きさには詰め物が含まれる。この辺はgtk_misc_set_padding()でも同様。

GtkBoxでの詰め物  gtk_box_pack_start()の場合は,box widgetの内側,子widgetの外側に詰め物を入れる。この場合は,詰め物が子widgetの大きさに含まれない点でgtk_misc_set_padding()等と異なる。ただ,外側といっても囲むわけではなく,また,一つ一つの子widgetの外側に詰め物を入ていくと,(指定する幅が同じなら)widgetが並ぶところの詰め物の幅は,一番外側の幅の2倍になる。

GtkBoxで子widget間の距離を等しくする  GtkBoxで子widget間の詰め物と外側の詰め物の幅を同じにしようと思うと,gtk_container_set_border_width()とgtk_hbox_new() / gtk_vbox_new()を組み合わせるのがよい。

GtkWidget* box = gtk_hbox_new(FALSE, 20);               // (2) 子widget間の詰め物
gtk_container_set_border_width(GTK_CONTAINER(box), 20); // (1) 外側の詰め物
 gtk_box_pack_start()では詰め物に'0'を指定することに注意しよう。