各位好, 又是在下,還記得上期筆者為大家介紹了很多不同的多平臺(tái) Framework 嗎?其中有一套是在下認(rèn)為表現(xiàn)最好的 -- wxWindows。還記得有一次依科跟我說(shuō)了一句 : " wxWindows 比 GTK 難寫(xiě), 又不比 Qt 好用" (大概是這樣吧)..... 唔... wxWindows 真是這麼難, 這麼不好用嗎? 請(qǐng)讓筆者慢慢的替各位介紹吧。
wxWindows 簡(jiǎn)介
關(guān)於 wxWindows 的基本資料上期也已經(jīng)介紹了, 它支援 Win3.1、Win32、GTK、motiff 還有其他如 wxPython 的套件。 這篇文章主要會(huì)介紹一下 wxWindows 各物件及在 Linux 下示範(fàn)一下如何用它篇寫(xiě)程式。
要用 wxWindows 篇程,當(dāng)然是先要安裝 wxWindows 了。使用 Windows 版的讀者在下建議要用 Visual C++ Compile, 因?yàn)槭褂闷渌?compiler 據(jù)筆者的經(jīng)驗(yàn)非要花一個(gè)下午不可,如果大家想快快的 compile 完使用的話便請(qǐng)乖乖地用 Visual C++ 吧~ ^^ 目錄中會(huì)有一個(gè)名為 src/wxvc.dsp 的檔案,把它拿到 Visual C++ 下Compile 應(yīng)該沒(méi)有問(wèn)題。如果其間出現(xiàn)問(wèn)題的話可能只是一些特別字元被吃了(在 dev list 上說(shuō) Unicode 下可能出現(xiàn)此程況 .... ) ,那大家只需把那些字元加上去便成了。Compile 完成後會(huì)有數(shù)個(gè) .lib 檔出現(xiàn), 其中 wx.lib 或 wxd.lib (Debug mode) ,建議大家沒(méi)事幹 compile wx.lib 出來(lái)便好了。至於 DLL 麻.... 你要的話便 compile src/wxvc_dll.dsp 吧! 其它 compiler 的篇釋方法便請(qǐng)參考 install.txt。
至於 Linux 的 GTK 版可沒(méi)有那麼煩 (哈哈! 誰(shuí)說(shuō) Windows 下的程式一定比 Linux 下易用)。筆者在篇釋 Linux GTK 版遇到的麻煩比 Windows 版少的多...
./configure --with-gtk
make
make install
ldconfig
呵呵, 那麼 dynamic 版便會(huì)被篇釋出來(lái),如果要用 static 版, 請(qǐng)先把 dynamic 版移除,再重新安裝,在 ./configure 後加上 --disable-shared。
至於在寫(xiě)程式時(shí)如何使用它? 使用 Visual C++ 的讀者在下建議把 wxWindows 的 Minimal Project Sample 當(dāng)成 Template, 以後寫(xiě)新程式 copy 一份來(lái)用好了, 免得 PATH , INCLUDEDIR 一大堆慢慢填上。至於用 gcc 的? 簡(jiǎn)單了,加上這個(gè)參數(shù) :
`wx-config --libs --cflags`
便會(huì)自動(dòng)加上所有要用的參數(shù) (留意, 是 "`" 號(hào)不是 "'" 呀! 不知道` ` 用來(lái)幹什麼的人請(qǐng)看一看參考書(shū)...)
如果你已經(jīng)完成了 wxWindows 的安裝, 和成功的 compile "Minimal" 這個(gè) samples,那你便成功了一半啦! (特別是用 Windows 版的, 筆者用 C++ Builder 4 Compile 時(shí)差點(diǎn)兒昏過(guò)去, 最後還是用 Visual C++ la!)
為方便起見(jiàn), 以下的範(fàn)例都是用 Linux GTK 版的,但 Win32 版當(dāng)然也能夠 compile。
Hello World!
又是 Hello World? 那... 當(dāng)然了... 要介紹一隻 Framework 沒(méi)有 比helloworld 更好的範(fàn)例吧! 筆者寫(xiě)了一個(gè)簡(jiǎn)單的範(fàn)例, 儲(chǔ)存作 helloworld.cpp, 內(nèi)容如下:
#include
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif
#ifndef WX_PRECOMP
#include
#endif
class MyApp: public wxApp
{
public:
bool OnInit();
};
class MyWindows : public wxFrame
{
public:
MyWindows(const wxString& title);
void OnExit(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
private:
wxButton *bn_exit;
DECLARE_EVENT_TABLE()
};
enum
{
Btn_exit = 100,
};
BEGIN_EVENT_TABLE(MyWindows, wxFrame)
EVT_CLOSE(MyWindows::OnExit)
EVT_BUTTON(Btn_exit, MyWindows::OnQuit)
END_EVENT_TABLE()
IMPLEMENT_APP(MyApp)
MyWindows *frame;
bool MyApp::OnInit()
{
frame = new MyWindows("Hello World!");
frame->Show(TRUE);
SetTopWindow(frame);
return TRUE;
}
MyWindows::MyWindows(const wxString& title)
: wxFrame((wxFrame *) NULL, -1, title,
wxPoint(0,0), wxSize(600, 400),
wxDEFAULT_FRAME_STYLE)
{
bn_exit = new wxButton(this, Btn_exit, "Exit!", wxPoint(0, 0), wxSize(100,50));
}
void MyWindows::OnExit(wxCommandEvent &event)
{
Destroy();
}
void MyWindows::OnQuit(wxCommandEvent &event)
{
frame->Close(TRUE);
}
再 compile, 指令如下:
gcc `wx-config --libs --cflags` helloworld.cpp -o hello
./hello
結(jié)果如下:
快樂(lè)吧! 才不過(guò)六十行,而且我相信各位如果曾有篇程經(jīng)驗(yàn), 應(yīng)該一眼便看出此程式的意思。程式碼看完了,讓我解說(shuō)一下吧!
讓我們看一看 wxWindows 程式的特別架構(gòu):
首先是 include file:
#include
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include
#endif
基本上只是把 wx.h 連入來(lái)便好了,wxprec.h 也是包有 wx.h 的。 wxprec.h 是用來(lái)作 precompilation 的(在 Visual C++ 下, 而 Borland C++ 你用什麼它也會(huì) precompilation 的了。)至於什麼是 precompilation 則在此不提了, 總之你在 compile 完程式一次後再 compile 第二次時(shí)會(huì)很爽便是了, heeee。
然後便是定義那些 class 啦, 範(fàn)例中....
class MyApp: public wxApp
{
public:
bool OnInit();
};
class MyWindows : public wxFrame
{
public:
MyWindows(const wxString& title);
void OnExit(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
private:
wxButton *bn_exit;
DECLARE_EVENT_TABLE()
};
wxApp 是必需的,每個(gè)程式也要用,其中的 OnInit() function 會(huì)在程式開(kāi)始時(shí)被 Call 到。 至於 MyWindows 的祖先 wxFrame 則是一個(gè)基本 Window ,是有其他代替品的,不過(guò)最常用也算是這個(gè)了。
比較要注意的是 wxFrame 下的 DECLARE_EVENT_TABLE()。 wxWindows 用了很多的 Macro, DECLARE_EVENT_TABLE() 是在每件你自己定義並繼承了 wxWindows 中要接受 EVENT 的物件也要call一次的 Macro,要留意留意啦~ 否則便會(huì)有一串 ErrorMsg 在 compile 時(shí)跑出來(lái)~ heee
至於
enum
{
Btn_exit = 100,
};
則只是定義一個(gè) ID 用來(lái)分辨一會(huì)兒的物件。
EVENT_TABLE 便是主菜了。 wxWindows 使用了 EVENT_TABLE 來(lái)讓不同的 Object 接收 Event wxMenu 可以有自己的 EVENT_TABLE, wxFrame 也會(huì)有, 以下便是這個(gè)程式的 EVENT table 了。
BEGIN_EVENT_TABLE(MyWindows, wxFrame) // wxFrame 是 MyWindows 的祖先
EVT_CLOSE(MyWindows::OnExit) // EVT_CLOSE 是指 wxFrame close 時(shí)
EVT_BUTTON(Btn_exit, MyWindows::OnQuit) //EVT_BUTTON 當(dāng)然是 button 被按下啦~
END_EVENT_TABLE()
看到了嗎? EVENT_TABLE 是不是很方便? 感覺(jué)上是 ar! 我比較喜歡使用 EVENT TABLE。
IMPLEMENT_APP(MyApp)
唔..... 大家有發(fā)現(xiàn)程式由頭到尾都沒(méi)有 main() 嗎? 那怎樣行? heee... IMPLEMENT_APP 這個(gè) Macro 會(huì)替大家寫(xiě)好 main() 的啦! 所以每個(gè) wxWindows 的程式都要有 IMPLEMENT_APP 。 還記得剛才說(shuō)過(guò)程式開(kāi)始運(yùn)行時(shí)會(huì) call OnInit() 嗎? 所以我們會(huì)在 OnInit() 中顯示 MyWindows 那個(gè) windows。 程式開(kāi)始了!
bool MyApp::OnInit()
{
frame = new MyWindows("Hello World!");
frame->Show(TRUE);
SetTopWindow(frame);
return TRUE;
}
再後面的程式碼相信大家看一看 wxWindows 的 manuel 便能夠明白了。比較要注意的是其中 OnExit call 了 Destroy ();,在 wxWindows 中 如果你要?jiǎng)h除一個(gè) wxFrame, 不能使用 delete,而要使用 Destroy 的,大家該注意一下。
wxWindows 的 Object
wxWindows 提供了很多元件, 大家可以在 wxWindows 的 Manuel 中看到。讓我在此簡(jiǎn)單地介紹一下常用的元件吧!
1) Windows 類
wxDialog、wxFrame 都是常用的基本 windows、 wxMDIParentFrame、wxMDIChildFrame , 靠這兩個(gè)元件便可以實(shí)作 MDI 程式設(shè)計(jì)介面。 wxWizard 可以讓你建做一個(gè)精靈。 wxPanel、wxScrolledWindow 都十分常用
2) Common dialogs 類
除了提供一般的 Dialog 例如 Open File, Print Setup 之類,還有要求別人輸入文字的 wxTextEntryDialog, 和要求別人作選擇的 wxSingleChoiceDialog 和 wxMultipleChoiceDialog。
3) 一般元件
wxButton, wxBitmapButton 之類都有.... 相信不用多作介紹了。 其中 wxMenu 除了可以插入在 wxFrame 中還可以配合 PopupMenu(wxMenu, x, y) 此 function 作 POPUP Menu。
4) Layout 類
有一些如 wxSizer 之類的元件~ 但筆者還是偏向用傳統(tǒng)的方法安排元件位置,除非有像 Jav a 的 Layout 一樣方便的元件吧!
5) DC
wxWindows 也容許你在畫(huà)面或 Printer 什至是 PostScript 檔案上直接繪上圖像或文字。
6) 圖像類
wxBitmap 配合"ImageHandlers"可以載入很多不同的圖像格式如 JPG, PNG 之類,十分方便而 wxFont, wxPen 之類則可以配合 DC 。
7) 事件類
不同的事件會(huì)傳回一個(gè)事件元件,例如 wxCommandEvent,OnPaint 則有 wxPaintEvent, Mouse Event 則用 wxMouseEvent 來(lái)告知你相關(guān)的資訊,等等~~~
8) 資料結(jié)構(gòu)
wxWindows 提供了很多資料結(jié)構(gòu)如 wxString, wxHashTable , wxList 之類,用來(lái)減少對(duì) STL 的倚重( Manuel 中說(shuō)因?yàn)椴煌?compiler 對(duì)STL 的支援度不一, 所以最好不要用 STL)。
9) Network
wxWindows 有 wxDialUpManager, wxHTTP, wxURL, wxSocketClient 之類的元件, 也是很方便, 能夠解決 Socket 的跨平臺(tái)問(wèn)題。唯獨(dú) wxURL 中讀取 HTTP 的部分好像還是使用 HTTP1.0 如果你要用它來(lái)下載網(wǎng)頁(yè)可要考慮一下。因?yàn)楝F(xiàn)在 support VirtualHosting 的 web server 大多要求 client 用 HTTP1.1, 所以可能要自己重寫(xiě)了。
10) 資料流
wxWindows 提供了運(yùn)用資料流概念的元件, 如 wxInputStream、wxFileOutputStream、 wxSocketInputStream 之類,把資料流的概念用在 File, Socket 什至是壓縮資料方面。方便篇程。
11) HTML
wxWindows 中的 wxHTML 十分好用~ 可以把 HTML 直接送到 wxHTML 中顯示,那麼制作 Help File 之類也可以直接用 HTML 然後在程式中顯示。
12) 其他
除此之外 wxWindows 還有很多元件呢~ 在 IPC 方面有 DDE/ TCP 的classes。 又有 wxDocume nt 方便寫(xiě)主要作文件處理的程式。 wxPrinter , wxPreviewFrame 方便在跨平臺(tái)程式中加入列印部分。也提供 Drag and Drog, 檔案讀寫(xiě)和 Multi-Thread 等必要的功能。而且各位喜歡的話也可以自己寫(xiě)元件或是使用別人寫(xiě)的元件,例如有人為 wxWindows 寫(xiě)了一個(gè) RichText Editor (可惜在 Linux 下不能輸入中文)。怎麼樣? 心動(dòng)了吧!
較為複雜的例子
這次作者將會(huì)把自己寫(xiě)的一個(gè)程式的 GUI 部分刊登出來(lái)並簡(jiǎn)單的解說(shuō)一下,讓大家看一看實(shí)際用 wxWindows 篇程是怎樣作的。
main.h :
#include
#ifndef WX_PRECOMP
#include
#endif
// 因?yàn)闀?huì)用到特別的功能, 所以必需 include 起來(lái)
#include
#include
#define ICON_POSITION "images/n_icons.png"
#define SMALL_WINDOWS_WIDTH 64
#define SMALL_WINDOWS_HEIGHT 64
#define APP_NAME "NTools"
#define FRAME_NAME "Main"
#define URL_CONF_PATH "/URL/data"
class MyCenterMenu: public wxMenu
{
public:
MyCenterMenu(const wxString& title);
private:
// 因?yàn)?wxMenu 也會(huì)接收 Event, 所以要 DECLARE_EVENT_TABLE()
DECLARE_EVENT_TABLE()
};
class MyApp: public wxApp
{
public:
bool OnInit();
};
class MySmallWin: public wxFrame
{
public:
MySmallWin(const wxString& title);
void OnExit(wxCommandEvent& event);
void DoClose(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnPaint(wxPaintEvent& event);
void OnLeftDown(wxMouseEvent &event);
void OnRightDown(wxMouseEvent &event);
void OnMotion(wxMouseEvent &event);
wxBitmap *m_image1;
MyCenterMenu *menu;
int m_oldx, m_oldy;
private:
DECLARE_EVENT_TABLE()
};
enum
{
ID_CenterClick = 1,
Menu_Help_About,
Menu_Config,
Menu_Target,
Menu_Exit,
Timer_Check,
};
main.cpp:
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif
#include "main.h"
BEGIN_EVENT_TABLE(MySmallWin, wxFrame)
// 每次有 Close Event 時(shí)(例如 User 按下右上角的交差)都會(huì)被叫到
EVT_CLOSE(MySmallWin::OnExit)
EVT_PAINT(MySmallWin::OnPaint)
// 解說(shuō)一下吧, 這個(gè)程式當(dāng) user 按下右 Mouse 時(shí), 便會(huì)彈出 menu
EVT_RIGHT_DOWN(MySmallWin::OnRightDown)
EVT_LEFT_DOWN(MySmallWin::OnLeftDown)
// 當(dāng)按下左 Mouse 不放並移動(dòng) Mouse 便會(huì)移動(dòng) Windows。
EVT_MOTION(MySmallWin::OnMotion)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(MyCenterMenu, wxMenu)
EVT_MENU(Menu_Exit ,MySmallWin::DoClose)
EVT_MENU(Menu_Help_About ,MySmallWin::OnAbout)
// 因?yàn)橹皇枪?fàn)例, 所以按什麼也會(huì)關(guān)閉。
EVT_MENU(Menu_Target ,MySmallWin::DoClose)
EVT_MENU(Menu_Config ,MySmallWin::DoClose)
END_EVENT_TABLE()
IMPLEMENT_APP(MyApp)
MySmallWin *frame;
bool MyApp:: OnInit()
{
frame = new MySmallWin("NTools");
frame->Show(TRUE);
SetTopWindow(frame);
return TRUE;
}
MySmallWin::MySmallWin(const wxString& title)
: wxFrame((wxFrame *) NULL,
-1, title,
wxPoint(0,0), wxSize(SMALL_WINDOWS_WIDTH,SMALL_WINDOWS_HEIGHT),
// 這樣 Window 便會(huì)縮小了
wxMINIMIZE_BOX | wxSTAY_ON_TOP | wxSYSTEM_MENU | wxSIMPLE_BORDER )
{
// 用wxInitAllImageHandlers() 先開(kāi)動(dòng)所有 ImageHandler
wxInitAllImageHandlers();
// 載入那個(gè) PNG 圖檔
m_image1 = new wxBitmap(ICON_POSITION, wxBITMAP_TYPE_PNG);
}
void MySmallWin::DoClose(wxCommandEvent& event)
{
frame->Close(TRUE);
}
void MySmallWin::OnExit(wxCommandEvent& event)
{
Destroy();
}
void MySmallWin::OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(this);
PrepareDC(dc);
// 在每次需要重繪時(shí)便把 m_image1 畫(huà)一次
dc.DrawBitmap(*m_image1, 0 , 0);
}
void MySmallWin::OnRightDown(wxMouseEvent& event)
{
// 先填上 Menu 中的資料
menu = new MyCenterMenu("NTools");
menu->Append(Menu_Help_About, "&About");
menu->Append(Menu_Config, "&Config");
menu->Append(Menu_Target, "&URL Manager");
menu->AppendSeparator();
menu->Append(Menu_Exit, "&Exit");
//wxMenu 配合 PopupMenu 可以實(shí)作 Popup menu 用
PopupMenu(menu, event.GetX(), event.GetY());
}
void MySmallWin::OnLeftDown(wxMouseEvent& event)
{
m_oldx = event.GetX();
m_oldy = event.GetY();
}
void MySmallWin::OnMotion(wxMouseEvent& event)
{
// 如果 User 按了左邊 Mouse...
if(event.LeftIsDown())
{
int x,y;
x=event.GetX();
y=event.GetY();
// 便跟隨它的 Mouse 移動(dòng)吧!
// ClientToScreen 可以把在 window 中的 x,y 座標(biāo)轉(zhuǎn)為 Screen 上的 x,y 座標(biāo)
frame->ClientToScreen(&x, &y);
x=x-m_oldx;
y=y-m_oldy;
// SetSize 可以設(shè)定 Window 的位置和大小
frame->SetSize(x, y, 64, 64);
}
}
MyCenterMenu::MyCenterMenu(const wxString& title)
: wxMenu(title)
{
}
about.h :
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif
#include "main.h"
#include
//About Dialog 的Window
class MyAbout: public wxFrame
{
public:
MyAbout(wxFrame *parent, const wxString& title);
void OnExit(wxCommandEvent& event);
void OnSize(wxCommandEvent& event);
wxHtmlWindow *about_html;
private:
DECLARE_EVENT_TABLE()
};
about.cpp :
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif
#include "about.h"
extern MySmallWin *frame;
char *about_content = NTools 0.1
Written By Chpapa (c) 2001
The program is free software, you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software Foundation
, either version 2 of the License, or any later version.
Chpapa (admin@oursky.com)
BEGIN_EVENT_TABLE(MyAbout, wxFrame)
EVT_CLOSE(MyAbout::OnExit)
EVT_SIZE(MyAbout::OnSize)
END_EVENT_TABLE()
MyAbout::MyAbout(wxFrame *parent , const wxString& title)
: wxFrame(parent,
-1, title,
wxPoint(0,0), wxSize(320,240),
wxSTAY_ON_TOP | wxDEFAULT_FRAME_STYLE)
{
int w, h;
this->GetClientSize(&w, &h);
about_html = new wxHtmlWindow(this, -1, wxPoint(0,0), wxSize(w,h));
// 設(shè)定好 wxHTML, 並把 about_content 的那堆 HTML 碼填入去
about_html->SetPage(about_content);
}
void MyAbout::OnSize(wxCommandEvent& event)
{
// 無(wú)論如何也把 wxHTML 設(shè)定到和 Windows 一樣大, GetClientSize() 可以
// 讀取現(xiàn)在 Windows 的大小
int w, h;
this->GetClientSize(&w, &h);
about_html->SetSize(0,0,w,h);
}
void MyAbout::OnExit(wxCommandEvent& event)
{
Destroy();
}
void MySmallWin::OnAbout(wxCommandEvent& event)
{
MyAbout *about = new MyAbout(frame, "About NTools....");
about->Show(TRUE);
// CentreOnScreen 可以把 Windows 設(shè)定在 Screen 的中央
about->CentreOnScreen(wxBOTH);
}
然後打 gcc `wx-config --libs --cflags` main.cpp about.cpp -o sample 來(lái)篇釋結(jié)果如下:
總結(jié)
說(shuō)實(shí)在的,wxWindows 的功能太多~ 各位應(yīng)好好閱讀一下 Manuel 參考~ 用 wxWindows 的各種元件和提供的 Macro , Functions 應(yīng)可以滿足大部分篇程需要。筆者篇幅所限當(dāng)然不能對(duì)每個(gè)元件的功能都介紹一下(那是一本書(shū)ar! ),只能在這裹獻(xiàn)一下醜對(duì) wxWindows 2 作簡(jiǎn)單的介紹,希望對(duì)在這方兩有興趣的人給一點(diǎn)提示。有興趣的讀者可以聯(lián)絡(luò)我 ar~
Chpapa (admin@oursky.com)
原文在 LinuxHall 第九期刊載