wxWidgets函数调用
wxWidgets Function calling
我目前正在编写一个扫雷程序。有一个连接到菜单的功能。当用户点击“级别”菜单时,程序会显示几个不同级别(即不同块数)的子菜单。我现在只定义了“SetNovice”函数,但发现地雷不会像构造函数那样再次随机分布(这是将按钮与“ButtonOnClicked”函数绑定以设置地雷)。
在 SetNovice 中,我首先调用“RemoveChild()”函数删除 GUIPanel 上的 btn,然后删除数组“MineField”,然后像构造函数一样再次创建 btn 和 MineField,但地雷不是那里。地雷应该在调用“ButtonOnClicked”函数时设置。正如我在创建 btn 时在 SetNovice 中所写的那样,我将 btn“绑定”到 ButtonOnClicked,但没有设置地雷。
程序本应在调用 SetNovice 时随机重置地雷,但结果并没有这样做。为什么?
“MineField”是一个整数数组,其中“-1”是地雷所在的位置。但是我不确定为什么在“SetNovice”函数中,当我尝试删除原来的 MineField 以调整到一个新的 MineField 时,按钮绑定到的函数不会设置地雷。
下面是我的GUIWindow.h
#pragma once
#include "wx/wx.h"
#include "wx/mediactrl.h"
#include "wx/hyperlink.h"
#include <wx/thread.h>
#include "cuppMediaCtrl.h"
class GUIWindow : public wxFrame
{
public:
GUIWindow();
~GUIWindow();
public:
int Width = 20; // default
int Height = 20; // default
int score = 0;
int count = 0;
wxButton **btn; // an array of pointers
int* MineField = nullptr; // a pointer, determining if mines exist
bool ISFirstClick = true;
wxPanel* GUIPanel = new wxPanel(this, wxID_ANY);
wxGridSizer* grid;
wxGridSizer* gridNovice;
wxStatusBar* statusBar = CreateStatusBar(3);
void ButtonOnClicked(wxCommandEvent &evt);
void OnQuit(wxCommandEvent& event);
void ChangeBtnColorRed(wxCommandEvent& evt);
void ChangeBtnColorWhite(wxCommandEvent& evt);
void ChangeBtnColorGreen(wxCommandEvent& evt);
void ChangeBtnColorBlue(wxCommandEvent& evt);
void ChangeBtnColorTokyoHot(wxCommandEvent& evt);
void SetNovice(wxCommandEvent& evt);
void SetIntermediate(wxCommandEvent& evt);
void SetProfessional(wxCommandEvent& evt);
void SetMaster(wxCommandEvent& evt);
void SetGodLevel(wxCommandEvent& evt);
void Website1(wxCommandEvent& evt);
void Website2(wxCommandEvent& evt);
void ShowCreator(wxCommandEvent& evt);
DECLARE_EVENT_TABLE();
};
下面是GUIWindow.cpp(我删除了一些不相关的代码和功能)
#include "GUIWindow.h"
wxBEGIN_EVENT_TABLE(GUIWindow, wxFrame)
EVT_BUTTON(wxID_ANY, ButtonOnClicked)
wxEND_EVENT_TABLE()
GUIWindow::GUIWindow() : wxFrame(nullptr, wxID_ANY, "地雷遊戲", wxPoint(30, 30), wxSize(700, 700))
{
btn = new wxButton *[Width * Height]; // btn == an array of pointers (should allocate new memory)
grid = new wxGridSizer(Width, Height, 0, 0); // set a grid pointer to control the grid
MineField = new int[Width * Height]; // This is where mines are distributed, using nField to indicate mines
wxFont font(18, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
wxString str1 = "Current points: ";
statusBar->SetStatusText(str1, 0);
wxMenuBar* menuBar = new wxMenuBar();
wxMenu* menu1 = new wxMenu();
menuBar->Append(menu1, wxT("About"));
wxMenu* menu2 = new wxMenu();
menuBar->Append(menu2, wxT("Game"));
SetMenuBar(menuBar);
menu1->Append(10001, wxT("Creator"));
wxMenu* imp2 = new wxMenu();
imp2->Append(10011, wxT("Tokyo Hot"));
imp2->Append(10012, wxT("White"));
imp2->Append(10013, wxT("Green"));
imp2->Append(10014, wxT("Red"));
imp2->Append(10015, wxT("Blue"));
menu2->AppendSubMenu(imp2, wxT("Theme"));
wxMenu* imp3 = new wxMenu();
imp3->Append(10020, wxT("Novice"));
imp3->Append(wxID_ANY, wxT("Intermediate"));
imp3->Append(wxID_ANY, wxT("Professional"));
imp3->Append(wxID_ANY, wxT("Master"));
imp3->Append(wxID_ANY, wxT("God-Level"));
menu2->AppendSubMenu(imp3, wxT("Level"));
wxMenu* imp4 = new wxMenu();
imp4->Append(10002, "Introduction");
imp4->Append(10003, "Explanation");
menu1->AppendSubMenu(imp4, "Hints");
wxMenuItem* quit = new wxMenuItem();
quit = new wxMenuItem(menu1, wxID_EXIT, wxT("Quit\tCtrl+Q"));
menu1->Append(quit);
Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::OnQuit));
Centre();
Connect(10001, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ShowCreator));
Connect(10011, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorTokyoHot));
Connect(10012, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorWhite));
Connect(10013, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorGreen));
Connect(10014, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorRed));
Connect(10015, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorBlue));
Connect(10020, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::SetNovice));
Connect(10002, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::Website1));
Connect(10003, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::Website2));
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x)); // every pointer points to a button object with class wxButton
btn[y * Width + x]->SetFont(font);
grid->Add(btn[y * Width + x], 1, wxEXPAND, wxALL);
btn[y * Width + x]->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &GUIWindow::ButtonOnClicked, this);
MineField[y * Width + x] = 0; //set each block to 0 (default value)
}
}
GUIPanel->SetSizer(grid); // "this" is pointing at the parent wxFrame: GUIWindow
GUIPanel->Show();
}
GUIWindow::~GUIWindow()
{
delete[] btn;
}
void GUIWindow::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void GUIWindow::SetNovice(wxCommandEvent& evt)
{
Width = 10;
Height = 10;
score = 0;
GUIPanel->DestroyChildren(); // delete btn, but NOT removing grid.
delete[] MineField;
gridNovice = new wxGridSizer(Width, Height, 0, 0);
wxFont font(18, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
btn = new wxButton* [Width * Height];
MineField = new int[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x)); // every pointer points to a button object with class wxButton
btn[y * Width + x]->SetFont(font);
gridNovice->Add(btn[y * Width + x], 1, wxEXPAND, wxALL);
btn[y * Width + x]->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &GUIWindow::ButtonOnClicked, this);
MineField[y * Width + x] = 0; //set each block to 0 (default value)
}
}
GUIPanel->SetSizer(gridNovice);
GUIPanel->Show();
}
void GUIWindow::ButtonOnClicked(wxCommandEvent &evt)
{
// get position of button
int x = (evt.GetId()) % Width;
int y = (evt.GetId()) / Width;
int mine_count = 0;
wxString s1 = "Believe in Yourself.";
wxString s2 = "You are the PRIDE of NTUEE !";
wxString s3 = "TAIWAN need you!!! so DO NOT give up!!!";
wxString s4 = "We are all together, blessing you.";
wxString s5 = "Who's the best minesweeper ? YOU!!!";
wxString s6 = "Get bored? Try other functions in the menu!";
wxString s7 = "想脫魯 ? 踩地雷吧 XDXDXD";
wxString phraseArr[] = { s1, s2, s3, s4, s5, s6, s7};
if (ISFirstClick == true)
{
int mine = Width * Height / 2;
while (mine != 0)
{
int rx = rand() % Width;
int ry = rand() % Height;
if (MineField[ry * Width + rx] == 0 && rx != x && ry != y)
{
MineField[ry * Width + rx] = -1; // -1 indicates the mine
mine--;
}
}
ISFirstClick = false; // avoid mines been redistributed again
}
btn[y * Width + x]->Enable(false);
if (MineField[y * Width + x] == -1)
{
wxMessageBox("BOOM!");
statusBar->SetStatusText(wxT(""), 2); // reset the status bar
this->count++;
//reset mines
this -> score = 0;
ISFirstClick = true;
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
MineField[y * Width + x] = 0;
btn[y * Width + x]->SetLabel("");
btn[y * Width + x]->Enable(true);
}
}
}
else
{
if (this->count < 7)
{
wxString Newstr = phraseArr[this->count];
statusBar->SetStatusText(Newstr, 2);
this->count++;
}
else if (this->count >= 7) this->count = 0;
this -> score += 1;
wxString scoreStr = std::to_string(this->score);
statusBar->SetStatusText(scoreStr, 1);
//count the mines surrounding
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
if (x + i >= 0 && x + i < Width && y + i >= 0 && y + j < Height)
{
if (MineField[(y + j) * Width + (x + i)] == -1)
{
mine_count++;
}
}
}
}
if (mine_count >= 0)
{
btn[y * Width + x]->SetLabel(std::to_string(mine_count));
}
}
evt.Skip();
}
尝试添加
GUIPanel->Layout();
到GUIWindow::SetNovice
方法结束。这将导致所有添加到面板的按钮都由网格大小调整器排列。
也行
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x));
可能会将按钮设置为具有已分配给其他事物的 ID。你应该使用类似的东西:
btn[y * Width + x] = new wxButton(GUIPanel, wxID_ANY);
相反。如果您需要跟踪 ButtonOnClicked
方法的按钮坐标,您可以执行类似
的操作
- 将地图
std::map<wxWindowID,std::pair<int,int>> m_buttonCoords;
添加到 GUIWindow class。
- in
GUIWindow::SetNovice
在方法开始时清除地图并在创建按钮的循环中调用 m_buttonCoords.insert( std::make_pair(btn[y * Width + x]->GetId(),std::make_pair(x,y)) );
- 在
GUIWindow::ButtonOnClicked
中用这样的过程提取坐标:
std::pair<int,int> coords = m_buttonCoords[evt.GetId()];
int x = coords.first;
int y = coords.second;
可能有更好的方法,但这是我首先想到的。
我目前正在编写一个扫雷程序。有一个连接到菜单的功能。当用户点击“级别”菜单时,程序会显示几个不同级别(即不同块数)的子菜单。我现在只定义了“SetNovice”函数,但发现地雷不会像构造函数那样再次随机分布(这是将按钮与“ButtonOnClicked”函数绑定以设置地雷)。
在 SetNovice 中,我首先调用“RemoveChild()”函数删除 GUIPanel 上的 btn,然后删除数组“MineField”,然后像构造函数一样再次创建 btn 和 MineField,但地雷不是那里。地雷应该在调用“ButtonOnClicked”函数时设置。正如我在创建 btn 时在 SetNovice 中所写的那样,我将 btn“绑定”到 ButtonOnClicked,但没有设置地雷。
程序本应在调用 SetNovice 时随机重置地雷,但结果并没有这样做。为什么?
“MineField”是一个整数数组,其中“-1”是地雷所在的位置。但是我不确定为什么在“SetNovice”函数中,当我尝试删除原来的 MineField 以调整到一个新的 MineField 时,按钮绑定到的函数不会设置地雷。
下面是我的GUIWindow.h
#pragma once
#include "wx/wx.h"
#include "wx/mediactrl.h"
#include "wx/hyperlink.h"
#include <wx/thread.h>
#include "cuppMediaCtrl.h"
class GUIWindow : public wxFrame
{
public:
GUIWindow();
~GUIWindow();
public:
int Width = 20; // default
int Height = 20; // default
int score = 0;
int count = 0;
wxButton **btn; // an array of pointers
int* MineField = nullptr; // a pointer, determining if mines exist
bool ISFirstClick = true;
wxPanel* GUIPanel = new wxPanel(this, wxID_ANY);
wxGridSizer* grid;
wxGridSizer* gridNovice;
wxStatusBar* statusBar = CreateStatusBar(3);
void ButtonOnClicked(wxCommandEvent &evt);
void OnQuit(wxCommandEvent& event);
void ChangeBtnColorRed(wxCommandEvent& evt);
void ChangeBtnColorWhite(wxCommandEvent& evt);
void ChangeBtnColorGreen(wxCommandEvent& evt);
void ChangeBtnColorBlue(wxCommandEvent& evt);
void ChangeBtnColorTokyoHot(wxCommandEvent& evt);
void SetNovice(wxCommandEvent& evt);
void SetIntermediate(wxCommandEvent& evt);
void SetProfessional(wxCommandEvent& evt);
void SetMaster(wxCommandEvent& evt);
void SetGodLevel(wxCommandEvent& evt);
void Website1(wxCommandEvent& evt);
void Website2(wxCommandEvent& evt);
void ShowCreator(wxCommandEvent& evt);
DECLARE_EVENT_TABLE();
};
下面是GUIWindow.cpp(我删除了一些不相关的代码和功能)
#include "GUIWindow.h"
wxBEGIN_EVENT_TABLE(GUIWindow, wxFrame)
EVT_BUTTON(wxID_ANY, ButtonOnClicked)
wxEND_EVENT_TABLE()
GUIWindow::GUIWindow() : wxFrame(nullptr, wxID_ANY, "地雷遊戲", wxPoint(30, 30), wxSize(700, 700))
{
btn = new wxButton *[Width * Height]; // btn == an array of pointers (should allocate new memory)
grid = new wxGridSizer(Width, Height, 0, 0); // set a grid pointer to control the grid
MineField = new int[Width * Height]; // This is where mines are distributed, using nField to indicate mines
wxFont font(18, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
wxString str1 = "Current points: ";
statusBar->SetStatusText(str1, 0);
wxMenuBar* menuBar = new wxMenuBar();
wxMenu* menu1 = new wxMenu();
menuBar->Append(menu1, wxT("About"));
wxMenu* menu2 = new wxMenu();
menuBar->Append(menu2, wxT("Game"));
SetMenuBar(menuBar);
menu1->Append(10001, wxT("Creator"));
wxMenu* imp2 = new wxMenu();
imp2->Append(10011, wxT("Tokyo Hot"));
imp2->Append(10012, wxT("White"));
imp2->Append(10013, wxT("Green"));
imp2->Append(10014, wxT("Red"));
imp2->Append(10015, wxT("Blue"));
menu2->AppendSubMenu(imp2, wxT("Theme"));
wxMenu* imp3 = new wxMenu();
imp3->Append(10020, wxT("Novice"));
imp3->Append(wxID_ANY, wxT("Intermediate"));
imp3->Append(wxID_ANY, wxT("Professional"));
imp3->Append(wxID_ANY, wxT("Master"));
imp3->Append(wxID_ANY, wxT("God-Level"));
menu2->AppendSubMenu(imp3, wxT("Level"));
wxMenu* imp4 = new wxMenu();
imp4->Append(10002, "Introduction");
imp4->Append(10003, "Explanation");
menu1->AppendSubMenu(imp4, "Hints");
wxMenuItem* quit = new wxMenuItem();
quit = new wxMenuItem(menu1, wxID_EXIT, wxT("Quit\tCtrl+Q"));
menu1->Append(quit);
Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::OnQuit));
Centre();
Connect(10001, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ShowCreator));
Connect(10011, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorTokyoHot));
Connect(10012, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorWhite));
Connect(10013, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorGreen));
Connect(10014, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorRed));
Connect(10015, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::ChangeBtnColorBlue));
Connect(10020, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::SetNovice));
Connect(10002, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::Website1));
Connect(10003, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(GUIWindow::Website2));
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x)); // every pointer points to a button object with class wxButton
btn[y * Width + x]->SetFont(font);
grid->Add(btn[y * Width + x], 1, wxEXPAND, wxALL);
btn[y * Width + x]->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &GUIWindow::ButtonOnClicked, this);
MineField[y * Width + x] = 0; //set each block to 0 (default value)
}
}
GUIPanel->SetSizer(grid); // "this" is pointing at the parent wxFrame: GUIWindow
GUIPanel->Show();
}
GUIWindow::~GUIWindow()
{
delete[] btn;
}
void GUIWindow::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void GUIWindow::SetNovice(wxCommandEvent& evt)
{
Width = 10;
Height = 10;
score = 0;
GUIPanel->DestroyChildren(); // delete btn, but NOT removing grid.
delete[] MineField;
gridNovice = new wxGridSizer(Width, Height, 0, 0);
wxFont font(18, wxFONTFAMILY_ROMAN, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
btn = new wxButton* [Width * Height];
MineField = new int[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x)); // every pointer points to a button object with class wxButton
btn[y * Width + x]->SetFont(font);
gridNovice->Add(btn[y * Width + x], 1, wxEXPAND, wxALL);
btn[y * Width + x]->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &GUIWindow::ButtonOnClicked, this);
MineField[y * Width + x] = 0; //set each block to 0 (default value)
}
}
GUIPanel->SetSizer(gridNovice);
GUIPanel->Show();
}
void GUIWindow::ButtonOnClicked(wxCommandEvent &evt)
{
// get position of button
int x = (evt.GetId()) % Width;
int y = (evt.GetId()) / Width;
int mine_count = 0;
wxString s1 = "Believe in Yourself.";
wxString s2 = "You are the PRIDE of NTUEE !";
wxString s3 = "TAIWAN need you!!! so DO NOT give up!!!";
wxString s4 = "We are all together, blessing you.";
wxString s5 = "Who's the best minesweeper ? YOU!!!";
wxString s6 = "Get bored? Try other functions in the menu!";
wxString s7 = "想脫魯 ? 踩地雷吧 XDXDXD";
wxString phraseArr[] = { s1, s2, s3, s4, s5, s6, s7};
if (ISFirstClick == true)
{
int mine = Width * Height / 2;
while (mine != 0)
{
int rx = rand() % Width;
int ry = rand() % Height;
if (MineField[ry * Width + rx] == 0 && rx != x && ry != y)
{
MineField[ry * Width + rx] = -1; // -1 indicates the mine
mine--;
}
}
ISFirstClick = false; // avoid mines been redistributed again
}
btn[y * Width + x]->Enable(false);
if (MineField[y * Width + x] == -1)
{
wxMessageBox("BOOM!");
statusBar->SetStatusText(wxT(""), 2); // reset the status bar
this->count++;
//reset mines
this -> score = 0;
ISFirstClick = true;
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
MineField[y * Width + x] = 0;
btn[y * Width + x]->SetLabel("");
btn[y * Width + x]->Enable(true);
}
}
}
else
{
if (this->count < 7)
{
wxString Newstr = phraseArr[this->count];
statusBar->SetStatusText(Newstr, 2);
this->count++;
}
else if (this->count >= 7) this->count = 0;
this -> score += 1;
wxString scoreStr = std::to_string(this->score);
statusBar->SetStatusText(scoreStr, 1);
//count the mines surrounding
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
if (x + i >= 0 && x + i < Width && y + i >= 0 && y + j < Height)
{
if (MineField[(y + j) * Width + (x + i)] == -1)
{
mine_count++;
}
}
}
}
if (mine_count >= 0)
{
btn[y * Width + x]->SetLabel(std::to_string(mine_count));
}
}
evt.Skip();
}
尝试添加
GUIPanel->Layout();
到GUIWindow::SetNovice
方法结束。这将导致所有添加到面板的按钮都由网格大小调整器排列。
也行
btn[y * Width + x] = new wxButton(GUIPanel, (y * Width + x));
可能会将按钮设置为具有已分配给其他事物的 ID。你应该使用类似的东西:
btn[y * Width + x] = new wxButton(GUIPanel, wxID_ANY);
相反。如果您需要跟踪 ButtonOnClicked
方法的按钮坐标,您可以执行类似
- 将地图
std::map<wxWindowID,std::pair<int,int>> m_buttonCoords;
添加到 GUIWindow class。 - in
GUIWindow::SetNovice
在方法开始时清除地图并在创建按钮的循环中调用m_buttonCoords.insert( std::make_pair(btn[y * Width + x]->GetId(),std::make_pair(x,y)) );
- 在
GUIWindow::ButtonOnClicked
中用这样的过程提取坐标:
std::pair<int,int> coords = m_buttonCoords[evt.GetId()];
int x = coords.first;
int y = coords.second;
可能有更好的方法,但这是我首先想到的。