#2023重拾c++
目标
学习wxWeights的第一个Demo Bombs,并参考写一个HelloWX。
架构图
废话少说先上图。这么小的Demo,用上架构图这名字是不是很虚?
类分析
本Demo一共4个类
- BombsApp —— 类似Android里的Applaction,每个应用一个
- BombsFrame —— 类似Android里的Activity,有几个主窗口定义几个
- BombsCanvas —— 画布?Frame怎么画?看懂了再回来改这里
- BombsGame —— 游戏逻辑 我们的Demo不需要
如上图,BombsGame和wxWeights 库没有什么关联,大胆猜想这是游戏的逻辑,和框架绘制没有关系。我们学习wxWeight重点关注的应该是其他三个类。因此,初步设想,我们的Demo也是 App Frame Canvas 三个类。
BombsApp
声明
class BombsApp: public wxApp
{
public:
virtual bool OnInit() wxOVERRIDE;
private :
BombsFrame *m_frame;
BombsGame m_game;
};
wxDECLARE_APP(BombsApp);
一共这几行,再去掉游戏逻辑,看的很清楚。
- 定义了Frame
- 重写了OnInit
- 定义App wxDECLARE_APP宏我们的Demo不要搞丢了
实现
wxIMPLEMENT_APP(BombsApp);
bool BombsApp::OnInit()
{
srand((unsigned) time(NULL));
m_frame = new BombsFrame(&m_game);
m_frame->NewGame(bombsID_EASY, false);
return true;
}
实现Appwx IMPLEMENT_APP宏我们的Demo不要搞丢了
OnInit是应用初始化的时机,wx框架在初始化应用的时候会调用。
调用srand设置随机数的种子。我们的Demo不需要
创建Frame
调用Frame的NewGame方法,启动新的游戏我们的Demo不需要
这样说来,我们的Demo App只需要做一件事情,创建Frame。
BombsFrame
声明
class BombsFrame : public wxFrame
{
public:
BombsFrame(BombsGame *bombsGame);
void NewGame(int level, bool query);
private:
void OnNewGame(wxCommandEvent& event);
void OnEasyGame(wxCommandEvent& event);
void OnMediumGame(wxCommandEvent& event);
void OnHardGame(wxCommandEvent& event);
void OnEasyCorner(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
BombsGame *m_game;
bool m_easyCorner;
int m_lastLevel;
// Subwindows for reference within the program.
BombsCanvas *m_canvas;
wxDECLARE_EVENT_TABLE();
};
BombsFrame(BombsGame *bombsGame);
构造函数,用来创建窗口,这里是重点!
void NewGame(int level, bool query);
新游戏逻辑的接口我们的Demo不需要
void OnNewGame(wxCommandEvent& event);
void OnEasyGame(wxCommandEvent& event);
void OnMediumGame(wxCommandEvent& event);
void OnHardGame(wxCommandEvent& event);
void OnEasyCorner(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
这一堆OnXXX是菜单点击相应,我们的Demo可以实现一个意思一下
BombsGame *m_game;
bool m_easyCorner;
int m_lastLevel;
// Subwindows for reference within the program.
BombsCanvas *m_canvas;
m_game m_easyCorner m_lastLevel 不用深究,都是游戏逻辑我们的Demo不用
m_canvas 绘制对象指针,帮助Frame做绘制的,BombsCanvas 对象的指针,下一个要分析的类。注释有点迷。
wxDECLARE_EVENT_TABLE();
定义一个事件对应表,用来实现菜单。
我们自己的Demo 只需要构造函数,一个菜单相应函数,绘制对象指针 + wxDECLARE_EVENT_TABLE 宏。
实现
事件对应表
wxBEGIN_EVENT_TABLE(BombsFrame, wxFrame)
EVT_MENU(wxID_NEW, BombsFrame::OnNewGame)
EVT_MENU(bombsID_EASY, BombsFrame::OnEasyGame)
EVT_MENU(bombsID_MEDIUM, BombsFrame::OnMediumGame)
EVT_MENU(bombsID_HARD, BombsFrame::OnHardGame)
EVT_MENU(bombsID_EASYCORNER, BombsFrame::OnEasyCorner)
EVT_MENU(wxID_EXIT, BombsFrame::OnExit)
EVT_MENU(wxID_ABOUT, BombsFrame::OnAbout)
wxEND_EVENT_TABLE()
事件对应表实现,定义了ID 和 相应函数的对应关系。
构造函数
BombsFrame::BombsFrame(BombsGame *game)
: wxFrame(NULL, wxID_ANY, wxT("wxBombs"), wxDefaultPosition,
wxSize(300, 300), wxDEFAULT_DIALOG_STYLE|wxMINIMIZE_BOX)
{
m_game = game;
m_easyCorner = false;
m_lastLevel = bombsID_EASY;
SetIcon(wxICON(bombs));
#if wxUSE_STATUSBAR
CreateStatusBar();
#endif
// Create a menu bar for the frame
wxMenuBar *menuBar = new wxMenuBar;
wxMenu *menuFile = new wxMenu;
wxMenu *menuLevel = new wxMenu;
menuLevel->AppendRadioItem(bombsID_EASY, wxT("&Easy (10x10)\tCtrl-1"));
menuLevel->AppendRadioItem(bombsID_MEDIUM, wxT("&Medium (15x15)\tCtrl-2"));
menuLevel->AppendRadioItem(bombsID_HARD, wxT("&Hard (25x20)\tCtrl-3"));
menuFile->Append(wxID_NEW, wxT("&New game\tCtrl-N"));
menuFile->Append(bombsID_LEVEL, wxT("&Level"),menuLevel, wxT("Starts a new game"));
menuFile->AppendCheckItem(bombsID_EASYCORNER, wxT("&Easy corner"));
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT, wxGetStockLabel(wxID_EXIT), wxT("Quits the application"));
menuBar->Append(menuFile, wxT("&File"));
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT, wxT("&About"),
wxT("Displays the program information") );
menuBar->Append(menuHelp, wxT("&Help"));
SetMenuBar(menuBar);
// Create child subwindows.
m_canvas = new BombsCanvas(this, m_game);
// Ensure the subwindows get resized o.k.
// OnSize(width, height);
// Centre frame on the screen.
Centre(wxBOTH);
// Show the frame.
Show();
}
构造函数 中wxFrame构造需要关注
BombsFrame::BombsFrame(BombsGame *game)
: wxFrame(NULL, wxID_ANY, wxT("wxBombs"), wxDefaultPosition,
wxSize(300, 300), wxDEFAULT_DIALOG_STYLE|wxMINIMIZE_BOX)
wxFrame源码
wxFrame(wxWindow *parent,
wxWindowID id,
const wxString& title,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxDEFAULT_FRAME_STYLE,
const wxString& name = wxASCII_STR(wxFrameNameStr))
parent,我们要实现的是独立窗口,没有父窗,所以这里是NULL
id,填了 wxID_ANY,我去看一下定义
/* any id: means that we don't care about the id, whether when installing
* an event handler or when creating a new window */
wxID_ANY = -1,
不需要关注的id。我们的Demo也这么干就好了,后续再来研究
title 标题,很清晰 wxT(“wxBombs”)
妥妥的放在了这里
pos = wxDefaultPosition 窗口初始位置,默认值是 wxDefaultPosition Demo里填的也是wxDefaultPosition
size = wxDefaultSize 窗口的初始位置,默认值 wxDefaultSize Demo里填的是wxSize(300, 300)
style = wxDEFAULT_FRAME_STYLE 窗口样式,默认值 wxDEFAULT_FRAME_STYLE Demo里填的是wxDEFAULT_DIALOG_STYLE|wxMINIMIZE_BOX 窗口样式要看wxWeights的文档了,后续再研究。
m_game = game;
m_easyCorner = false;
m_lastLevel = bombsID_EASY;
游戏逻辑忽略掉
SetIcon(wxICON(bombs));
设置图标
这里bombs是资源ID,如何对应上的我们后续分析源码。
#if wxUSE_STATUSBAR
CreateStatusBar();
#endif
wxUSE_STATUSBAR 宏是设置是否启动状态栏的,我们的设置是打开的。
CreateStatusBar 创建状态栏
// Create a menu bar for the frame
wxMenuBar *menuBar = new wxMenuBar;
wxMenu *menuFile = new wxMenu;
wxMenu *menuLevel = new wxMenu;
menuLevel->AppendRadioItem(bombsID_EASY, wxT("&Easy (10x10)\tCtrl-1"));
menuLevel->AppendRadioItem(bombsID_MEDIUM, wxT("&Medium (15x15)\tCtrl-2"));
menuLevel->AppendRadioItem(bombsID_HARD, wxT("&Hard (25x20)\tCtrl-3"));
menuFile->Append(wxID_NEW, wxT("&New game\tCtrl-N"));
menuFile->Append(bombsID_LEVEL, wxT("&Level"),menuLevel, wxT("Starts a new game"));
menuFile->AppendCheckItem(bombsID_EASYCORNER, wxT("&Easy corner"));
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT, wxGetStockLabel(wxID_EXIT), wxT("Quits the application"));
menuBar->Append(menuFile, wxT("&File"));
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT, wxT("&About"),
wxT("Displays the program information") );
menuBar->Append(menuHelp, wxT("&Help"));
SetMenuBar(menuBar);
这一整段就是在创建这个菜单栏了好像截个gif,有机会了研究下
没有行号有点抓狂
wxMenuBar *menuBar = new wxMenuBar;
MenuBar 就是这个东西
wxMenu *menuFile = new wxMenu;
wxMenu是这个东西
wxMenu *menuLevel = new wxMenu;
menuLevel 就是这个东西
于是:
menuLevel->AppendRadioItem(bombsID_EASY, wxT("&Easy (10x10)\tCtrl-1"));
menuLevel->AppendRadioItem(bombsID_MEDIUM, wxT("&Medium (15x15)\tCtrl-2"));
menuLevel->AppendRadioItem(bombsID_HARD, wxT("&Hard (25x20)\tCtrl-3"));
menuLevel 里 AppendRadioItem 了三次,参数是 ID + 字符串
RandioItem就是一组只能选一个,前面有个点标注当前选的哪个。
menuFile->Append(wxID_NEW, wxT("&New game\tCtrl-N"));
menuFile->Append(bombsID_LEVEL, wxT("&Level"),menuLevel, wxT("Starts a new game"));
menuFile->AppendCheckItem(bombsID_EASYCORNER, wxT("&Easy corner"));
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT, wxGetStockLabel(wxID_EXIT), wxT("Quits the application"));
menuFile 里添加了5个东西
普通菜单项 wxT(“&New game\tCtrl-N”)
子菜单wxT(“&Level”), 对应的是menuLevel
CheckItem wxT(“&Easy corner”)效果如下
Separator,就是分割线
普通菜单项,wxGetStockLabel(wxID_EXIT) ,这个菜单项还带有状态栏提示: wxT(“Quits the application”)
// Returns label that should be used for given stock UI element (e.g. "&OK"
// for wxSTOCK_OK); if wxSTOCK_WITH_MNEMONIC is given, the & character
// is included; if wxSTOCK_WITH_ACCELERATOR is given, the stock accelerator
// for given ID is concatenated to the label using \t as separator
WXDLLIMPEXP_CORE wxString wxGetStockLabel(wxWindowID id,
long flags = wxSTOCK_WITH_MNEMONIC);
通过wxGetStockLabel 获取wx预定义按钮的文字。这样看来,第一菜单项 wxID_NEW wxT(“&New game\tCtrl-N”),也可是类似的实现,我们改了试试!
果然,wxGetStockLabel(wxID_NEW) 还是 New,状态栏的文本也出现了。
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT, wxT("&About"),
wxT("Displays the program information") );
menuBar->Append(menuHelp, wxT("&Help"));
SetMenuBar(menuBar);
创建一个Help菜单
添加一个wxT(“&About”)按钮, 状态栏提示 wxT(“Displays the program information”)
再把 Help菜单加到wxMenu(顶部菜单)上
最后把wxMenu填到窗口上
// Create child subwindows.
m_canvas = new BombsCanvas(this, m_game);
注释说创建一个子窗口,有点迷
总之是创建了 BombsCanvas 下一个需要研究的类,晚点再看。
// Centre frame on the screen.
Centre(wxBOTH);
// Show the frame.
Show();
窗口居中 显示窗口,常规操作。窗口看不到记得试试Show!
菜单响应函数
挑两个菜单响应函数看看
void BombsFrame::OnExit(wxCommandEvent& WXUNUSED(event))
{
Close();
}
退出 处理函数,调用了Frame基类的Close方法,毫无疑问是要关掉窗口
void BombsFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox(
wxT("wxBombs (c) 1996 by P. Foggia\n<foggia@amalfi.dis.unina.it>"),
wxT("About wxBombs") );
}
关于 处理函数,调用了wxWeights提供的MessageBox函数,弹出对话框
未完待续