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 方法的按钮坐标,您可以执行类似

的操作
  1. 将地图 std::map<wxWindowID,std::pair<int,int>> m_buttonCoords; 添加到 GUIWindow class。
  2. in GUIWindow::SetNovice 在方法开始时清除地图并在创建按钮的循环中调用 m_buttonCoords.insert( std::make_pair(btn[y * Width + x]->GetId(),std::make_pair(x,y)) );
  3. GUIWindow::ButtonOnClicked中用这样的过程提取坐标:
std::pair<int,int> coords = m_buttonCoords[evt.GetId()];
int x = coords.first;
int y = coords.second;

可能有更好的方法,但这是我首先想到的。