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