0%

WxWidgets学习笔记-2-应用程序类WxAPP和Frame窗口类学习

1.WxApp

每一个wxWidgets程序都需要定义一个wxApp类的子类,并且需要创建并且只能创建一个这个类的实例,

实例控制着整个程序的执行,这个继承自wxApp的子类至少需要定义一个OnInit函数,

当运行代码的时候,将会调用这个函数(和一个典型的Win32程序中的main函数或者WinMain函数类似)。

1.wxApp类

定义这个子类及其OnInit函数的代码如下所示:

1
2
3
4
5
class MyApp : public wxApp
{
public:
virtual bool OnInit();//相当于Main函数 函数入口
};

在这个Onlnit函数中,通常应该作的事情包括:

创建至少一个窗口实例(实例是C++中的关键词),对传入的命令行参数进行解析,为应用程序进行数据设置和其它的一些初始化的操作

如果这个函数返回真, wxWidgets将开始事件循环用来接收用户输入并且在必要的情况下处理这些输入。

如果OnInit函数返回假, wxWidgets将会释放它内部已经分配的资源,然后结束整个程序的运行。

2.OnInit函数的实现

看一个最简单的OnInit函数的实现:

1
2
3
4
5
6
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame(wxT(”Minimal wxWidgets App”)); //实例化一个Frame
frame->Show(true);//展示一个Frame
return true;
}

3.wXT宏

wxT这个宏,在接下来的例子中,这个宏还会被频繁用到。

它的作用是让你的代码兼容Unicode模式。这个宏和另外一个T宏的作用是完全一样的。

使用这个宏也不会带来运行期的性能损失。

可能还会遇到另外一个类似的”-()”标记,这个是用来告诉wxWidgets将其中的字符串翻译成指定语言的版本


4.IMPLEMENT_APP宏

那么创建MyApp的实例的代码在哪里呢?

实际上,这是在wxWidgets内部实现的,不过你仍然需要告诉wxWidgets需要创建哪一个App类的实例,所以你还需要增加下面的一个宏:

1
wxIMPLEMENT_APP(MyApp);

如果没有实现这个类, wxWidgets就不知道怎样创建一个新的应用程序对象。

这个宏除了上述的功能以外,还会检查编译应用程序使用的库文件是否和当前的库文件相匹配(前面我们已经介绍了,你可以编译多个配置文件的wxWidgets库文件),如果没有这种检查,由此而产生的一些运行期的错误将很难被定位出原因。

5.DECLARE_APP(MyAPP)宏

当wxWidgets创建这个MyApp类的实例的时候,会将创建的结果赋值给一个全局变量wxTheApp.

你当然可以在你的程序中使用这个变量,但是你可能不得不一遍又一遍的进行从wxApp到MyApp的类型强制转换。

增加下面的这一行声明以后,你就可以调用wxGetApp()函数,这个函数会返回一个到这个MyApp实例的引用

1
DECLARE_APP(MyApp)

5.一点提示

即使没有声明DECLARE-APP,你仍然可以不用进行类型强制转化就直接对wxTheApp变量调用wxApp(注意,不是MyApp而是wxApp,它是MyApp的基类的方法,这可以避免在所有的头文件中包含MyApp的头文件,这对于那些库中的代码(而不是应用程序的代码)来说也更有意义,而且还可以缩短编译的时间。

2.WxFrame

一个Frame窗口是一个可以容纳别的窗口的顶层窗口,通常拥有一个标题栏和一个菜单栏。Frame类的定义可以放在MyApp类后。

1
2
3
4
5
6
7
8
class MyFrame : public wxFrame
{
public:
MyFrame();
private:
void OnExit(wxCommandEvent& event);//退出事件
void OnAbout(wxCommandEvent& event);
};

这个窗口类有一个构造函数和两个用来把菜单命令和C++代码相连的事件处理函数

3.事件处理函数

1.时间表EVENT TABLE

1
2
3
4
BEGIN EVENT TABLE(MyFrame, wxFrame) 
EVT MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT MENU(wxID_EXIT, MyFrame::OnQuit)
END EVENT TABLE()

​ 所谓事件表,是一组位于类的实现文件(.cpp文件)中的宏,用来告诉wxWidgets来自用户或者其它地方的事件应该怎样和类的成员函数对应起来。

​ 前面展示的事件表表明,要把标识符分别为wxID_EXITwxID_ABOUT的菜单事件和MyFrame的成员函数OnAbout和OnQuit关联起来。这里的EVTMENU宏只是很多中事件宏中的一个,事件宏的作用是告诉wxWidgets哪种事件应该被关联到哪个成员函数。

​ 这里的两个标识wxID_ABOUT和wxID_EXIT是wxWidgets预定义的宏,通常应该通过枚举,常量或者宏定义的方式定义你自己的标识符,下边是一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
enum
{
Menu_File_Quit = 100,//窗口退出ID=100
Menu_File_Open,//打开ID=101
Menu_File_Save,//保存102

Menu_Render_Start,//渲染开始103
Menu_Render_Pause,//停止104
Menu_Render_Resume//重新开始105
};
//事件表 类和基类
BEGIN_EVENT_TABLE( wxraytracerFrame, wxFrame )
EVT_MENU( Menu_Render_Start, wxraytracerFrame::OnRenderStart )
EVT_MENU( Menu_Render_Pause, wxraytracerFrame::OnRenderPause )
EVT_MENU( Menu_Render_Resume, wxraytracerFrame::OnRenderResume )
EVT_MENU( Menu_File_Save, wxraytracerFrame::OnSaveFile )
EVT_MENU( Menu_File_Open, wxraytracerFrame::OnOpenFile )
EVT_MENU( Menu_File_Quit, wxraytracerFrame::OnQuit )
EVT_COMMAND(ID_RENDER_COMPLETED, wxEVT_RENDER,
wxraytracerFrame::OnRenderCompleted)
END_EVENT_TABLE()

​ 用上面的方法定义的事件表是一种静态的事件表,不可以在运行期改变。

2.OnAbout和OnQuit函数

1
2
3
4
5
6
7
8
9
10
11
void MyFrame::OnAbout(wxCommandEvent& event)
{
wxString msg;
msg.Printf(wxT(”Hello and welcome to %s”), wxVERSION STRING);
wxMessageBox(msg, wxT(”About Minimal”), wxOK | wxICON INFORMATION, this);
}
void MyFrame::OnQuit(wxCommandEvent& event)
{
Close();
}

当用户点击关于菜单项的时候, MyFrame:OnAbout函数弹出一个消息框。这用到了wxWidgets提供的API wxMessageBox,它的四个参数分别代表消息内容,标题,窗口类型以及父窗口。

3.wxMessageBox消息框

使用规范:
1
wxMessageBox(msg, wxT(”About Minimal”), wxOK | wxICON INFORMATION, this);
解释:
wxMessageBox 内容 标题 窗口类型 父窗口
msg wxT(”About Minimal”) wxOK wxICON INFORMATION this

4.OnQuit的解释

​ 当用户点击退出菜单项的时候, MyFrame:OnQuit函数被调用(你已经意识到了,这是事件表的功劳) 。它调用wxFrame类的Close函数来释放frame窗口。因为没有别的窗口存在了,所以就触发了应用程序的退出,

​ 实际上, wxFrame类的Close函数并不直接关闭frame窗口,而是产生一个wxEVTCLOSE-WINDOW事件,这个事件默认的处理函数调用wxWindow:: Destroy函数释放了frame窗口。

​ 用户还可以通过别的方法关掉应用程序,比如通过点击标题栏上的关闭按钮或者是通过系统菜单中的关闭菜单,在这种情况下, OnQuit函数是怎样被调用的呢?事实上,在这种情况, OnQuit函数并没有被调用。这时, wxWidgets会通过Close函数 (象OnQuit中的那样) ,给frame窗口发送一个wxEVTCLOSEWINDOW事件,这个事件默认的处理函数会释放掉frame窗口。

​ 在你的应用程序中,可以通过拦截这个事件来改变这种默认的行为,比如有时候,你可能想问一问你的用户是不是真的要这样做.

​ 另外,大多数的应用程序类还应该重载(重载是C++的关键词)一个OnExit函数,以便在任何时候程序退出时,执行清理和资源回收的动作。需要注意的是,这个函数只有在Onlnit函数返回真的时候才会被执行。当然,在我们这个小例子中就用不着定义这个函数了。

4.Frame构造函数

最后,让我们来看看Frame窗口的构造函数,正是它实现了frame窗口的图标,菜单栏和状态条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include ”mondrian.xpm” 
MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID ANY, title)//表1
{
SetIcon(wxIcon(mondrian xpm));

wxMenu ∗fileMenu = new wxMenu;
wxMenu ∗helpMenu = new wxMenu;
helpMenu−>Append(wxID_ABOUT, wxT(”&About...\tF1”), wxT(”Show about dialog”));
fileMenu−>Append(wxID_EXIT, wxT(”E&xit\tAlt−X”), wxT(”Quit this program”));

wxMenuBar ∗menuBar = new wxMenuBar();
menuBar−>Append(fileMenu, wxT(”&File”));
menuBar−>Append(helpMenu, wxT(”&Help”));
SetMenuBar(menuBar);
CreateStatusBar(2);
SetStatusText(wxT(”Welcome to wxWidgets!”));
}

1.MyFrame构造函数

1
2
MyFrame::MyFrame(const wxString& title) 
: wxFrame(NULL, wxID ANY, title)

​ 这个构造函数首先调用它的基类(wxFrame)的构造函数,使用的参数是父窗口(还没有父窗口,所以用NULL),窗口标识(wxIDANY标识让wxWidgets自己选择一个)和标题。这个基类的构造函数才真正创建了一个窗口的实例。

除了这样的调用方法,还有另外一种方法是直接在构造函数里面显式调用基类默认的构造函数(就是指不带任何参数的构造函数),然后调用wxFrame:Create函数来创建一个frame窗口的实例。

2.SetIcon() 图标设置

1
SetIcon(wxIcon(mondrian xpm)); 

小图片或者是图标在所有的平台上都可以用XPM格式来表示。XPM文件其实是一个ASCI编码的完全符合C++语法的文本文件,所以可以直接用C++的方式包含到代码中,然后将这个图标和frame窗口关联。

5.WxMenu菜单栏和wxMenuBar菜单条

1
2
3
4
5
6
7
8
9
10
11
wxMenu ∗fileMenu = new wxMenu; 
wxMenu ∗helpMenu = new wxMenu;
helpMenu−>Append(wxID_ABOUT, wxT(”&About...\tF1”), wxT(”Show about dialog”));
fileMenu−>Append(wxID_EXIT, wxT(”E&xit\tAlt−X”), wxT(”Quit this program”));
//全局快捷键Alt−X
wxMenuBar ∗menuBar = new wxMenuBar(); //菜单栏
menuBar−>Append(fileMenu, wxT(”&File”));
menuBar−>Append(helpMenu, wxT(”&Help”));
SetMenuBar(menuBar);
CreateStatusBar(2);
SetStatusText(wxT(”Welcome to wxWidgets!”));

​ 接下来创建了菜单条。增加菜单项的Append函数的三个参数的意义分别为:菜单项标识,菜单上的文本以及一个稍微长一些的帮助字符串。这个帮助字符串会自动在菜单项被高亮显示的时候自动显示在状态栏上。菜单上的文本中由”&”符号前导的字符将成为菜单的快捷操作符,在实际的显示中用下划线表示。而”\t”符号则前导一个全局的快捷键,这个快捷键可以在菜单项没有被打开的时候触发菜单功能。

​ 这个构造函数所做的最后一件事是创建一个由两个区域组成的状态条并且在状态条的第一个区域写上欢迎的字样。

表1

wxFrame初始化声明 NULL wxID ANY title
父窗口NULL 窗口标识wxID_ANY让wxWidgets自己选择一个 标题

表2

1.Append()

wxMenu使用规范:

1
wxMenu−>Append(wxID_ABOUT(标识ID), wxT(”&显示的名字...\t快捷键”), wxT(”帮助字符串”));
wxMenuBar使用规范:
1
2
wxMenuBar ∗menuBar = new wxMenuBar(); //声明菜单栏
menuBar−>Append(wxMenu, wxT(”&File”));

wxMenu与wxMenuBar的附加函数

Append 菜单项标识 菜单上的文本 帮助字符串
wxID_ABOUT wxT(”&About…\tF1”) wxT(”Show about dialog”)

6.全部代码

main.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Name: minimal.cpp
// Purpose: Minimal wxWidgets sample
// Author:Julian Smart
#include ”wx/wx.h”
// 应用程序类
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};

//主窗口
class MyFrame : public wxFrame
{
public:
//主窗口构造函数
MyFrame(const wxString& title);

//事件处理函数
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
private:
//声明事件表
DECLARE_EVENT_TABLE()
};

//宏定义
DECLARE_APP(MyApp)//有了这一行就可以使用MyApp& wxGetApp()了
IMPLEMENT_APP(MyApp)//告诉应用程序是哪个类wxWidgets

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//初始化程序
bool MyApp::OnInit()
{
//创建主窗口
MyFrame ∗frame = new MyFrame(wxT(”Minimal wxWidgets App”));

//显示主窗口
frame−>Show(true);

//开始事件处理循环
return true;
}


//事件表
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU(wxID_EXIT, MyFrame::OnExit)
END_EVENT_TABLE()


void MyFrame::OnAbout(wxCommandEvent& event)
{
wxString msg;
msg.Printf(wxT(”Hello and welcome to %s”),
wxVERSION STRING);

wxMessageBox(msg, wxT(”About Minimal”),
wxOK | wxICON INFORMATION, this);
}


void MyFrame::OnExit(wxCommandEvent& event)
{
// 释放主窗口
Close();
}


//构造函数 最复杂的写最下边
MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title)
{
//设置窗口图标
SetIcon(wxIcon(mondrian_xpm));

// 建菜单条
wxMenu *fileMenu = new wxMenu;

// 添加“关于”菜单项
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(wxID_ABOUT, wxT("&About...\tF1"),
wxT("Show about dialog"));

fileMenu->Append(wxID_EXIT, wxT("E&xit\tAlt-X"),
wxT("Quit this program"));

// 将菜单项添加到菜单条中
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(fileMenu, wxT("&File"));
menuBar->Append(helpMenu, wxT("&Help"));

// 然后将菜单条放置在主窗口上.…
SetMenuBar(menuBar);

// 创建一个状态条来让一切更有趣些。
CreateStatusBar(2);
SetStatusText(wxT("Welcome to wxWidgets!"));
}

类比Qt的话感觉就是

2. Qt类比

1
2
3
4
5
6
7
8
9
10
class MyQtGuiApp : public QMainWindow
{
Q_OBJECT

public:
MyQtGuiApp(QWidget *parent = Q_NULLPTR);

private:
Ui::MyQtGuiApp ui;
}

MyQtGuiApp.cpp

1
2
3
4
5
QtGuiApp::QtGuiApp(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
}

欢迎关注我的其它发布渠道