第 3 章 もっと GTK+ 33
3.3 シグナルとコールバック関数の詳細
3.3.2 コールバック関数の設定
コールバック関数を設定する中心的な関数はg signal connect dataです.この関数のプロトタイプ宣言は次のようになって います.
g u l o n g g _ s i g n a l _ c o n n e c t _ d a t a (g p o i n t e r i n s t a n c e ,
c o n s t g c h a r * d e t a i l e d _ s i g n a l , G C a l l b a c k c _ h a n d l e r ,
g p o i n t e r d a t a ,
G C l o s u r e N o t i f y d e s t r o y _ d a t a , G C o n n e c t F l a g s c o n n e c t _ f l a g s ) ;
この関数の戻り値は,設定したコールバック関数を特定するIDとして使用され,一度設定したコールバック関数を解除する 際などに使用されます.
第6引数のフラグGConnectFlagsは,表3.4のように定義されています.このフラグの値によってコールバック関数の振る 舞いが変わってきます.これについては後で説明します.
*1http://library.gnome.org/devel/gtk-tutorial/stable/a2700.html
第1引数 コールバック関数を関連付けるオブジェクト 第2引数 シグナル名
第3引数 コールバック関数
第4引数 コールバック関数に渡すデータ
第5引数 コールバック関数が呼び出されなくなったときに呼び出される関数 第6引数 コールバック関数のフラグ
戻り値 コールバック関数を特定するID
コールバック関数が呼び出されなくなったときに呼び出される関数
関数g signal connect dataの第5引数に与える関数は,次のように定義されています.
v o i d ( *G C l o s u r e N o t i f y) (g p o i n t e r d a t a , G C l o s u r e * c l o s u r e ) ;
この関数の第1引数に,関数g signal connect dataの第4引数で指定したデータが渡されます.第2引数に関する解説はこ こでは省略します.
コールバック関数のプログラム例
関数g signal connect dataを使ったプログラムの例を,ソース3–3に示します.コンパイルして実行すると,図3.7のウィ
ンドウが表示されます.
“Click me!”ボタンをクリックすると,設定したコールバック関数が呼び出され,関数g signal connect dataの第4引数に
指定した文字列“Hello World”がターミナルに表示されます.
この文字列は,関数g strdupで領域確保しているので,プログラムの終了時にこの領域を解放する必要があります.関数
g signal connect dataの第5引数に指定する関数は,設定したコールバック関数が使用されなくなったときに呼び出される関
数なので,この関数内でデータの領域を解放します.
この関数を呼び出すために,図3.7のウィンドウの閉じるボタンをクリックして,プログラムを終了してみてください.以 下に示すように“Destroy the callback function data.” と表示された後,関数に渡されたデータ(この場合は文字列“Hello
World”)が表示され,プログラムが終了します.
$ ./signal-sample1 Hello World
Destroy the callback function data.
The value of the destroyed data = ’Hello World’
ソース3–3 コールバック関数の例1 : signal-sample1.c
1 # i n c l u d e <g t k / g t k . h >
2
3 s t a t i c v o i d
4 c b _ b u t t o n (G t k W i d g e t * w i d g e t , g p o i n t e r u s e r _ d a t a )
5 {
6 g _ p r i n t ( " % s \ n " , (g c h a r * ) u s e r _ d a t a ) ;
表3.4 コールバック関数の接続設定
値 説明
G CONNECT AFTER ユーザが設定するコールバック関数を,標準で設定されているコール
バック関数が呼び出された後で実行する.
G CONNECT SWAPPED 別のウィジェットのコールバック関数として実行する.
図3.7 コールバック関数の例1
7 }
8
9 s t a t i c v o i d
10 d e s t r o y _ d a t a (g p o i n t e r u s e r _ d a t a , G C l o s u r e * c l o s u r e )
11 {
12 g _ p r i n t ( " D e s t r o y t h e c a l l b a c k f u n c t i o n d a t a . \ n " ) ;
13 g _ p r i n t ( " T h e v a l u e o f t h e d e s t r o y e d d a t a = ’%s ’\n " ,
14 (g c h a r * ) u s e r _ d a t a ) ;
15 g _ f r e e ( (g c h a r * ) u s e r _ d a t a ) ;
16 }
17 18 i n t
19 m a i n (i n t ar g c , c h a r * * a r g v )
20 {
21 G t k W i d g e t * w i n d o w ;
22 G t k W i d g e t * b u t t o n ;
23 g u l o n g h a n d l e ;
24
25 g t k _ i n i t ( & a r g c , & a r g v ) ;
26
27 w i n d o w = g t k _ w i n d o w _ n e w ( G T K _ W I N D O W _ T O P L E V E L ) ;
28 g t k _ w i n d o w _ s e t _ t i t l e ( G T K _ W I N D O W ( w i n d o w ) , " S i g n a l S a m p l e 1 " ) ;
29 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 , 2 5 0 , 5 0 ) ;
30 g _ s i g n a l _ c o n n e c t ( G _ O B J E C T ( w i n d o w ) , " d e s t r o y " ,
31 G _ C A L L B A C K ( g t k _ m a i n _ q u i t ) , N U L L ) ;
32
33 b u t t o n = g t k _ b u t t o n _ n e w _ w i t h _ l a b e l ( " C l i c k m e ! " ) ;
34 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 ) , b u t t o n ) ;
35
36 g _ s i g n a l _ c o n n e c t _ d a t a ( G _ O B J E C T ( b u t t o n ) , " c l i c k e d " ,
37 G _ C A L L B A C K ( c b _ b u t t o n ) ,
38 (g p o i n t e r * ) g _ s t r d u p ( " H e l l o W o r l d " ) ,
39 d e s t r o y _ d a t a , 0 ) ;
40 g t k _ w i d g e t _ s h o w _ a l l ( w i n d o w ) ;
41
42 g t k _ m a i n ( ) ;
43
44 r e t u r n 0 ;
45 }
g signal connect dataの代わりになる関数
関数g signal connect dataの使い方は理解してもらえたでしょうか.コールバック関数のデータをプログラムの終了と同時
に解放したいような場合には,この関数は大変便利です.
しかし,実際の場面ではGClosureNotifyを必要としないことが多いため,以下に示す3つの関数が用意されています.
• g signal connect
一番よく使用されるのがこの関数です.設定したコールバック関数は,ウィジェットに標準で設定されているコールバッ ク関数が呼び出される前に実行されます.
g u l o n g g _ s i g n a l _ c o n n e c t (g p o i n t e r i n s t a n c e ,
c o n s t g c h a r * d e t a i l e d _ s i g n a l , G C a l l b a c k c _ h a n d l e r ,
g p o i n t e r d a t a ) ;
• g signal connect after
ウィジェットにコールバック関数を設定するという意味では上の関数と同様です.上の関数との違いは,ウィジェットに 標準で設定されているコールバック関数が呼び出された後で実行されることです.
g u l o n g g _ s i g n a l _ c o n n e c t _ a f t e r (g p o i n t e r i n s t a n c e ,
c o n s t g c h a r * d e t a i l e d _ s i g n a l , G C a l l b a c k c _ h a n d l e r ,
g p o i n t e r d a t a ) ;
• g signal connect swapped
この関数は関連付けられたシグナルが発生したときにc handerに指定したコールバック関数を呼び出しますが,コール バック関数の第1引数にはこの関数を関連付けたウィジェットではなく,dataに指定したウィジェットが渡されます.
g u l o n g g _ s i g n a l _ c o n n e c t _ s w a p p e d (g p o i n t e r i n s t a n c e ,
c o n s t g c h a r * d e t a i l e d _ s i g n a l ,
G C a l l b a c k c _ h a n d l e r , g p o i n t e r d a t a ) ;
g signal connectとg signal connect afterのプログラム例
これらの関数について具体例を見てみましょう.まずは関数g signal connectと関数g signal connect afterの動作を見てみ ます.ソースコードはソース3–4になります.
先ほどの例と同じウィンドウにボタンが配置されただけのアプリケーションを作成して,ボタンに対して4つのコールバッ ク関数を関連付けます(44–51行目).このように1つのウィジェットに対して複数のコールバック関数を設定することが可能 です.
設定したコールバック関数が,どのような順番で実行されるかを確認します.プログラムを実行してボタンをクリックする と,ターミナルに以下のようなメッセージが表示されます.
$ ./signal-sample2 function 1.
function 2.
function 3.
function 4.
この結果から次のことがわかります.
• 設定する順番にかかわらず,関数g signal connectで関連付けたコールバック関数は先に,関数g signal connect after で関連付けたコールバック関数は最後に呼び出される.
• 同じ関数で関連付けられたコールバック関数は,関連付けられた順番に呼び出される.
ソース3–4 コールバック関数の動作: signal-sample2.c
1 # i n c l u d e <g t k / g t k . h >
2
3 s t a t i c v o i d
4 c b _ b u t t o n 1 (G t k W i d g e t * w i d g e t , g p o i n t e r u s e r _ d a t a )
5 {
6 g _ p r i n t ( " f u n c t i o n 1 . \ n " ) ;
7 }
8
9 s t a t i c v o i d
10 c b _ b u t t o n 2 (G t k W i d g e t * w i d g e t , g p o i n t e r u s e r _ d a t a )
11 {
12 g _ p r i n t ( " f u n c t i o n 2 . \ n " ) ;
13 }
14
15 s t a t i c v o i d
16 c b _ b u t t o n 3 (G t k W i d g e t * w i d g e t , g p o i n t e r u s e r _ d a t a )
17 {
18 g _ p r i n t ( " f u n c t i o n 3 . \ n " ) ;
19 }
20
21 s t a t i c v o i d
22 c b _ b u t t o n 4 (G t k W i d g e t * w i d g e t , g p o i n t e r u s e r _ d a t a )
23 {
24 g _ p r i n t ( " f u n c t i o n 4 . \ n " ) ;
25 }
26 27 i n t
28 m a i n (i n t a r g c , c h a r * * a r g v )
29 {
30 G t k W i d g e t * w i n d o w ;
31 G t k W i d g e t * b u t t o n ;
32
33 g t k _ i n i t ( & a r g c , & a r g v ) ;
34
35 w i n d o w = g t k _ w i n d o w _ n e w ( G T K _ W I N D O W _ T O P L E V E L ) ;
36 g t k _ w i n d o w _ s e t _ t i t l e ( G T K _ W I N D O W ( w i n d o w ) , " S i g n a l S a m p l e 2 " ) ;
37 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 , 2 5 0 , 5 0 ) ;
38 g _ s i g n a l _ c o n n e c t ( G _ O B J E C T ( w i n d o w ) , " d e s t r o y " ,
39 G _ C A L L B A C K ( g t k _ m a i n _ q u i t ) , N U L L ) ;
40
41 b u t t o n = g t k _ b u t t o n _ n e w _ w i t h _ l a b e l ( " C l i c k m e ! " ) ;
42 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 ) , b u t t o n ) ;
43
44 g _ s i g n a l _ c o n n e c t _ a f t e r ( G _ O B J E C T ( b u t t o n ) , " c l i c k e d " ,
45 G _ C A L L B A C K ( c b _ b u t t o n 3 ) , N U L L ) ;
46 g _ s i g n a l _ c o n n e c t _ a f t e r ( G _ O B J E C T ( b u t t o n ) , " c l i c k e d " ,
47 G _ C A L L B A C K ( c b _ b u t t o n 4 ) , N U L L ) ;
48 g _ s i g n a l _ c o n n e c t ( G _ O B J E C T ( b u t t o n ) , " c l i c k e d " ,
49 G _ C A L L B A C K ( c b _ b u t t o n 1 ) , N U L L ) ;
50 g _ s i g n a l _ c o n n e c t ( G _ O B J E C T ( b u t t o n ) , " c l i c k e d " ,
51 G _ C A L L B A C K ( c b _ b u t t o n 2 ) , N U L L ) ;
52
53 g t k _ w i d g e t _ s h o w _ a l l ( w i n d o w ) ;
54
55 g t k _ m a i n ( ) ;
56
57 r e t u r n 0 ;
58 }
g signal connect swappedのプログラム例
次に関数g signal connect swappedの動作について解説します.
これまで説明した関数では,コールバック関数の第1引数にはコールバック関数を関連付けたウィジェットが渡されました.
しかし,関数g signal connect swappedで設定したコールバック関数の第1引数には,関数g signal connect swappedの第 4引数に指定したウィジェットが渡されます.実際にどのように動作するのか次の例を見てみましょう.ソースコードはソース 3–5です.
ソースコードの34–35行目では,1つ目のボタンを作成してコールバック関数を設定しています.このコールバック関数で は,ボタンをクリックするたびにボタンが何回クリックされたかをボタンのラベルに表示します.
次に39–40行目で2つ目のボタンを作成して,関数g signal connect swappedでコールバック関数を設定しています.コー
ルバック関数は1つ目のボタンと共通です.
このコールバック関数は,第1引数に渡されたウィジェットのラベルを書き換えるようになっているので,2つ目のボタン のコールバック関数を関数g signal connect等で設定すると,2つ目のボタンのラベルが更新されるはずです.しかし,関数 g signal connect swappedの第4引数に1つ目のウィジェットを指定しているので,2つ目のボタンがクリックされても,コー ルバック関数の第1引数に渡されるのは1つ目のボタンウィジェットということになります.
つまり,どちらのボタンがクリックされても,ラベルが更新されるのは1つ目のボタンになります.
プログラムを実行すると,図3.8のウィンドウが表示されます.実際に両方のボタンをクリックして動作を確認してみてくだ さい.
ソース3–5 関数g signal connect swappedの例: signal-sample3.c
1 # i n c l u d e <g t k / g t k . h >
2
3 s t a t i c v o i d
4 c b _ b u t t o n (G t k W i d g e t * w i d g e t , g p o i n t e r u s e r _ d a t a )
5 {
6 s t a t i c g i n t c o u n t = 0 ;
7 g c h a r b u f [ 6 4 ] ;
8
9 s p r i n t ( buf , " % d t i m e ( s ) c l i c k e d . " , + + c o u n t ) ;
10 g t k _ b u t t o n _ s e t _ l a b e l ( G T K _ B U T T O N ( w i d g e t ) , b u f ) ;
11 }
12 13 i n t
14 m a i n (i n t ar g c , c h a r * * a r g v )
15 {
16 G t k W i d g e t * w i n d o w ;
図3.8 関数g signal connect swappedの例
17 G t k W i d g e t * h b o x ;
18 G t k W i d g e t * b u t t o n 1 ;
19 G t k W i d g e t * b u t t o n 2 ;
20
21 g t k _ i n i t ( & a r g c , & a r g v ) ;
22
23 w i n d o w = g t k _ w i n d o w _ n e w ( G T K _ W I N D O W _ T O P L E V E L ) ;
24 g t k _ w i n d o w _ s e t _ t i t l e ( G T K _ W I N D O W ( w i n d o w ) , " S i g n a l S a m p l e 3 " ) ;
25 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 , 5 0 0 , 5 0 ) ;
26 g _ s i g n a l _ c o n n e c t ( G _ O B J E C T ( w i n d o w ) , " d e s t r o y " ,
27 G _ C A L L B A C K ( g t k _ m a i n _ q u i t ) , N U L L ) ;
28
29 h b o x = g t k _ h b o x _ n e w ( T R U E , 0 ) ;
30 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 ) , h b o x ) ;
31
32 b u t t o n 1 = g t k _ b u t t o n _ n e w _ w i t h _ l a b e l ( " 0 t i m e ( s ) c l i c k e d . " ) ;
33 g t k _ b o x _ p a c k _ s t a r t ( G T K _ B O X ( h b o x ) , b u t t o n 1 , T R U E , T R U E , 0 ) ;
34 g _ s i g n a l _ c o n n e c t ( G _ O B J E C T ( b u t t o n 1 ) , " c l i c k e d " ,
35 G _ C A L L B A C K ( c b _ b u t t o n ) , N U L L ) ;
36
37 b u t t o n 2 = g t k _ b u t t o n _ n e w _ w i t h _ l a b e l ( " C l i c k m e ! " ) ;
38 g t k _ b o x _ p a c k _ s t a r t ( G T K _ B O X ( h b o x ) , b u t t o n 2 , T R U E , T R U E , 0 ) ;
39 g _ s i g n a l _ c o n n e c t _ s w a p p e d ( G _ O B J E C T ( b u t t o n 2 ) , " c l i c k e d " ,
40 G _ C A L L B A C K ( c b _ b u t t o n ) , (g p o i n t e r) b u t t o n 1 ) ;
41
42 g t k _ w i d g e t _ s h o w _ a l l ( w i n d o w ) ;
43
44 g t k _ m a i n ( ) ;
45
46 r e t u r n 0 ;
47 }