Fltk 1.3 系列教程(4)
原創(chuàng),轉(zhuǎn)載請注明出處。Copyright (c) Dingmaotu(Leading) (dingmaotu@126.com)
Callback、面向?qū)ο蠛妥址幚?/span>
以前多在163博客上寫東西,現(xiàn)在發(fā)現(xiàn)對于編程的事,還是在博客園上有人關(guān)注。原來的3個教程轉(zhuǎn)過來,成了轉(zhuǎn)載(雖然是自己轉(zhuǎn)自己的),
發(fā)到首頁,被警告了……這次在博客園首發(fā)吧
上一次學(xué)了高級一點(diǎn)的布局,以及怎樣處理resize。現(xiàn)在讀者應(yīng)該能根據(jù)手冊創(chuàng)建任意控件組合的復(fù)雜的對話框了。
這一節(jié)我主要講一下Fltk對控件事件的處理,順便把上節(jié)課的例子重構(gòu)成面向?qū)ο箫L(fēng)格的。另外Fltk中有些特性(或Bug)
已經(jīng)過時,很讓人不爽,其中字符串處理就是一個。
在Fltk中,每一個控件只有一個我們感興趣的事情,比如對于按扭,就是用戶按下它,對于文本輸入框,就是文本改變等。
所以每一個控件(Fl_Widget)都有一個方法叫callback(Fl_Callback*, void *),指定一個回調(diào)函數(shù)以及傳給這個函數(shù)的參數(shù)。
回調(diào)函數(shù)的原型是這樣的:
typedef void (Fl_Callback)(Fl_Widget*, void *);
第一個參數(shù)是事件發(fā)生的那個控件,第二個是在指定callback時給的任意參數(shù)。這個函數(shù)試用于所有控件,因此,我們這一節(jié)
課貌似又要結(jié)束了……不過還是那句話,寫代碼時還會遇到問題,下面是所有的代碼(很多注釋,主要內(nèi)容都在這里):
1 #include "FL/Fl.H"
2 #include "FL/Fl_Double_Window.H"
3 #include "FL/Fl_Button.H"
4 #include "FL/Fl_Input.H"
5 #include "FL/Fl_Multiline_Input.H"
6 #include "FL/Fl_Box.H"
7 #include "FL/Fl_Pack.H"
8
9 // 以下文件包含的是常見的顯示消息的函數(shù),例如
10 // void fl_alert(const char *fmt, ...), 顯示信息,像MessageBox
11 // int fl_ask(const char *fmt, ...), 問問題(返回1或0)
12 // void fl_beep(int type), beep一下
13 // 這些函數(shù)都很方便(printf類型的),但是為什么不放到Fl.H這個大雜燴類呢?
14 // 可見Fltk的API還有諸多不合理之處
15 #include "FL/fl_ask.H"
16
17 class EscapeChar; // 見下文
18
19 // Fltk中最方便的就是繼承已有的類,進(jìn)行各種自定義
20 class CommentDialog: public Fl_Double_Window
21 {
22 public:
23 // 繼承的構(gòu)造函數(shù)格式也基本是固定的
24 CommentDialog(int x, int y, int w, int h, const char* title)
25 :Fl_Double_Window(x, y, w, h, title)
26 {
27 CreateUI();
28 size_range(w, h, 0, 0);
29 resizable(_comment);
30 // 對于Window來說,它的Callback就是當(dāng)它被關(guān)閉時要調(diào)用的函數(shù)
31 // 這對于實(shí)現(xiàn)文檔有改變,問用戶想不想繼續(xù)之類很有用
32 this->callback(gcallback, (void*)this);
33 }
34 // 這是Fltk標(biāo)準(zhǔn)的也是唯一的回調(diào)函數(shù),第一個是指向觸發(fā)回調(diào)的控件,
35 // 第二個是在綁定回調(diào)時設(shè)置的任意參數(shù)。
36 //
37 // 使用面向?qū)ο蟮娘L(fēng)格時,因?yàn)椴荒馨杨惓蓡T函數(shù)作為回調(diào)函數(shù),所以一般使用這種方法:
38 // 在父控件的類中聲明一個靜態(tài)回調(diào)函數(shù),所有的子控件需要設(shè)置回調(diào)時
39 // 都使用這個回調(diào),而這個回調(diào)的第二個參數(shù)可以設(shè)為
40 // 父控件類實(shí)例的指針,通過這個指針調(diào)用相應(yīng)的方法
41 static void gcallback(Fl_Widget* w, void *data)
42 {
43 CommentDialog *d = (CommentDialog*)data;
44 if(w == d->_name->child(1)) d->OnNameCheck(w);
45 else if(w == d->_mail->child(1)) d->OnMailCheck(w);
46 else d->OnOk(w); // “好的”按鈕或者直接關(guān)閉窗口
47 }
48
49 void OnNameCheck(Fl_Widget* w);
50 void OnMailCheck(Fl_Widget* w);
51 void OnOk(Fl_Widget* w);
52
53 const char* GetName() const {return ((Fl_Input*)_name->child(0))->value();}
54 const char* GetMail() const {return ((Fl_Input*)_mail->child(0))->value();}
55 const char* GetComment() const {return _comment->value();}
56
57 private:
58 void CreateUI();
59 Fl_Pack *CreatePack(int y, const char * item, const char* button);
60
61 Fl_Pack * _name;
62 Fl_Pack * _mail;
63 Fl_Pack * _ok;
64 Fl_Multiline_Input * _comment;
65 };
66
67 void CommentDialog::CreateUI()
68 {
69 _name = CreatePack(10, "姓名:", "檢查!");
70 _mail = CreatePack(50, "郵件:", "檢查!");
71 _comment = new Fl_Multiline_Input(50, 100, w()-60, h()-150, "評論:");
72 _ok = CreatePack(h()-40, NULL, "好的");
73 }
74
75 Fl_Pack * CommentDialog::CreatePack(int y, const char * item, const char* button)
76 {
77 Fl_Pack * pack = new Fl_Pack(50, y, w()-20, 30);
78 pack->type(Fl_Pack::HORIZONTAL);
79 pack->spacing(10);
80 Fl_Widget * itemw;
81 if(item)
82 itemw = new Fl_Input(0,0,w()-175, 30, item);
83 else
84 itemw = new Fl_Box(0, 0, w()-175, 30);
85 Fl_Button *b = new Fl_Button(0, 0, 100, 30, button);
86 b->callback(CommentDialog::gcallback, (void *)this);
87 pack->end();
88
89 pack->resizable(itemw);
90 return pack;
91 }
92 // 以下的檢查代碼只是把輸入顯示出來!因?yàn)楸敬沃饕v回調(diào)函數(shù)的設(shè)置,
93 // 所以沒有實(shí)現(xiàn)真正的檢查代碼。注意Fltk很方便的顯示信息的函數(shù)
94 void CommentDialog::OnNameCheck(Fl_Widget* w)
95 {
96 EscapeChar name(GetName());
97 fl_alert("您好: %s!", (const char*) name);
98 }
99 void CommentDialog::OnMailCheck(Fl_Widget* w)
100 {
101 EscapeChar name(GetName());
102 EscapeChar mail(GetMail());
103 fl_alert("%s, 您好! 您的Email是:\n%s!",
104 (const char*)name,
105 (const char*)mail);
106 }
107 void CommentDialog::OnOk(Fl_Widget* w)
108 {
109 EscapeChar name(GetName());
110 EscapeChar comment(GetComment());
111 fl_alert("%s 認(rèn)為:\n%s\n多謝您的評論!",
112 (const char*)name,
113 (const char*)comment);
114 // 當(dāng)最后一個窗口隱藏時,F(xiàn)ltk自動退出
115 hide();
116 }
117
118 int main(int argc, char **argv)
119 {
120 CommentDialog *dlg = new CommentDialog(100, 200, 460, 320, "FltkCallback");
121 dlg->show();
122 return Fl::run();
123 }
大家可以看到,大部分代碼都還是和第三部分所講的一樣的,如果您對任何內(nèi)容不熟悉,可以再復(fù)習(xí)“Fltk 1.3 系列教程(3)”。
這是代碼運(yùn)行的截圖:

上面的代碼還展示了怎樣在面向?qū)ο蟮那樾蜗率褂妙惓蓡T方法作為回調(diào)處理函數(shù),因?yàn)镕ltk的回調(diào)機(jī)制比較通用靈活,這些的
實(shí)現(xiàn)都很簡單。通過封裝已有代碼,就構(gòu)建了一個可重用的對話框,在實(shí)際情況下應(yīng)該比直接過程式代碼好用。
細(xì)心與好奇的讀者可能注意到那個奇怪的EscapeChar類。如果你不做任何處理,你會發(fā)現(xiàn)在fl_alert不能正確顯示
email的“@”字符。為什么呢?因?yàn)镕ltk一個過時的特性,label和symbol系統(tǒng)。在任何Fltk控件的label設(shè)置字符串
中(即構(gòu)造函數(shù)中指定的字符串或者label(const char*)函數(shù)中設(shè)置的字符串),如果有且僅有以下符號(摘自Fltk 1.3 Doc):

Fltk會把它顯示成這些圖形符號,看起來非常方便,但是實(shí)際上問題很多,例如一但有其他字符串出現(xiàn),就完全
不顯示了。大家可以參考http://www.fltk.org/str.php?L2689,這種特性問題之多難以處理。所以建議在
新代碼中,盡量少使用這些功能。Fl_Browser控件中也使用“@”符號來表示字符串的格式,在開頭加上“@.”就
可以關(guān)閉格式解析,問題不大。但是在我們的代碼中email肯定含有“@”符號,而這個是不能在消息框的label中
正確顯示的。怎么解決這個問題呢?
第一種是高手使用的方法,也是比較惡心的方法,就是自己實(shí)現(xiàn)繪制符號的函數(shù),然后替換默認(rèn)的符號處理系統(tǒng),
這樣一來,一切都清靜了。見Erco的例子大全:http://seriss.com/people/erco/fltk/#DisableSymbols。
第二種就是把字符串中的“@”字符替換成“@@”,這樣就可以轉(zhuǎn)義掉“@”字符,相對于修改整個系統(tǒng)的行為,這個
方法還是比較容易接受的。但是問題又出來了:如果Fltk提供了字符串處理函數(shù)或者它就是用的ASCII,實(shí)現(xiàn)這個功能是
很簡單的,但是它使用的是UTF8,于是我們又不得不看看Fltk中的UTF8函數(shù)了。
大家知道在1.3之前Fltk是不支持Unicode的,這對于CJK國家非常不便。1.3通過utf8支持了unicode,但是這些
支持是非常基礎(chǔ)的,在"FL/fl_utf8.h"中,只有最底層的utf8處理函數(shù),而不是高層的字符串操作函數(shù)。雖然UTF8
兼容ASCII,但是即使輸入都是ASCII,你按照默認(rèn)的處理也是不行的(會有亂碼,只能保證它是'\0'結(jié)束的)。
所以鄙人認(rèn)為實(shí)現(xiàn)一個fl_string類是十分必要的。但是這個教程不能面面俱到,可能以后會專門寫一篇文章講這個
類的實(shí)現(xiàn)。下面的類是EscapeChar,能轉(zhuǎn)義UTF8字符串中的任何ASCII字符。
1 // 不得已而為之
2 #include <cstring> // for strlen strcpy
3 #include "FL/fl_utf8.h" // for fl_utf8len(char c)
4 class EscapeChar
5 {
6 public:
7 EscapeChar(const char* text, char c='@')
8 :_c(c)
9 {
10 _text = new char[2*strlen(text)];
11 int k = 0;
12 while(*text){
13 int j = fl_utf8len(*text);
14 for(int i = 0; i < j; ++i)
15 _text[k+i] = *text++;
16 k += j;
17 if( j == 1 && _text[k-j] == _c){
18 _text[k] = _c;
19 k += j;
20 }
21 }
22 _text[k] = '\0';
23 }
24 ~EscapeChar()
25 {
26 if(_text) delete _text;
27 }
28 EscapeChar(const EscapeChar& e)
29 :_c(e._c)
30 {
31 _text = new char[strlen(e._text)];
32 strcpy(_text, e._text);
33 }
34
35 EscapeChar& operator=(const EscapeChar& e)
36 {
37 if(&e == this)
38 return *this;
39 _c = e._c;
40 size_t len = strlen(e._text);
41 if(strlen(_text) < len){
42 delete _text;
43 _text = new char[len];
44 }
45 strcpy(_text, e._text);
46
47 return *this;
48 }
49 operator char*() const {return _text;}
50
51 private:
52 char _c;
53 char *_text;
54 };
這次的教程就這么多內(nèi)容,有任何問題,歡迎指出。以前的教程如下,供感興趣的人參考:
http://www.rzrgm.cn/leading/archive/2011/10/15/fltk_1_3_tutorial_3.html
http://blog.163.com/dingmaotu@126/blog/static/2148430201183072750266/ (教程2,因?yàn)樵瓉砭W(wǎng)易圖片不讓外鏈,很難看,干脆鏈原來的網(wǎng)址吧)
http://www.rzrgm.cn/leading/archive/2011/10/15/fltk_1_3_tutorial_1.html

浙公網(wǎng)安備 33010602011771號