第2章 GTK+ で画像ビューワを作ってみよう
第2章 GTK+ で画像ビューワを作ってみよう 24 第2章 GTK+で画像ビューワを作ってみよう
2.7.2 メニュー構成の作成
メニュー構成は,XML形式で指定します.XMLを外部のテキストファイルに記述するか,その内容をソースコード中に文 字列として保持するか,いずれかの方法で作成します.今回はメニュー構成を文字列として作成し,ソースコード中に埋め込む ことにします.今回作成するメニューの構成は,次のような文字列で表されます.
"<interface>"
" <menu id=’appmenu’>"
" <submenu>"
" <attribute name=’label’>File</attribute>"
" <section>"
" <item>"
" <attribute name=’label’>Open</attribute>"
" <attribute name=’action’>app.open</attribute>"
" <attribute name=’accel’><Primary>o</attribute>"
" </item>"
" </section>"
" <section>"
" <item>"
" <attribute name=’label’>Quit</attribute>"
" <attribute name=’action’>app.quit</attribute>"
" <attribute name=’accel’><Primary>q</attribute>"
" </item>"
" </section>"
" </submenu>"
" </menu>"
"</interface>";
メニューの構成は,<interface>タグから始まり, <menu></menu>タグ内に記述します. <menu>タグ内の IDは
GtkBuilerを用いて作成したメニューバーの情報を取り出す際の識別子として用いられます. ここでは,メニューバーの中に
Fileメニューがあり,そのメニューアイテムとしてOpenとQuitが存在する構成となっています.この場合, Fileメニューは
<submenu>タブで記述し,<attribute name=’label’>タブでそのラベルを指定します. また, OpenとQuitのメニューアイ
テムは<item>タブで記述します. メニューアイテムの属性は<attribute>タブで記述し,属性の種類をname=’ ’という形で
指定します. メニューアイテムの代表的な属性を以下に挙げます.
• label ... メニューアイテムのラベル
• action ... GActionEntry構造体の要素と関連付けるための文字列
• accel ... メニューアイテムに対するアクセラレータキー
action属性の文字列は, GtkApplication(またはGtkWidget)に関連付けされたGActionMapの識別子と GActio-nEntry構造体の第1メンバであるname変数の文字列を用いて, ”GActionMap識別子.GActionEntryのname文字列”の形式 で記述する必要があります.
また, OpenとQuitの間に仕切り(セパレータ)を配置する場合には,仕切りの前後のメニューアイテムを<section>タブで
挟むようにします.
2.7.3 メニューアイテムに対するコールバック関数の記述
メニューアイテムが選択されたときに呼び出されるコールバック関数の記述は,GActionEntry構造体で行い,以下の5項 目を設定します.
1. メニューアイテムと関連付けするための文字列
2. メニューアイテムが選択されたときに呼び出されるコールバック関数
第2章 GTK+ で画像ビューワを作ってみよう
2.7 メニューバーの追加 25
3. パラメータタイプ 4. 状態
5. メニューアイテムの状態が変わったときに呼び出されるコールバック関数
前項で定義したメニューおよびメニューアイテムの場合は,次のように詳細を設定します.
static GActionEntry entries[] = { {"open", cb_open, NULL, NULL, NULL}, {"quit", cb_quit, NULL, NULL, NULL}, };
特別な処理をしない限りは第3から第5メンバは設定しなくても問題ありませんので,今回はすべてNULLとしています.
2.7.4 コールバック関数の登録
次にGActionEntry構造体で記述したコールバック関数をアプリケーションと関連付けしたGActionMapに登録します.
登録を行うには関数g action map add action entries を使用します. 今回のようにGtkApplicationを使っている場合は, GtkApplication自身をGActioMapとして使用することができます(ソース2–6132–134行目).
v o i d g _ a c t i o n _ m a p _ a d d _ a c t i o n _ e n t r i e s ( G A c t i o n M a p * a c t i o n _ m a p , c o n s t G A c t i o n E n t r y * e n t r i e s ,
g i n t n _ e n t r i e s ,
g p o i n t e r u s e r _ d a t a ) ;
第1引数 GActionMap型の変数へのポインタ
第2引数 GActionEntryで定義されたコールバック関数情報
第3引数 GActionEntryのエントリ数
第4引数 コールバック関数に渡すデータ
2.7.5 メニューバーウィジェットの作成
最後に,GtkBuilerを用いてメニュー定義情報からメニューバーウィジェットを作成して,アプリケーションに配置します.
メニュー定義情報がファイルの場合は,関数gtk builder add from fileを使用します. また,メニュー定義情報が文字列の場 合は,関数gtk builder add from stringを使用します. 以下は, GtkBuilderに文字列で定義したメニュー情報を追加する例に なります.
GtkBuilder *builder = gtk_builder_new ();
gtk_builder_add_from_string (builder, menu_info, -1, NULL);
関数gtk builder add from stringは,文字列からメニュー定義情報を登録する関数です.第3引数にはメニュー構成を示す
文字列の長さを指定しますが,文字列全体を使用する場合には−1を指定します.
g u i n t g t k _ b u i l d e r _ a d d _ f r o m _ s t r i n g (G t k B u i l d e r * b u i l d e r , c o n s t g c h a r * b u f f e r , g s i z e l e n g t h , G E r r o r * * e r r o r ) ;
第1引数 GtkBuilder型の変数 第2引数 メニュー構成文字列 第3引数 メニュー構成文字列の長さ
第4引数 エラー情報を格納する変数へのポインタ 戻り値 成功した場合には正の数,エラーの場合には0
第2章 GTK+ で画像ビューワを作ってみよう 26 第2章 GTK+で画像ビューワを作ってみよう
2.7.6 メニューバーウィジェットの取得
すべてのメニュー情報をGtkBuilderに登録したら,関数gtk builder get objectを使用して,メニューバーウィジェットを 取得します.
G O b j e c t * g t k _ b u i l d e r _ g e t _ o b j e c t (G t k B u i l d e r * b u i l d e r , c o n s t g c h a r * n a m e ) ;
第1引数 GtkBuilder型の変数 第2引数 識別文字列
戻り値 取得したオブジェクト
最後に,以下に示すように関数gtk builder get objectで取得したオブジェクトをGMenuModel型のポインタに変換して, 関数gtk application set menubarを使ってアプリケーションに配置します.
GMenuModel *menubar;
menubar = (GMenuModel *) gtk_builder_get_object (builder, "appmenu");
gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
2.7.7 コンパイルと動作確認
ソースコードを入力し,コンパイルしたら,いつものように実行してみましょう.
$ ./image-viewer ~/images/Parrots.png
図2.11のようにメニューバーが表示され,メニューやショートカットキーを利用してプログラムを終了できるようになりまし た.プログラムを終了する際にはコールバック関数(ソース2–618–26行目)が呼び出されますが,関数の第3引数のuser data
にはGtkApplication型の変数が与えられています. これは,ソース2–6122–124行目でコールバック関数をGActionMapに
登録する際に,関数の第3引数にGtkApplication型の変数appを指定しているためです.
図2.11 メニューバーを追加した画像ビューワ
ソース2–6 メニューバーの追加: image-viewer.c
1 # i n c l u d e <g t k / g t k . h >
2 # i n c l u d e <s t d l i b . h >
3 4 / *
5 O p e nメ ニ ュ ー が 選 択 さ れ た と き に 呼 び 出 さ れ る 関 数
第2章 GTK+ で画像ビューワを作ってみよう
2.7 メニューバーの追加 27
6 * /
7 s t a t i c v o i d
8 c b _ o p e n ( G S i m p l e A c t i o n * a c t i o n ,
9 G V a r i a n t * p a r a m t e r ,
10 g p o i n t e r u s e r _ d a t a )
11 {
12 g _ p r i n t ( " T h i s ␣ f u n c t i o n ␣ i s ␣ n o t ␣ i m p l e m e n t e d ␣ y e t . \ n " ) ;
13 }
14 15 / *
16 ボ タ ン が ク リ ッ ク さ れ た と き に 呼 び 出 さ れ る 関 数 17 * /
18 s t a t i c v o i d
19 c b _ q u i t ( G S i m p l e A c t i o n * a c t i o n ,
20 G V a r i a n t * p a r a m e t e r ,
21 g p o i n t e r u s e r _ d a t a )
22 {
23 / * メ イ ン ル ー プ を 終 了 * /
24 G A p p l i c a t i o n * a p p = G _ A P P L I C A T I O N ( u s e r _ d a t a ) ;
25 g _ a p p l i c a t i o n _ q u i t ( a p p ) ;
26 }
27 28 / *
29 * メ ニ ュ ー の 構 造 30 * /
31 s t a t i c c o n s t g c h a r m e n u _ i n f o [ ] =
32 " <i n t e r f a c e > "
33 " ␣ ␣ <m e n u ␣ i d = ’a p p m e n u ’ >"
34 " ␣ ␣ ␣ ␣ <s u b m e n u > "
35 " ␣ ␣ ␣ ␣ ␣ ␣ <a t t r i b u t e ␣ n a m e = ’l a b e l ’ >F i l e < / a t t r i b u t e > "
36 " ␣ ␣ ␣ ␣ ␣ ␣ <s e c t i o n > "
37 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <i t e m > "
38 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <a t t r i b u t e ␣ n a m e = ’l a b e l ’ >O p e n < / a t t r i b u t e > "
39 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <a t t r i b u t e ␣ n a m e = ’a c t i o n ’ >a p p . o p e n < / a t t r i b u t e > "
40 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <a t t r i b u t e ␣ n a m e = ’a c c e l ’ >&l t ; P r i m a r y & g t ; o < / a t t r i b u t e > "
41 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ </i t e m > "
42 " ␣ ␣ ␣ ␣ ␣ ␣ </s e c t i o n > "
43 " ␣ ␣ ␣ ␣ ␣ ␣ <s e c t i o n > "
44 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <i t e m > "
45 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <a t t r i b u t e ␣ n a m e = ’l a b e l ’ >Q u i t < / a t t r i b u t e > "
46 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <a t t r i b u t e ␣ n a m e = ’a c t i o n ’ >a p p . q u i t < / a t t r i b u t e > "
47 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ <a t t r i b u t e ␣ n a m e = ’a c c e l ’ >&l t ; P r i m a r y & g t ; q < / a t t r i b u t e > "
48 " ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ </i t e m > "
49 " ␣ ␣ ␣ ␣ ␣ ␣ </s e c t i o n > "
50 " ␣ ␣ ␣ ␣ </s u b m e n u > "
51 " ␣ ␣ </m e n u > "
52 " </i n t e r f a c e > " ;
53 54 / *
55 * メ ニ ュ ー ア イ テ ム の 詳 細 56 * /
57 s t a t i c G A c t i o n E n t r y e n t r i e s [ ] = {
58 { " o p e n " , c b _ o p e n , N U L L , N U L L , N U L L } ,
59 { " q u i t " , c b _ q u i t , N U L L , N U L L , N U L L } ,
60 } ;
61 62 / *
63 * ア プ リ ケ ー シ ョ ン が 有 効 に な っ た と き に 呼 び 出 さ れ る 関 数 64 * /
65 s t a t i c v o i d c b _ a c t i v a t e ( G A p p l i c a t i o n * app ,
66 g p o i n t e r u s e r _ d a t a )
67 {
68 G t k W i d g e t * w i n d o w ;
69
70 / * ウ ィ ン ド ウ の 作 成 * /
71 w i n d o w = g t k _ a p p l i c a t i o n _ w i n d o w _ n e w ( G T K _ A P P L I C A T I O N ( a p p ) ) ;
72 / * ウ ィ ン ド ウ の 大 き さ の 設 定 * /
73 g t k _ w i d g e t _ s e t _ s i z e _ r e q u e s t ( w i n d o w , 3 0 0 , 2 0 0 ) ;
74 / * メ ニ ュ ー の 作 成 * /
75 G t k B u i l d e r * b u i l d e r ;
76 b u i l d e r = g t k _ b u i l d e r _ n e w ( ) ;
77 g t k _ b u i l d e r _ a d d _ f r o m _ s t r i n g ( b u i l d e r , m e n u _ i n f o , -1 , N U L L ) ;
78 G M e n u M o d e l * m e n u b a r ;
79 m e n u b a r = ( G M e n u M o d e l * ) g t k _ b u i l d e r _ g e t _ o b j e c t ( b u i l d e r , " a p p m e n u " ) ;
80 g t k _ a p p l i c a t i o n _ s e t _ m e n u b a r ( G T K _ A P P L I C A T I O N ( a p p ) , m e n u b a r ) ;
81 {
第2章 GTK+ で画像ビューワを作ってみよう 28 第2章 GTK+で画像ビューワを作ってみよう
82 / * ス ク ロ ー ル バ ー 付 き ウ ィ ン ド ウ の 作 成 * /
83 G t k W i d g e t * s c r o l l _ w i n d o w ;
84 s c r o l l _ w i n d o w = g t k _ s c r o l l e d _ w i n d o w _ n e w ( N U L L , N U L L ) ;
85 / * ス ク ロ ー ル バ ー 付 き ウ ィ ン ド ウ を 配 置 * /
86 g t k _ c o n t a i n e r _ a d d ( G T K _ C O N T A I N E R ( w i n d o w ) , s c r o l l _ w i n d o w ) ;
87 / * ス ク ロ ー ル バ ー の 表 示 設 定 * /
88 g t k _ s c r o l l e d _ w i n d o w _ s e t _ p o l i c y ( G T K _ S C R O L L E D _ W I N D O W ( s c r o l l _ w i n d o w ) ,
89 G T K _ P O L I C Y _ A U T O M A T I C ,
90 G T K _ P O L I C Y _ A U T O M A T I C ) ;
91 {
92 G t k W i d g e t * i m a g e ;
93
94 / * フ ァ イ ル か ら 画 像 を 読 み 込 ん で イ メ ー ジ の 作 成 * /
95 i m a g e = g t k _ i m a g e _ n e w _ f r o m _ f i l e ( (c h a r * ) u s e r _ d a t a ) ;
96 / * イ メ ー ジ を ス ク ロ ー ル バ ー 付 き ウ ィ ン ド ウ に 配 置 * /
97 g t k _ c o n t a i n e r _ a d d ( G T K _ C O N T A I N E R ( s c r o l l _ w i n d o w ) , i m a g e ) ;
98 }
99 }
100 / * ウ ィ ン ド ウ の 表 示 * /
101 g t k _ w i d g e t _ s h o w _ a l l ( w i n d o w ) ;
102 }
103 104 / *
105 メ イ ン 関 数 106 * /
107 i n t
108 m a i n (i n t ar g c , c h a r * * a r g v )
109 {
110 G t k A p p l i c a t i o n * a p p ;
111 G t k W i d g e t * w i n d o w ;
112
113 / * 引 数 の チ ェ ッ ク * /
114 i f ( a r g c ! = 2 )
115 {
116 g _ p r i n t ( " U s a g e : ␣ % s ␣ i m a g e - f i l e \ n " , a r g v [ 0 ] ) ;
117 e x i t ( 1 ) ;
118 }
119 / * ア プ リ ケ ー シ ョ ン の 作 成 * /
120 a p p = g t k _ a p p l i c a t i o n _ n e w ( " t e s t . g t k . i m a g e v i e w e r " , 0 ) ;
121 / * ア ク シ ョ ン の 登 録 * /
122 g _ a c t i o n _ m a p _ a d d _ a c t i o n _ e n t r i e s ( G _ A C T I O N _ M A P ( a p p ) ,
123 e n t r i e s , G _ N _ E L E M E N T S ( e n t r i e s ) ,
124 a p p ) ;
125 / * シ グ ナ ル ハ ン ド ラ の 登 録 * /
126 g _ s i g n a l _ c o n n e c t ( app , " a c t i v a t e " , G _ C A L L B A C K ( c b _ a c t i v a t e ) , a r g v [ 1 ] ) ;
127 / * メ イ ン ル ー プ * /
128 g _ a p p l i c a t i o n _ r u n ( G _ A P P L I C A T I O N ( a p p ) , 0 , N U L L ) ;
129
130 r e t u r n 0 ;
131 }
2.7.8 まとめ
GtkBuilderを利用することで簡単にメニューが作成できるとはいえ,メニューを作成するまでにさまざまな手順が必要でし
た.メニュー作成の手順をもう一度簡単にまとめておきます.
1. メニュー構成の記述
今回は文字列で定義しましたが,テキストファイルに定義する方法もあります.
2. コールバック関数の記述
3. コールバック関数をアプリケーションに登録 4. メニューバーの取得とアプリケーションへの配置