扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
为什么用VC6,而不是其它?我并没有说一定要用VC6,或只能用VC6,只不过此文专门针对VC6而已。过一段时间我可能会写一篇在Code::Blocks中使用wxWidgets的文章(可惜在Code::Blocks中创建wxWidgets工程太容易了,还有必要介绍吗?我只感觉到在linux下编译Code::Blocks本身的源代码有一些困难)。
完整地介绍 wxWidgets 或全部列出其所有功能,是很困难的事情。我也不打算这么做。我只列一下我目前所知道的、能想起来的且比较在意的一些 wxWidgets 特性。
点击菜单: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... 打开项目属性设置对话框。
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
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”)。
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
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的库文件(*.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程序,就不用设置这些路径了。
各个编译器不同,有的支持预编译头文件,有的不支持,支持预编译头文件的,使用的语法也有所不同,如果在每个源文件中都重复的写未免不爽,还是集中到一个头文件中来比较好。但是注意,有了此文件并不决定或限制你使用还是不使用预编译头文件,用不用以及怎么用还是在你。
点击菜单 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 文件就可以了。
点击菜单 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;
}
点击菜单 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已经创建完毕,下图是其执行结果:
编译生成的 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中。这是运行结果:
如果界面再复杂一些,上面这种方法就行不通了,我们需要引入“Sizer”(详见ttp://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); } |
上面是多行编辑框控件,下面是按钮控件,当窗口大小变化时,编辑框控件将在水平和垂直方向上自动扩展,而按钮始终位于窗口底部居中。这是运行结果:
上述代码中涉及的控件ID(ID_TEXTCTRL,ID_BUTTON)是我们在 mainframe.cpp 中自行定义的(定义控件ID的目的是为了下一步了事件处理):
enum CtrlID { ID_TEXTCTRL, ID_BUTTON }; |
采用 Sizer 机制进行界面布局有相当大的优势。要想设计好自己的程序界面,必须对 Sizer 有比较深入的了解。
参考文档:ttp://www.wxwidgets.org/manuals/2.6.3/wx_sizeroverview.html(Sizer一览)
采用XML格式文件(XRC文件)定义程序界面也是不错的方式,详见:ttp://www.wxwidgets.org/manuals/2.6.3/wx_xrcoverview.html(基于XML的资源系统一览)。
无论如何,手工进行界面布局总是很繁杂,我们需要(可视化)工具的帮助:ttp://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)下的运行结果:
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者