設定開發環境
在建立應用程式前,需要設定Visual Studio開發環境和WxWidgets的相關變數,具體包括: 包含目錄:包含WxWidgets標頭檔。在項目“屬性→配置屬性→C++目錄→包含目錄”項中,添加C:\wxWidgets-3.0.2\include\msvc和C:\ wxWidgets-3.0.2\include。 庫目錄:包含WxWidgets庫檔案。在項目“屬性→配置屬性→C++目錄→庫目錄”項中,添加C:\wxWidgets-3.0.2\lib\vc_dll。 系統內容變數:設定WxWidgets動態連結程式庫檔案目錄。在Windows控制台“系統→進階系統設定→進階→環境變數→系統變數”中,在Path變數的內容中增加C:\wxWidgets-3.0.2\lib\vc_dll。 前置處理器定義:設定與Windows平台相關的WxWidgets宏,主要包括 _WXMSW_、WXUSINGDLL等。 在程式中包含程式所需的標頭檔。
其中,前置處理器定義和包含程式所需標頭檔兩個步驟可以通過建立一個標頭檔wxInc.h來集中解決,使用者代碼中只需包含該標頭檔即可。
/// wxInc.h#ifndef _WX_INC_H_#define _WX_INC_H_#define __WXMSW__ /// 使用windows平台#define WXUSINGDLL /// 使用動態連結程式庫#ifdef _DEBUG#define __WXDEBUG__ /// 使用WxWidgets調試#endif //_DEBUG#include "wx/config.h"#include "wx/wx.h" /// 使用WxWidgets通用功能和類#include "wx/aui/aui.h" /// 使用AUI#include "wx/artprov.h" /// 使用預定義的表徵圖資源#endif ///_WX_INC_H_
簡單視窗程序
最簡單的WxWidgets視窗程序需要2個類:
(1)wxApp類,代表了應用程式,負責訊息迴圈。
(2)wxFrame,代表了視窗,處理各類視窗事件。
下面建立一個簡單的視窗程序。程式運行效果如下圖所示。
簡單視窗程序建立步驟如下:
(1)從wxApp派生使用者類MyApp。
wxApp採用了類似MFC的宏定義和處理機制,通過DECLARE_APP()和IMPLEMENT_APP()兩個宏聲明和定義程式全域變數(也就是程式的進入點)。
/// MyApp.h#include "wxInc.h"class MyApp : public wxApp{public: virtual bool OnInit();};DECLARE_APP(MyApp)
如未聲明DECLARE_APP()和IMPLEMENT(),則編譯時間會報錯如下:
MSVCRTD.lib(crtexew.obj) : error LNK2019: 無法解析的外部符號 _WinMain@16,該符號在函數 ___tmainCRTStartup 中被引用
(2)重載wxApp::OnInit函數,在該函數內建立和顯示主視窗。
/// MyApp.cpp#include "MyApp.h"IMPLEMENT_APP(MyApp)bool MyApp::OnInit(){ /// 1.建立視窗 wxFrame * mainWnd = new wxFrame(NULL, wxID_ANY, wxT("main window")); /// 2.將視窗設定為程式主視窗 SetTopWindow(mainWnd); /// 3.顯示視窗。 mainWnd->Show(true); return true;}
上述程式的wxPython版本如下(簡單明了):
/// MyApp1.pyimport wxif __name__ == "__main__": app = wx.App() mainWnd = wx.Frame(None, -1, "main window") app.SetTopWindow(mainWnd) mainWnd.Show() app.MainLoop()
添加菜單、工具列和狀態列
前面程式顯示了一個“白板”視窗,沒有什麼實際的意義。作為一個應用程式,必須通過與使用者進行互動,提供使用者所需的內容。應用程式中,與使用者互動的最簡單的方式是菜單、工具列、狀態列等。
下面,我們就建立一個有菜單、工具列和狀態列的應用程式介面。程式運行效果如下圖所示。
該程式的主要介面要素包括: 2個功能表項目,每個功能表項目下又各包含2個子項 1個工具列,工具列上有3個按鈕 1個狀態列,狀態列上分3個顯示區
具體步驟如下:
(1)自訂視窗類別,在該視窗類別中建立菜單、工具列和狀態列。
/// MyFrame.h#pragma once#include "wxInc.h"class MyFrame : public wxFrame{public: MyFrame();};
/// MyFrame.cpp#include "MyFrame.h"enum { ID_ITEM_1 = wxID_HIGHEST + 1, ID_ITEM_2, ID_ITEM_3, ID_ITEM_4, ID_ITEM_5, ID_ITEM_6, ID_ITEM_7,};MyFrame::MyFrame() : wxFrame(NULL, wxID_ANY, wxT("main window")){ /// 1. 建立功能表列 wxMenuBar * menuBar = new wxMenuBar(); wxMenu * menu1 = new wxMenu(); menu1->Append(ID_ITEM_1, wxT("subitem 1-1")); menu1->Append(ID_ITEM_2, wxT("subitem 1-2")); menuBar->Append(menu1, wxT("item 1")); wxMenu * menu2 = new wxMenu(); menu2->Append(ID_ITEM_3, wxT("subitem 2-1")); menu2->Append(ID_ITEM_4, wxT("subitem 2-2")); menuBar->Append(menu2, wxT("item 2")); SetMenuBar(menuBar); /// 2. 建立工具列 wxToolBar * toolBar = new wxToolBar(this, wxID_ANY); toolBar->AddTool(ID_ITEM_5, wxT("button1"), wxArtProvider::GetBitmap(wxART_GO_BACK, wxART_TOOLBAR)); toolBar->AddTool(ID_ITEM_6, wxT("button2"), wxArtProvider::GetBitmap(wxART_TIP, wxART_TOOLBAR)); toolBar->AddSeparator(); toolBar->AddTool(ID_ITEM_7, wxT("button3"), wxArtProvider::GetBitmap(wxART_UNDO, wxART_TOOLBAR)); toolBar->Realize(); /// 在增加按鈕後必須調用此函數 SetToolBar(toolBar); /// 3. 建立狀態列 wxStatusBar * statusBar = CreateStatusBar(3); statusBar->SetStatusText(wxT("this is pane 0"), 0); statusBar->SetStatusText(wxT("this is pane 1"), 1); statusBar->SetStatusText(wxT("this is pane 2"), 2);}
(2)將該視窗類別的執行個體作為主視窗
/// MyApp.cpp#include "MyApp.h"IMPLEMENT_APP(MyApp)bool MyApp::OnInit(){ /// 1.建立視窗 wxFrame * mainWnd = new wxFrame(NULL, wxID_ANY, wxT("main window")); /// 2.將視窗設定為程式主視窗 SetTopWindow(mainWnd); /// 3.顯示視窗。 mainWnd->Show(true); return true;}
至此,代碼完畢,編譯執行後可以得到預期的介面。但是,此程式只是顯示了介面要素,並未執行訊息處理。後續還將在此基礎上添加訊息處理部分。
上述程式需注意的有以下幾點:
(1)wxToolBar的AddTool函數,在3.0.2版本後應使用建議的函數簽名,以避免編譯器警示。
(2)wxToolBar在調用完AddTool增加按鈕後,必須調用Realize()函數,以使工具列更新。
(3)可以利用wxArtProvider擷取預定義的映像資源。
上述程式的wxPython版本如下(也不複雜):
/// MyApp1.pyimport wxclass MyFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "main window") #1.增加功能表列 menuBar = wx.MenuBar() menu1 = wx.Menu() menu1.Append(-1, "subitem 1-1") menu1.Append(-1, "subitem 1-2") menuBar.Append(menu1, "item 1") menu2 = wx.Menu() menu2.Append(-1, "subitem 2-1") menu2.Append(-1, "subitem 2-2") menuBar.Append(menu2, "item 2") self.SetMenuBar(menuBar) #2.增加工具列 toolBar = wx.ToolBar(self) toolBar.AddTool(-1, wx.ArtProvider.GetBitmap(wx.ART_GO_BACK, wx.ART_TOOLBAR)) toolBar.AddTool(-1, wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_TOOLBAR)) toolBar.AddSeparator() toolBar.AddTool(-1, wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR)) toolBar.Realize() self.SetToolBar(toolBar) #3.增加狀態列 statusBar = self.CreateStatusBar(3) statusBar.SetStatusText("this is pane 0", 0) statusBar.SetStatusText("this is pane 1", 1) statusBar.SetStatusText("this is pane 2", 2)if __name__ == "__main__": app = wx.App() mainWnd = MyFrame() app.SetTopWindow(mainWnd) mainWnd.Show() app.MainLoop() #啟動訊息迴圈
添加訊息處理
wxWidgets訊息處理主要有兩種模式:
(1)靜態繫結訊息處理函數
一是類似MFC的預定義模式。通過DECLARE_EVENT_TABLE、BEGIN_EVENT_TABLE、END_EVENT_TABLE等宏,將訊息映射至指定的函數。
下面用一個例子來說明事件相關的宏的定義和使用.
/// MyFrame.hclass MyFrame : public wxFrame{ DECLARE_EVENT_TABLE()public: MyFrame();public: void OnItem1(wxCommandEvent & evt);};
/// MyFrame.cppBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(ID_ITEM_1, OnItem1)END_EVENT_TABLE()…void MyFrame::OnItem1(wxCommandEvent & evt){ wxMessageBox(wxT("item 1"));}
上面的例子中,將ID為ID_ITEM_1的菜單事件綁定至MyFrame的OnItem1函數。選擇該功能表項目後,將會調用MyFrame的OnItem1函數。
(2)動態綁定訊息處理函數
通過調用Bind函數,將訊息綁定至指定的處理函數(函數對象)。
下面用一個例子來說明Bind的使用方法:
/// MyFrame.cppMyFrame::MyFrame(...){ Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExit, this, wxID_EXIT);}
上面的例子中,將ID為wxID_EXIT的菜單事件綁定至MyFrame的OnExit函數。選擇該功能表項目後,將會調用MyFrame的OnExit函數。
下面,在前面的視窗程序基礎上,分別使用靜態和動態Binder 方法增加訊息處理,形成一個有簡單互動的程式。程式在選擇不同的功能表項目後,會彈出相應的對話方塊。運行效果如下:
下面,分別修改MyFrame.h和MyFrame.cpp程式,代碼如下:
/// MyFrame.h#pragma once#include "wxInc.h"class MyFrame : public wxFrame{ DECLARE_EVENT_TABLE()public: MyFrame();public: void OnItem1(wxCommandEvent & evt); void OnItem2(wxCommandEvent & evt);};
/// MyFrame.cpp#include "MyFrame.h"enum { ID_ITEM_1 = wxID_HIGHEST + 1, ID_ITEM_2, ID_ITEM_3, ID_ITEM_4, ID_ITEM_5, ID_ITEM_6, ID_ITEM_7,};/// 靜態繫結訊息處理BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(ID_ITEM_1, OnItem1) END_EVENT_TABLE()MyFrame::MyFrame() : wxFrame(NULL, wxID_ANY, wxT("main window")){ /// 1. 建立功能表列 wxMenuBar * menuBar = new wxMenuBar(); wxMenu * menu1 = new wxMenu(); menu1->Append(ID_ITEM_1, wxT("subitem 1-1")); menu1->Append(ID_ITEM_2, wxT("subitem 1-2")); menuBar->Append(menu1, wxT("item 1")); wxMenu * menu2 = new wxMenu(); menu2->Append(ID_ITEM_3, wxT("subitem 2-1")); menu2->Append(ID_ITEM_4, wxT("subitem 2-2")); menuBar->Append(menu2, wxT("item 2")); SetMenuBar(menuBar); /// 2. 建立工具列 wxToolBar * toolBar = new wxToolBar(this, wxID_ANY); toolBar->AddTool(ID_ITEM_5, wxT("button1"), wxArtProvider::GetBitmap(wxART_GO_BACK, wxART_TOOLBAR)); toolBar->AddTool(ID_ITEM_6, wxT("button2"), wxArtProvider::GetBitmap(wxART_TIP, wxART_TOOLBAR)); toolBar->AddSeparator(); toolBar->AddTool(ID_ITEM_7, wxT("button3"), wxArtProvider::GetBitmap(wxART_UNDO, wxART_TOOLBAR)); toolBar->Realize(); /// 在增加按鈕後必須調用此函數 SetToolBar(toolBar); /// 3. 建立狀態列 wxStatusBar * statusBar = CreateStatusBar(3); statusBar->SetStatusText(wxT("this is pane 0"), 0); statusBar->SetStatusText(wxT("this is pane 1"), 1); statusBar->SetStatusText(wxT("this is pane 2"), 2); /// 4. 動態綁定訊息 Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnItem2, this, ID_ITEM_2);}void MyFrame::OnItem1(wxCommandEvent & evt){ wxMessageBox(wxT("item 1"));}void MyFrame::OnItem2(wxCommandEvent & evt){ wxMessageBox(wxT("item 2"));}
由於Python沒有wxWidgets宏定義處理方式,所以需要通過動態綁定訊息處理,代碼如下:
/// MyFrame.pyimport wxclass MyFrame(wx.Frame): def __init__(self): ID_ITEM1 = wx.ID_HIGHEST + 1 ID_ITEM2 = wx.ID_HIGHEST + 2 wx.Frame.__init__(self, None, -1, "main window") #1.增加功能表列 menuBar = wx.MenuBar() menu1 = wx.Menu() menu1.Append(ID_ITEM1, "subitem 1-1") menu1.Append(ID_ITEM2, "subitem 1-2") menuBar.Append(menu1, "item 1") menu2 = wx.Menu() menu2.Append(-1, "subitem 2-1") menu2.Append(-1, "subitem 2-2") menuBar.Append(menu2, "item 2") self.SetMenuBar(menuBar) #2.增加工具列 toolBar = wx.ToolBar(self) toolBar.AddTool(-1, wx.ArtProvider.GetBitmap(wx.ART_GO_BACK, wx.ART_TOOLBAR)) toolBar.AddTool(-1, wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_TOOLBAR)) toolBar.AddSeparator() toolBar.AddTool(-1, wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR)) toolBar.Realize() self.SetToolBar(toolBar) #3.增加狀態列 statusBar = self.CreateStatusBar(3) statusBar.SetStatusText("this is pane 0", 0) statusBar.SetStatusText("this is pane 1", 1) statusBar.SetStatusText("this is pane 2", 2) #4.增加訊息處理 self.Bind(wx.EVT_MENU, self.OnItem1, id=ID_ITEM1) self.Bind(wx.EVT_MENU, self.OnItem2, id=ID_ITEM2) def OnItem1(self, event): wx.MessageBox("item 1") pass def OnItem2(self, event): wx.MessageBox("item 2") passif __name__ == "__main__": app = wx.App() mainWnd = MyFrame() app.SetTopWindow(mainWnd) mainWnd.Show() app.MainLoop() #啟動訊息迴圈