這篇文檔有意思的地方是它關注了wx的一些細節,以及一些有用但在入門的文檔裡很少見到詳述的類或wx特性。排版整理完畢。
皿
wxWindows 庫,無論是否作為動態連結程式庫 (DLL) 來編譯它,都有可能有非常小的執行體。它還提供了用於多平台開發的各種特性:可以獲得 OpenGL 介面以及對 HTML、Unicode 和國際化的內建支援。它可以協助您將應用程式從僅用於 Windows 的 MFC(Microsoft Foundation Classes)移植到其它平台,比方說,Linux。wxWindows 的一個主要目的是在儘可能多的平台上運行,這樣才能支援幾乎每種可用的 C++ 編譯器。它還沒有使用標準 C++ 的全部特性(例如名稱空間、std::string 類和 STL 容器)。但標準 C++ 已被列在議程中,並且已經有了一些對於新類型轉換文法和 std::string 的支援。
簡要曆史
Julian Smart 於 1992 年在愛丁堡大學 (University of Edinburgh) 人工智慧應用學院開始了 wxWindows 的研究。1995 年,Markus Holzem 發布了其到 Xt(X 工具箱)的 wxWindows 移植。停了一段時間後,1997 年 5 月,Windows 和 GTK+ 移植被合并,並放入 CVS 資產庫,所有對 wxWindows 做出貢獻的人都可以使用它。到 97 年底,Julian Smart 開始分發 wxWindows 的 CD-ROM,包括完整的源碼、編譯器材料等等。
目前 wxWindows 在 GPL 下發布,但有一個例外:可以分發不帶有原始碼的二進位可執行檔。這對於商業項目來說是個不錯的選擇。它有各種 UNIX 版 和 Microsoft Windows 版,以及 Macintosh OS 版。到 OS/2 和其它作業系統的移植也正在開發中。現在,讓我們開始進入具有豐富內容的本文...
要運用的平台
因為 wxWindows 隨時可以使用本機控制項,所以它採取與大多數其它多平台 GUI 庫不同的方式來對待小視窗組件。可以仿效那些停用控制項,例如 Unix 下的樹控制項。這將為應用程式的使用者提供類似的外觀和感覺。wxWindows 庫當前支援以下平台:
- Windows 3.1,Windows 95/98,Windows NT
- 帶有 Motif/Lesstif 的大多數 UNIX 版本
- 帶有 GTK+ 的大多數 UNIX 版本
- Mac
僅帶非 GUI 類的 wxBase 庫也可以在 UNIX/Win32 和 BeOS 下構建(有一些限制)。即使您不是將 wxWindows 作為 DLL 編譯,也可以獲得非常小的可執行檔。例如,使用 Windows 平台的 Microsoft Visual C++ 所編譯的最小的樣本應用程式小於 400 KB。因為 wxWindows 的可執行程式很小,所以通常可以避免所謂的“DLL 災難”。
現在,讓我們看看多平台特性...
獨立於體繫結構的類型
為避免體繫結構的依賴性,該庫提供了各種獨立於體繫結構的類型和宏,能夠根據應用程式的尾數來處理位交換。它們包括:
- wxInt32(32 位有符號整數)
- wxInt16(16 位有符號整數)
- wxInt8(8 位有符號整數)
- wxUint32(32 位不帶正負號的整數)
- wxUint16 = wxWord(16 位不帶正負號的整數)
- wxUint8 = wxByte(8 位不帶正負號的整數)
位交換宏可用於整數和不帶正負號的整數(其中的 xx 代表 16 或 32,BE 代表大尾數法,LE 代表小尾數法。)
- wxINTxx_SWAP_ON_BE()
- wxUINTxx_SWAP_ON_BE()
- wxINTxx_SWAP_ON_LE()
- wxUINTxx_SWAP_ON_LE()
- wxINTxx_SWAP_ALWAYS()
- wxUINTxx_SWAP_ALWAYS()
這裡的用法很直接,如下例所示:
32 位帶正負號的整數變數的位元組交換
wxInt32 old_var = 0xF1F2F3F4; wxInt32 new_var = wxINT32_SWAP_ALWAYS( old_var ); |
除了這些宏以外,wxWindows 還提供了 #define 來定義機器(庫在其上編譯)的當前尾數。以下是它的一例:
使用 #define
if ( wxBYTE_ORDER == wxLITTLE_ENDIAN ) { // Do stuff for little endian machine... } else { // Do stuff for big endian machine... } |
檔案處理
要為具有不同檔案儲存體器概念的不同平台進行編寫總是很困難的。要克服這一問題,wxWindows 有一些函數用於多平台檔案處理。首先,讓我們看一些用於基本檔案操作(例如複製、刪除和重新命名)的函數。
基本檔案操作
wxString old_report = "smithers_00.doc"; wxString new_report = "my_smithers.doc"; if ( wxCopyFile( old_report, "smithers_00.bak" ) == true ) { if ( wxRemoveFile( old_report ) == true ) { if ( wxRenameFile( new_report, old_report ) == false ) { // Doh! } } } |
另一個為不同平台進行編寫所帶來的嚴重問題是目錄分隔字元,但使用 wxPathList 類的話就可以完全避免這一問題。 wxPathList 包含了搜尋檔案所用的目錄列表。如果希望尋找某一檔案,只需要將檔案名稱傳遞給 wxPathList 類,它就會搜尋預先定義的目錄。
wxPathList 類
wxPathList path_list; // Add current working directory path_list.Add( "." );
// Add one directory above current working directory path_list.Add( ".." ); // Add directories from environment variable PATH to the list path_list.AddEnvList( "PATH" ); wxString path = path_list.FindValidPath( "homer.bmp" ); |
wxWindows 中還有兩個有用的函數: wxFileNameFromPath() ,用於將檔案名稱從完整路徑中剝離,以及 wxPathOnly() ,用於將路徑從完整路徑中剝離。
HTML
Vaclav Slavik 的 wxHTML 庫對基本 HTML 進行文法分析併產生 HTML。它並沒有完全實現 HTML 標準,但它的功能對於處理線上說明已經足夠了,它還可以使用標記處理常式進行擴充。要顯示 HTML,需要建立一個類為 wxHtmlWindow 的對象。然後調用它的方法來設定相關的架構和相關的狀態列,前者用來實際地顯示 HTML,後者用來顯示由 HTML 文法分析器所產生的訊息。
wxHTML
wxHtmlWindow html_window = new wxHtmlWindow( this ); html_window->SetRelatedFrame( this, "HTML : %%s" ); html_window->SetRelatedStatusBar( 0 ); |
然後,可以通過使用以下函數來裝入 HTML 頁面:
裝入 HTML
html_window->LoadPage( "burns.htm" ); |
也可以通過以下函數來顯示 HTML 程式碼
顯示 HTML
html_window->SetPage( "<html><body>Hello, Monty!</body></html>" ); |
映像
wxImage 類使用映像格式處理常式來裝入各種不同的影像檔格式。可以通過實現自己的映像格式處理常式,擴充 wxImage 來裝入新的映像格式。現有的映像格式處理常式使用眾所周知的庫,例如 Sam Leffler 的 libTIFF 庫或獨立 JPEG 小組的 JPEG 庫。有一些用於 BMP、PNG、JPEG、GIF、PCX、PNM 和 TIFF 的處理常式。可以通過使用應用程式啟動代碼中 wxImage 類的靜態方法 AddHandler() 來啟用每個映像格式處理常式。
wxImages
bool MyApp::OnInit() { wxImage::AddHandler( new wxPNGHandler ); // more ... } |
要使用所有現有的映像格式處理常式,只需要調用函數 wxInitAllImageHandlers() 而不是上面顯示的 AddHandler() 方法。
在其它平台上使用應用程式中的工具列位元影像時要特別小心。在 Windows 上,您將看到 Windows 位元影像格式,但 Linux 位元影像通常是 pixmaps,在這種情況下,就不能完全避免有條件的編譯。讓我們看一些樣本代碼。
#if defined(__WXGTK__) || defined(__WXMOTIF__) #include "maggie.xpm" #endif // more ... void MyApp::UseBitmap() { wxBitmap bitmap( wxBITMAP( maggie )); // more ... } |
理解了嗎?所有效果都是由 wxBITMAP() 宏實現的。對於 Windows 和 OS/2,它將使用應用程式資源中名為 'maggie' 的位元影像來建立 wxBitmap 對象。對於所有其它平台,它將使用稱為 'maggie_xpm' 的 pixmap 來建立 wxBitmap 對象。
當可以使用 wxDC::DrawBitmap() 在裝置上下文中繪製位元影像時,必須為映像操作使用 wxImage 對象,如下所示。
映像操作
wxImage* p_image = new wxImage( bitmap ); // Have some fun if ( p_image->Ok() ) { if ( p_image->GetHeight() > 50 && p_image->GetWidth() > 50 ) { unsigned char red = p_image->GetRed( 50, 50 ); unsigned char green = p_image->GetGreen( 50, 50 ); unsigned char blue = p_image->GetBlue( 50, 50 ); // Secure but might be slow p_image->SetRGB( 50, 50, red, green, blue ); // If you want fast action use a pointer... unsigned char* data = p_image->GetData(); // Manipulate the data... } } |
Unicode 和國際化 (i18n)
開發適用於國際市場的軟體時,不能指望每個人都能閱讀以英文表示的應用程式訊息。但廣泛使用的 ANSI 代碼又不能處理所有語言符號。(例如,它不能處理中文。)而同時編寫 ANSI 和 Unicode 又有些複雜,您可以從下例中領略到這一點:
ANSI 和 Unicode
#ifdef USE__UNICODE wchar_t wide_char = L'h'; wchar_t const* wide_string = L"Hello, World!"; int length = wcslen( wide_string ); #else char ansi_char = 'h'; char const* ansi_string = "Hello, World!"; int length = strlen( ansi_string ); #endif |
而使用 wxWindows Unicode 能力就只需要編寫以下代碼:
wxT() 宏以及 wxChar 和 wxString
wxChar wx_char = wxT( '*' ); wxString wx_string = wxT( "Hello, World!" ); int length = wx_string.Len(); |
wxT() 宏以及 wxChar 和 wxString 類將處理所有問題。該庫在內部對所有訊息使用這種方法。當前有捷克語、丹麥語、德語、法語、意大利語和俄語譯文可用,但您可以在 wxLocale 類的協助下編譯庫的語言版本後使用它。
調試
庫提供了各種不同的類、函數和宏來協助您調試應用程式。如果以調試方式來編譯庫,用於類 wxObject (wxWindows 中大多數類的基類)的 new 和 delete 操作符就已經重新定義,可以儲存有關在堆上分配的對象的額外資訊。使用這些資訊,可以用類 wxDebugContext 來獲得有關對象分配、記憶體泄露、覆蓋和正在寫入的詳細資料。
// Start logging for Dump() call wxDebugContext::SetCheckpoint(); wxString *thing = new wxString; wxDate* date = new wxDate; // non-object allocation char *ordinaryNonObject = new char[1000]; // more ... // Print number of object and non-object allocations wxDebugContext::Dump(); // Print allocation statistics wxDebugContext::PrintStatistics(); |
對 wxDebugContext::Dump() 的調用將彈出一個包含分配列表的視窗。您可以在下面看到這樣一個列表,這是我用 wxWindows 提供的 memcheck 樣本建立的。
調用 wxDebugContext::Dump()
13:32:45: ----- Memory dump of memcheck at Tue Dec 26 13:32:45 2000 ----- 13:32:45: ../../../samples/memcheck/memcheck.cpp(88): non-object data at $DD3DC0, size 4 13:32:45: ../../../samples/memcheck/memcheck.cpp(89): wxDate at $DD40D0, size 24 13:32:45: ../../../samples/memcheck/memcheck.cpp(92): non-object data at $DD4118, size 1000 |
對 wxDebugContext::PrintStatistics() 的調用將提供一些統計資訊,如下所示。
調用 wxDebugContext::PrintStatistics()
13:32:45: ----- Memory statistics of memcheck at Tue Dec 26 13:32:45 2000 ----- 13:32:45: 1 objects of class wxDate, total size 24 13:32:45: 5 objects of class nonobject, total size 4256 13:32:45: 13:32:45: Number of object items: 1 13:32:45: Number of non-object items: 5 13:32:45: Total allocated size: 4280 |
將 MFC 應用程式移植到 Linux
wxWindows 庫也可以將 MFC 應用程式移植到 Linux 和其它作業系統。您將在下面的代碼摘錄中看到,wxWindows 字串類 wxString 與 MFC 字串類 CString 有某些相似性。
wxString
wxString s1 = "Hello, World!"; wxString s2 = "Hello"; if ( s1.IsEmpty() == false ) { s2.Empty(); s2 = s1.Left( 5 ); int pos = s1.Find( ',' ); s2 += s1.Mid( pos, 2 ); s2 += s1.Right( 6 ); } |
wxWindows 的事件系統也與 MFC 非常相似,訊息映射表都是將事件處理常式方法映射到事件系統。在 wxWindows 中,這些稱為事件表。事件表宏與 MFC 的訊息映射只有一點差別。在下面的原始碼中顯示了主要差異。
標頭檔的 MFC 代碼
class CButtonCtrl : public COleControl { // Implementation protected: LRESULT OnOcmCommand( WPARAM wParam, LPARAM lParam ); DECLARE_MESSAGE_MAP() }; |
實現檔案的 MFC 代碼
BEGIN_MESSAGE_MAP( CButtonCtrl, COleControl ) //{{AFX_MSG_MAP( CButtonCtrl ) ON_MESSAGE( OCM_COMMAND, OnOcmCommand ) //}}AFX_MSG_MAP END_MESSAGE_MAP() |
標頭檔的 wxWindows 代碼
class MyButton : public wxButton { void OnButton( wxMouseEvent& event ) private: DECLARE_EVENT_TABLE() }; |
實現檔案的 wxWindows 代碼
BEGIN_EVENT_TABLE( MyButton, wxButton ) EVT_BUTTON( -1, MyButton::OnButton ) END_EVENT_TABLE() |
如您所見,並沒有什麼不同。在郵件清單中已經有相當一部分人成功地將他們現有的 MFC 應用程式移植到 wxWindows(我們也非常願意協助您:)。
標準 C++
wxWindows 還沒有使用標準 C++ 技術(例如 std::string 、STL 或名稱空間),因為這樣會大量減少 wxWindows 可在其上進行編譯的平台數。(只有很少一些編譯器完全支援標準 C++ 的最新特性。)但隨著標準 C++ 越來越受到普遍支援,wxWindows 開發小組會將對標準 C++ 的支援整合到它的庫中。讓我們看看他們都做了哪些工作。
以標準 C++ 編寫進行的安全向下類型轉換
class A { virtual void foo() {}; }; class B : public A {};A* p_A = new B(); B* p_B = dynamic_cast(p_A); |
如果有任何錯誤, p_B 將包含 0 指標。在 wxWindows 中,您將發現一個基於宏的系統用於運行時類型資訊,但對於上述 C++ 代碼,必須按以下方法使用 wxDynamicCast() 宏:
用於標準 C++ 的wxDynamicCast() 宏
B* p_B = wxDynamicCast( p_A, B ); |
如果類型轉換失敗, p_B 將包含 0 指標。對於支援新類型轉換文法的實現,宏擴充為 dynamic_cast<> 。但不幸的是,宏只對指標起作用,而不對引用起作用。除了 wxDynamicCast() 宏以外,還有 wxStaticCast() 和 wxConstCast() 宏。
wxWindows 字串類 wxString 提供了 90% 的標準字串類方法。這裡有些樣本:
wxWindows' wxString
wxString s1 = "Hello, World!"; wxString s2 = "Hello"; s2.erase(); for ( size_t i = 0; i < s1.length(); ++i ) s2 += s1[i]; if ( s1.compare( s2 ) == 0 ) { // Strings are equal } |
請注意,如果在這裡將 wxString typedef 為 std::string ,就可以同時為 wxWindows 和標準 C++ 編寫。
文檔
wxWindows 文檔就目前來說一點也不出色。雖然一些比較“陳舊”的類(例如 wxString )文檔很齊全,但對最近實現的類(例如 wxGrid )或更模糊的類(例如 wxGLCanvas )的描述本應該做得更好。主要文檔提供了對庫及其概念的快速介紹、按照字母順序排列的類引用、編程策略、主題概述,以及對 wxHTML 和 wxPython 的某些注釋,提供了 HTML、WinHelp、MS HTML Help 和 PDF 格式(請參閱 參考資料)。
如果您剛剛接觸 wxWindows,應該從主題概述開始。它們提供了許多有關公用主題(例如調試、事件處理、列印等等)的基本資料和程式碼範例。主要文檔還包含了一些技術說明和教程,它們提供的主題資訊範圍從有關瞭解 wxWindows 的一些非常公用的問題到特定於編譯器的問題。您還將得到大量純文字檔案,包含用於所支援平台的安裝和版本資訊。wxWindows 發行版還提供了隨文檔一起的大約 50 個樣本應用程式。
支援
如果您在使用庫時遇到問題,在文檔中又找不到答案該怎麼辦?不用擔心。通常您有兩種支援可選:使用郵件清單的免費支援,或商業支援。如果您不幸需要快速解答,可能最好使用商業支援。雖然核心開發人員總是會關注郵件清單,但他們往往很忙碌,無法立即做出響應。但您通常可以在一兩天內得到答案。(如果提的問題不太尋常或比較複雜,則在再次提出問題之前應該至少等上兩天。)可以從 Sourceforge(請參閱 參考資料 )上的 CVS 原始碼資料庫訪問最新的源碼。
總結
現在您對 wxWindows 究竟是什麼,它為多平台開發提供了哪些產品和服務應該有了一定認識。但當然,這些不是全部。拿當前的 wxStudio 項目為例,它使用 wxWindows 開發類似於 IDE 的 Microsoft Visual Studio。或者 wxCVS 項目,它將是用於 CVS 系統的多平台圖形介面。或者 wxDesigner,它是由 Robert Roebling 開發的一種 RAD 工具,用於構建 wxWindows 對話方塊。您應該會發現,wxWindows 社區正在不斷成長,因此在下一次將要開發多平台項目時請關注一下它。
所有提到的產品名都是其相應擁有者的 商標或註冊商標。
參考資料
- 您可以參閱本文在 developerWorks 全球網站上的 英文原文.
- 仔細查看 wxWindows 首頁
- 閱讀有關 Sourceforge 上的 wxWindows資訊
- wxStudio 是建立免費、跨平台整合式開發環境的研究計劃
- wxDesigner 有 Windows 和 UNIX 版可用
- wxPROs 是 wxWindows 和 wxPython 的專業人員資源群組織
關於作者
|
|
|
Markus Neifer 最初在 LOGO 龜標的協助下開始編程,在此之後他使用過各種版本的 BASIC。在研究地理資訊期間,他學習了一段時間的 C,但隨後很快轉向 C++ 和 Java,因為它們具有物件導向的性質。 他曾在 R&D 部門工作,在那期間, 他發表了有關物件導向的科學軟體開發的文章。現在,他是地理資訊系統領域中的軟體工程師。可以通過 markusneifer@my-deja.com 與他聯絡。 |