天天看點

在VC6中建立wxWidgets項目[附圖]在VC6中建立wxWidgets項目關于wxWidgets建立項目設定項目屬性設定wxWidgets目錄建立wxWidgets預編譯頭檔案建立wxApp子類建立wxFrame子類添加子控件處理事件更進一步聯系作者參考資料

在VC6中建立wxWidgets項目

原創:liigo

 

前言:wxWidgets 是跨平台的GUI庫,用VC6會影響它的跨平台性嗎?當然不會,我們隻是用VC6充當編譯器和編輯器,隻要編寫代碼時注意不使用Windows相關的特性,寫出的代碼仍然是跨平台的,仍然是可以在其它作業系統下(如Linux)使用其它C++編譯器(如GCC)編譯并運作的。

為什麼用VC6,而不是其它?我并沒有說一定要用VC6,或隻能用VC6,隻不過此文專門針對VC6而已。過一段時間我可能會寫一篇在Code::Blocks中使用wxWidgets的文章(可惜在Code::Blocks中建立wxWidgets工程太容易了,還有必要介紹嗎?我隻感覺到在linux下編譯Code::Blocks本身的源代碼有一些困難)。

 

關于wxWidgets

完整地介紹 wxWidgets 或全部列出其所有功能,是很困難的事情。我也不打算這麼做。我隻列一下我目前所知道的、能想起來的且比較在意的一些 wxWidgets 特性。

  • C++開發的跨平台的GUI庫,開放源代碼(類LGPL,允許在商業程式中使用)
  • 有十多年的曆史,目前已經相當成熟、穩定
  • 支援多個平台(Windows/Windows CE,Linux,Unix/GTK+,Unix/Motif and X11,Mac OS,MGL,OS/2,Palm等)
  • 如果有可能,它總是采用作業系統本地界面(這一點與 Eclipse SWT 類似,當然SWT是java的)
  • 有衆多高品質的第三方元件/庫
  • 可以在多種程式設計語言中使用wxWidgets,如C/C++,Java,Python,Perl,Basic,JavaScript,Lua,Euphoria,Squeak等
  • 除了GUI部分,還涉及檔案系統,多線程,網絡,多媒體,資料庫通路等多個領域
  • 支援UNICODE,支援國際化(Internationalization)程式設計
  • 提供日志,調試,配置等實用功能
  • ……

 

建立項目

點選菜單:File -> New... 建立一個“Win32 Application” Project,項目名稱為“wxProject”,點選OK按鈕,

在下一步的提示中選擇“An Empty Project”,點選Finish按鈕完成項目的建立。

以下的設定和操作可能有一些繁瑣,但這是一勞永逸的事情。隻要你完成了第一個空白工程,以後再需要建立工程時複制一份就可以了。

 

設定項目屬性

以下四個編譯配置并不要求都必須設定好,如果您不打算使用Unicode,那麼不用設定“Win32 Unicode Debug”和“Win32 Unicode Release”,如果您僅僅想調試程式而非釋出,則隻需設定相應的“Debug”不用設定“Release”。最簡單的情況下,隻需設定“Win32 Debug”。

還有一點要注意,您需要事先編譯出相應版本的 wxWidgets 庫檔案。如“Win32 Unicode Debug”需要 Unicode+Debug 版本的 wxWidgets 庫。(wxWidgets 各種版本庫均可通過 <wx安裝目錄>/build/msw/wx.dsw 進行編譯)。

點選菜單:Project -> Settings... 打開項目屬性設定對話框。

Win32 Debug:

C/C++ General:

Preprocessor definitions: WIN32,_DEBUG,__WXMSW__,__WXDEBUG__,_MBCS,_WINDOWS,NOPCH

C/C++ Code Generation:

Use run-time library: Debug Multithreaded DLL

Link General:

Object/library modules: wxmsw26d_xrc.lib wxmsw26d_html.lib wxmsw26d_adv.lib wxmsw26d_core.lib wxbase26d_xml.lib wxbase26d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

Win32 Release:

C/C++ General:

Preprocessor definitions: WIN32,NDEBUG,__WXMSW__,_MBCS,_WINDOWS,NOPCH

C/C++ Code Generation:

Use run-time library: Multithreaded DLL

Link General:

Object/library modules: wxmsw26_xrc.lib wxmsw26_html.lib wxmsw26_adv.lib wxmsw26_core.lib wxbase26_xml.lib wxbase26.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

進行以下操作之前,請先通過菜單 Build -> Configurations... 增加兩個編譯配置“Win32 Unicode Debug”和“Win32 Unicode Release”(分别複制于“Win32 Debug”和“Win32 Release”)。

Win32 Unicode Debug:

C/C++ General:

Preprocessor definitions: WIN32,_DEBUG,__WXMSW__,__WXDEBUG__,_UNICODE,_WINDOWS,NOPCH

C/C++ Code Generation:

Use run-time library: Debug Multithreaded DLL

Link General:

Object/library modules: wxmsw26ud_xrc.lib wxmsw26ud_html.lib wxmsw26ud_adv.lib wxmsw26ud_core.lib wxbase26ud_xml.lib wxbase26ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

Win32 Unicode Release:

C/C++ General:

Preprocessor definitions: WIN32,NDEBUG,__WXMSW__,_UNICODE,_WINDOWS,NOPCH

C/C++ Code Generation:

Use run-time library: Multithreaded DLL

Link General:

Object/library modules: wxmsw26u_xrc.lib wxmsw26u_html.lib wxmsw26u_adv.lib wxmsw26u_core.lib wxbase26u_xml.lib wxbase26u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib 

 

設定wxWidgets目錄

在前面的設定中,指定了wxWidgets的庫檔案(*.lib),但VC可能并不知道到哪個目錄去尋找這些檔案。同時,我們的源代碼中也要包含(include)wxWidgets的頭檔案,其頭檔案所在目錄也需要指定。另外,為了更好的調試wx程式,最好把wxWidgets的源代碼所在目錄也設定好。

點選菜單 Tools -> Options...,進入 Directories 頁,分别加入以下路徑(下面的<wx>表示wxWidgets安裝目錄)

Include files:

<wx>/include

<wx>/include/msvc

Library files:
<wx>/lib/vc_lib
Source files:
<wx>/src

這一設定是針對VC全局的,以後再用VC建立wxWigets程式,就不用設定這些路徑了。

 

建立wxWidgets預編譯頭檔案

各個編譯器不同,有的支援預編譯頭檔案,有的不支援,支援預編譯頭檔案的,使用的文法也有所不同,如果在每個源檔案中都重複的寫未免不爽,還是集中到一個頭檔案中來比較好。但是注意,有了此檔案并不決定或限制你使用還是不使用預編譯頭檔案,用不用以及怎麼用還是在你。

點選菜單 File -> New...,建立一個C/C++頭檔案 wx_pch.h,其内容如下:

#ifndef WX_PCH_H_INCLUDED

#define WX_PCH_H_INCLUDED

#if ( defined(USE_PCH) && !defined(WX_PRECOMP) )

#define WX_PRECOMP

#endif // USE_PCH

// basic wxWidgets headers

#include <wx/wxprec.h>

// for use xrc files

#include <wx/xrc/xmlres.h>

#ifdef __BORLANDC__

#pragma hdrstop

#endif

#ifndef WX_PRECOMP

#include <wx/wx.h>

#endif

#ifdef USE_PCH

// put here all your rarely-changing header files

#endif // USE_PCH

#endif // WX_PCH_H_INCLUDED

wxWidgets官方文檔是大概也是這樣推薦,Code::Blocks中基本上就是這樣子,我隻是簡單的增加了一行“#include <wx/xrc/xmlres.h>”(為了使用XRC檔案)。

以後,工程中的源檔案,隻要包含(include) wx_pch.h 檔案就可以了。

 

建立wxApp子類

點選菜單 Insert -> New Class...,建立一個名稱為“App”的類(類名稱可以随意),考慮到代碼的跨平台性,建議将其所在檔案的名稱修改為全部使用小寫字母(如 app.h/app.cpp)。此操作将生成檔案 app.h 和 app.cpp。

VC在這裡生成的類代碼顯然是不滿足我們的要求的,需要進行以下修改:

app.h

增加預編譯頭檔案 wx_pch.h 的包含(以後建立的每個.h檔案都要包含它):#include "wx_pch.h"

指定App類的父類為wxApp:即将“class App”修改為“class App : public wxApp”

為類增加虛方法OnInit()的聲明:virtual bool OnInit();

在類聲明的下方增加 wxWidgets App 聲明:DECLARE_APP(App)

最終 app.h 的内容如下(其中經過手工改寫的地方已用黃色背景突出顯示):

// by: liigo.com

#if !defined(AFX_APP_H__B4514AF3_2125_487B_BD66_AF638A80E73A__INCLUDED_)

#define AFX_APP_H__B4514AF3_2125_487B_BD66_AF638A80E73A__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#include "wx_pch.h"

class App : public wxApp

{

public:

    App();

    virtual ~App();

    virtual bool OnInit();

};

DECLARE_APP(App)

#endif // !defined(AFX_APP_H__B4514AF3_2125_487B_BD66_AF638A80E73A__INCLUDED_)

app.cpp

增加頭檔案包含(此頭檔案将在下面建立MainFrame類時建立):#include "mainframe.h"

增加 OnInit() 方法的定義(其中用到的MainFrame類定義于mainframe.h,見後文):

bool App::OnInit()

{

    MainFrame* mainFrame = new MainFrame(NULL, _("MainFrame by liigo.com"));

    mainFrame->Show();

    SetTopWindow(mainFrame);

    return true;

}

在類定義的上方增加 wxWidgets App 定義:IMPLEMENT_APP(App)

最終 app.cpp 的内容如下(其中經過手工改寫的地方已用黃色背景突出顯示):

#include "app.h"

IMPLEMENT_APP(App)

App::App()

{

}

App::~App()

{

}

bool App::OnInit()

{

    MainFrame* mainFrame = new MainFrame(NULL, _("MainFrame by liigo.com"));

    mainFrame->Show();

    SetTopWindow(mainFrame);

    return true;

}

 

建立wxFrame子類

點選菜單 Insert -> New Class...,建立一個名稱為“MainFrame”的類(類名稱可以随意),考慮到代碼的跨平台性,建議将其所在檔案的名稱修改為全部使用小寫字母(如 mainframe.h/mainframe.cpp)。此操作将生成檔案 mainframe.h 和 mainframe.cpp。

下面對VC生成的類代碼進行相應的修改:

mainframe.h

增加預編譯頭檔案的包含:#include "wx_pch.h"

指定MainFrane類的父類為wxFrame:class MainFrame : public wxFrame

修改構造函數的聲明:MainFrame(wxWindow* parent, const wxString& title);

在類定義的末尾增加事件表聲明:DECLARE_EVENT_TABLE()

最終 mainframe.h 的内容如下(其中經過手工改寫的地方已用黃色背景突出顯示):

#if !defined(AFX_MAINFRAME_H__1BC90331_B69E_40F2_BDF7_197550D70F07__INCLUDED_)

#define AFX_MAINFRAME_H__1BC90331_B69E_40F2_BDF7_197550D70F07__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#include "wx_pch.h"

class MainFrame : public wxFrame

{

public:

    MainFrame(wxWindow* parent, const wxString& title);

    virtual ~MainFrame();

    DECLARE_EVENT_TABLE()

};

#endif // !defined(AFX_MAINFRAME_H__1BC90331_B69E_40F2_BDF7_197550D70F07__INCLUDED_)

mainframe.cpp

修改構造函數的定義:

MainFrame::MainFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title)

{

    //wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, _("some text"));

}

增加事件表定義(BEGIN_EVENT_TABLE 與 END_EVENT_TABLE 之間保留白白,留待以後綁定事件):

BEGIN_EVENT_TABLE(MainFrame, wxFrame)

END_EVENT_TABLE()

最終 mainframe.cpp 的内容如下(其中經過手工改寫的地方已用黃色背景突出顯示):

#include "mainframe.h"

BEGIN_EVENT_TABLE(MainFrame, wxFrame)

END_EVENT_TABLE()

MainFrame::MainFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title)

{

    //wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, _("some text"));

}

MainFrame::~MainFrame()

{

}

 

至此,一個wxWidget的空白Project已經建立完畢,下圖是其執行結果:

在VC6中建立wxWidgets項目[附圖]在VC6中建立wxWidgets項目關于wxWidgets建立項目設定項目屬性設定wxWidgets目錄建立wxWidgets預編譯頭檔案建立wxApp子類建立wxFrame子類添加子控件處理事件更進一步聯系作者參考資料

編譯生成的 exe 檔案的大小:

可執行檔案大小 Debug Release
Unicode 3.78M 956K
非Unicode 3.60M 932K

此資料全部是靜态連結wxWidgets的結果。動态連結的話,EXE的大小沒有意義——别忘了wxWidgets的版DLLs的大小總共約4到5M(Release版)。

添加子控件

向 wxFrame 或 wxDialog 中添加子控件是比較容易的,隻需在其子類的構造函數中 new 相應的子控件就可以了。

這是最簡單的情況:

MainFrame::MainFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title)

{

    wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, _("some text"));

}

沒錯,隻要“new”一下就搞定了,控件會自動出現在wxFrame中。這是運作結果:

在VC6中建立wxWidgets項目[附圖]在VC6中建立wxWidgets項目關于wxWidgets建立項目設定項目屬性設定wxWidgets目錄建立wxWidgets預編譯頭檔案建立wxApp子類建立wxFrame子類添加子控件處理事件更進一步聯系作者參考資料

如果界面再複雜一些,上面這種方法就行不通了,我們需要引入“Sizer”(詳見http://www.wxwidgets.org/manuals/2.6.3/wx_sizeroverview.html(Sizer一覽),此處不作深入解釋):

MainFrame::MainFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title)

{

    wxTextCtrl* textCtrl = new wxTextCtrl(this, ID_TEXTCTRL, _T("some text"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE );

    wxButton* button = new wxButton(this, ID_BUTTON, _("測試按鈕"), wxDefaultPosition, wxDefaultSize, 0 );

    wxBoxSizer* vBoxSizer = new wxBoxSizer(wxVERTICAL);

    this->SetSizer(vBoxSizer);

    vBoxSizer->Add(textCtrl, 1, wxALL|wxEXPAND, 5);

    vBoxSizer->Add(button, 0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxALIGN_BOTTOM, 5);

}

上面是多行編輯框控件,下面是按鈕控件,當視窗大小變化時,編輯框控件将在水準和垂直方向上自動擴充,而按鈕始終位于視窗底部居中。這是運作結果:

在VC6中建立wxWidgets項目[附圖]在VC6中建立wxWidgets項目關于wxWidgets建立項目設定項目屬性設定wxWidgets目錄建立wxWidgets預編譯頭檔案建立wxApp子類建立wxFrame子類添加子控件處理事件更進一步聯系作者參考資料

上述代碼中涉及的控件ID(ID_TEXTCTRL,ID_BUTTON)是我們在 mainframe.cpp 中自行定義的(定義控件ID的目的是為了下一步了事件處理):

enum CtrlID

{

    ID_TEXTCTRL, ID_BUTTON

};

采用 Sizer 機制進行界面布局有相當大的優勢。要想設計好自己的程式界面,必須對 Sizer 有比較深入的了解。

參考文檔:http://www.wxwidgets.org/manuals/2.6.3/wx_sizeroverview.html(Sizer一覽)

采用XML格式檔案(XRC檔案)定義程式界面也是不錯的方式,詳見:http://www.wxwidgets.org/manuals/2.6.3/wx_xrcoverview.html(基于XML的資源系統一覽)。

無論如何,手工進行界面布局總是很繁雜,我們需要(可視化)工具的幫助:http://www.wxwidgets.org/apps2.htm

 

處理事件

在wxWidgets中處理事件,主要有兩個步驟:編寫“事件處理函數(方法)”,填寫“事件表(EVENT_TABLE)”。

事件處理函數(方法)視事件的不同而有所不同,但也有規律:沒有傳回值,隻有一個引用型參數(且一定是wxEvent的子類),不是虛方法(virtual method)。事件處理函數(方法)的名稱沒有特殊規定,可以自行命名。

作為示例,我們來處理上圖中“測試按鈕”被按下的事件。

根據wxWidgets文檔,要處理按鈕事件,需在自己的類中添加如下事件處理函數(方法):void MainFrame::OnButtonClick(wxCommandEvent &event)

具體說來就是,在 mainframe.h 檔案中的 MainFrame 類中增加新的 OnButtonClick() 方法聲明:

private:

    void OnButtonClick(wxCommandEvent& event);

并在 mainframe.cpp 檔案中增加 OnButtonClick() 方法的定義:

void MainFrame::OnButtonClick(wxCommandEvent &event)

{

    //取編輯框中的文本并用資訊框顯示出來

    wxString text = ((wxTextCtrl*)this->FindWindow(ID_TEXTCTRL))->GetValue();

    wxMessageBox(text);

}

下面需要在 mainframe.cpp 中填寫“事件表(EVENT_TABLE)”,以便我們的“事件處理函數(方法)”能在适當的時機(即事件觸發時)被調用:

BEGIN_EVENT_TABLE(MainFrame, wxFrame)

    EVT_BUTTON(ID_BUTTON, MainFrame::OnButtonClick)

END_EVENT_TABLE()

在這個事件表中,我們使用宏 EVT_BUTTON 指定了按鈕的ID,以及“事件處理函數(方法)”。

注:上面一直講“事件處理函數(方法)”,其實是“方法(method)”不是“函數(function)”,隻是“方法”這個詞在程式設計領域和在日常生活中可以有不同的了解(“方法”也可以了解為“方式”),我如果說成“事件處理方法”,難免會産生歧義。當然,“事件處理函數(方法)”似乎也并不十分合适,應稱為“事件處理‘方法’”或“事件處理方法(method)”?再深究下去就有咬文嚼字的嫌疑了,聰明的讀者早已明白我的意思了吧?

現在“測試按鈕”已經可以響應滑鼠單擊事件了。下面兩圖分别是我們的程式在Windows和紅旗Linux(4)下的運作結果:

在VC6中建立wxWidgets項目[附圖]在VC6中建立wxWidgets項目關于wxWidgets建立項目設定項目屬性設定wxWidgets目錄建立wxWidgets預編譯頭檔案建立wxApp子類建立wxFrame子類添加子控件處理事件更進一步聯系作者參考資料
在VC6中建立wxWidgets項目[附圖]在VC6中建立wxWidgets項目關于wxWidgets建立項目設定項目屬性設定wxWidgets目錄建立wxWidgets預編譯頭檔案建立wxApp子類建立wxFrame子類添加子控件處理事件更進一步聯系作者參考資料

 

如何處理其它事件?

說白了,關鍵要知道兩點:事件處理函數(方法)的參數是什麼類型,填寫參數表時用哪一個宏(EVT_*)。

再補充一點:要知道“什麼控件”在“什麼時機”會觸發“什麼事件”。

要知道這些,就需要對wxWidgets的事件處理有一個比較全面的了解。

建議看一下wxWidgets官方文檔中的這篇文章:http://www.wxwidgets.org/manuals/2.6.3/wx_eventhandlingoverview.html(事件處理一覽)

尤其是其中的 Event macros summary(事件宏概要)一段。

電子書《Cross-Platform GUI Programming with wxWidgets》附錄9(Appendix I, 617頁)中對事件處理時所涉及的事件類型(wxXXXEvent)和事件宏(EVT_*)有比較好的總結,建議看一下,最好列印出來放在手邊,以便随時參考。

本文所涉及的完整源代碼可在此下載下傳:http://liigo.diy.myrice.com/article/wxProject/wxProject.zip

更進一步

了解 Sizer,熟悉界面設計:http://www.wxwidgets.org/manuals/2.6.3/wx_sizeroverview.html

了解 事件處理:http://www.wxwidgets.org/manuals/2.6.3/wx_eventhandlingoverview.html

了解 wxWidgets 提供了哪些控件,它們各自的屬性、方法、事件,以及它們的用法。

去 wxWidgets.org 上找第三方的控件/庫:http://www.wxwidgets.org/contrib2.htm#classes

去 wxWiki 上找第三方的控件/庫:http://www.wxwidgets.org/wiki/index.php/Table_Of_Contents#Pages_about_classes.2C_functions_or_macros

GUI庫嘛?無非就是控件(component)的使用:布局、操作、事件處理。

 

聯系作者

[email protected]

www.liigo.com

http://blog.csdn.net/liigo/

QQ: 175199125

參考資料

http://www.wxwidgets.org/

http://www.wxwidgets.org/wiki/index.php/Main_Page