WxWidgets學習日記[zz]

liu_kan發表於2005-06-07
執行一個wxWidgets應用程式,需要派生一個wxApp類,並覆蓋它的wxApp::OnInit方法。
一個應用程式必須有一個頂層wxFrame或一個wxDialog窗體,任一個frame可能包含一個或多個如wxPanel和wxSplitterWindow的例項,也可能是其他的窗體或控制元件。
一個frame可以包含一個wxMenuBar、一個wxToolBar,一個狀態條、一個wxIcon(如果說這個frame有圖示的話)。
wxDialog的例項同樣可以被用來作控制元件,用它的優點是它不需要額外建一個frame。
你也可以不用建立一個dialog並組裝它的各個元件,因為你可以使用一些已經便利的dialog類,如wxMessageDialog、wxFileDialog。
你不能直接在窗體上draw,而是使用一個裝置上下文(device context DC),wxDC是wxClientDC、wxPaintDC、wxMemoryDC、wxPostScriptDC、wxMemoryDC、wxMetafileDC和wxPrinterDC的基類。如果你的繪製函式中用到了wxDC作為引數的話,也就意味著你可以傳遞任何的DC類,也就是說你可以用同樣的程式碼來繪製不同的裝置。你可以用到wxDC的一些成員函式來進行繪製,比如wxDC::DrawLine和wxDC::DrawText。窗體上控制元件的有顏色(wxColour)、brush(wxBrush)和pen(wxPen)等一些常用到的屬性。
要攔截事件,你可以新增一個DECLARE_EVENT_TABLE的巨集到你的窗體類定義中,並要新增一個BEGIN_EVENT_TABLE ... END_EVENT_TABLE塊到你的實現程式碼中去。在這些巨集中,你可以新增事件巨集來對映事件(比如單擊滑鼠)到成員函式。這樣做將覆蓋掉預先定義好的事件處理如wxKeyEvent和wxMouseEvent。
大部分modern的應用會有一個線上、超文字幫助系統,為此,你需要用到wxHelp及wxHelpController類來控制wxHelp。
GUI應用並非都是圖形化wizardry,List以及hash table以wxList、wxStringList和wxHashMap的形式給出。當然,你需要一些與平臺無關的檔案處理函式,你會發現使用wxPathList來儲存和查詢一組path非常方便。
wxWidgets也提供了一個Hello World示例,並作了一定的說明,這裡我進行一下簡要的翻譯。
首先,你要確認包含了wxWidgets的標頭檔案。這可以是基礎檔案"wx/window.h",或者是全域性包含"wx/wx.h",它對一些支援預編譯頭的編譯器(在Windows平臺上多數是這樣)同樣有效。

//
// file name: hworld.cpp
//
// purpose: wxWidgets "Hello world"
//

// 針對一些支援預編譯頭的編譯器,includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

實際上,任何一個應用都應該定義一個wxApp的派生類,並重寫它的OnInit()方法使得程式能夠被初始化。

class MyApp: public wxApp
{
virtual bool OnInit();
};

主窗體的建立是通過派生wxFrame類並在構造器中為它新增一個選單和一個狀態條。同樣,任何一個希望對"event"作出響應的類都應該定義一個事件表,使用下面的巨集。最後,要定義好這些事件的處理"handlers"使其起作用。在這個例子中,我們處理兩選單項"Quit"和"About"(顯示一個"about"窗體)。這些處理事件不能是虛擬函式。

class MyFrame: public wxFrame
{
public:
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);

void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);

private:
DECLARE_EVENT_TABLE() //注意,後面沒有分號哦!!
};
對每一個選單項,給它們定義唯一的ID。這裡使用一個enum
enum
{
ID_Quit = 1,
ID_About,
};

然後定義一個事件表來指定類wxFrame的事件處理者。當然已經有一些預先定義的巨集來指明所有基本事件包括了從選擇列表框到resize事件。如果給的ID為-1,那麼處理事件會對任何事件作出反應,所以你只需要新增一個條目到事件表來應付所有的選單事件或按鈕事件。事件的源在事件處理器中仍然是可以區分的,就像處理器中的引數是對wxEvent的一個引用,它包含了大量事件相關的資訊(如發出事件的類的的ID以及指向它的指標)。

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(ID_Quit, MyFrame::OnQuit)
EVT_MENU(ID_About, MyFrame::OnAbout)
END_EVENT_TABLE()

如同在所有程式中一樣,必須有一個"main"函式。在wxWidgets中,main的是通過一個巨集實現的,它建立一個應用例項並啟動程式。
IMPLEMENT_APP(MyApp)
正如前面提及的wxApp::OnInit()在啟動的時候呼叫,用來初始化一個程式,你可能用它來顯示一個"splash screen"並建立一個主窗體。frame可以獲取一個標題("hello world")以及位置和起始大小。一個frame可以申明為top窗體。初始化成功返回true。

bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame( "Hello World", wxPoint(50,50), wxSize(450,340) );
frame->Show( true );
SetTopWindow( frame );
return true;
}

在主窗體的構造器中,我們建立一個選單,它有兩個選單項,同樣建立一個狀態條。新增在frame中。新增選單和狀態條呼叫的是不同的方法哦!

MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, -1, title, pos, size)
{
wxMenu *menuFile = new wxMenu;

menuFile->Append( ID_About, "&About..." );
menuFile->AppendSeparator();
menuFile->Append( ID_Quit, "E&xit" );

wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append( menuFile, "&File" );

SetMenuBar( menuBar );

CreateStatusBar();
SetStatusText( "Welcome to wxWidgets!" );
}

實際事件處理器已經申明過。MyFrame::OnQuit()用來關閉主窗體,呼叫Close()。引數true來指明其它窗體無權提示是否真正要關閉。如果無其他主窗體存在,應用程式將退出。

void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close( true );
}

MyFrame::OnAbout()用來顯示一個有一些文字的小窗體。

void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox( "This is a wxWidgets' Hello world sample",
"About Hello World", wxOK &line; wxICON_INFORMATION );
//像不像MessageBox?
}
wxWidgets應用程式沒有一個main,取而代之的是wxApp的派生類所定義的OnInit()函式。
與早些版本的wxWidgets不同,OnInit不再返回一個frame,而是返回一個bool值,說明處理是否可以繼續。
你也可以呼叫wxApp::SetTopWindow來讓wxWidgets知道頂層窗體。
注意,程式的command引數,也即以前的argc和argv在wxApp成員函式中仍然是有效的。
程式的結束要消毀所有的窗體。因為如此,儘可能地用父frame來建立新frame,這樣在delete頂層frame的時候會自動delete子frame。當然,你也可以在頂層frame的wxCloseEvent事件中手動地delete各個子frame。
在有的時候,可以呼叫wxExit函式來kill程式。
舉個例子,定義一個應用程式:

class DerivedApp : public wxApp
{
public:
virtual bool OnInit();
};

IMPLEMENT_APP(DerivedApp)

bool DerivedApp::OnInit()
{
wxFrame *the_frame = new wxFrame(NULL, ID_MYFRAME, argv[0]);
...
the_frame->Show(true);
SetTopWindow(the_frame);

return true;
}

注意IMPLEMENT_APP(appClass)的用法,它使得wxWidgets在初始化的時候在合適的地方動態地建立一個應用程式的例項。以前的版本使用的方法依賴於一個全域性應用程式物件,現在不推薦這樣做了,因為需要一個全域性的初始化過程可能使得它並不在應用程式構建的時候發生。你也可以在標頭檔案中使用DECLARE_APP(appClass)來申明wxGetApp函式,它會返回一個應用程式物件的引用。否則你只能用全域性的wxTheApp指標(一個wxApp型的指標)。

應用程式的關閉
當所有的頂層(top level)窗體都被關閉的時候,應用程式會正常結束。正常情況下是這樣的,也就是說如果你只有一個頂層窗體那麼呼叫一個Close()來處理選單的"Exit"是足夠的。如果這並不能令人滿意,也可以呼叫wxApp::SetExitOnFrameDelete來改變它。注意,從wxWidgets 2.3.3開始,在應用程式進入main loop之前這一過程是不會起作用的,換句話說,你可以非常安全地在wxApp::OnInit中顯示一個對話方塊而不必擔心這個對話方塊的關閉而導致應用程式的結束,儘管這個對話方塊當時是唯一的頂層窗體。
應用程式結束的另一個要注意的就是OnExit,它在應用程式結束時且在wxWidgets清理其內部結束之前呼叫。你必須在OnExit結束之前清除所有你所建立的wxWidgets物件。特別不要在類的析構器中清理它們。
舉例,下面的程式碼會出問題:

class MyApp : public wxApp
{
public:
wxCHMHelpController m_helpCtrl;
...
};

原因在於m_helpCtrl是一個成員物件,這樣它只有在類的析構器中清理。但MyApp物件只有在wxCHMHelpController所依賴的wxWidgets結構清理之後刪除。解決的辦法是在OnExit中清理HelpCtrl。

class MyApp : public wxApp
{
public:
wxCHMHelpController *m_helpCtrl;
...
};

bool MyApp::OnInit()
{
...
m_helpCtrl = new wxCHMHelpController;
...
}

int MyApp::OnExit()
{
delete m_helpCtrl;
return 0;
}
出自 http://www.amaotong.com/li...
另外可以關注一下 http://wxwidgets.cn/