wxTimer绑定Bind()后没有调用回调函数

3
我想在我的应用程序中使用wxTimer,但我不想使用预处理宏来绑定事件。相反,我希望在CTOR调用期间使用Bind()将事件绑定到我的UI元素上。
绑定计时器后,它没有启动或者没有调用事件处理程序。根据此处的描述,它应该按照我想要的方式工作。我也不想使用Connect()。以同样的方式绑定按钮就可以正常工作。这是一个最小的示例:
#include <wx/wx.h>
#include <iostream>

class MainFrame : public wxFrame
{
private:
    const int DELTA_TIME = 100; // time between timer events (in ms)
private:
    wxTimer* timer = nullptr;
    void OnTimer(wxTimerEvent& evt);
    void OnButtonClick(wxCommandEvent& evt);
public:
    MainFrame();
    virtual ~MainFrame();
};

void MainFrame::OnTimer(wxTimerEvent& evt)
{
    std::cout << "Timer Event!" << std::endl; // never happens
}

void MainFrame::OnButtonClick(wxCommandEvent& evt)
{
    std::cout << "Button Event!" << std::endl; // works just fine
}

MainFrame::MainFrame()
    : wxFrame(nullptr, wxID_ANY, "Test", wxPoint(0, 0), wxSize(600, 400))
{
    timer = new wxTimer(this, wxID_ANY);
    timer->Bind(
        wxEVT_TIMER,                    // evt type
        &MainFrame::OnTimer,            // callback
        this,                           // parent
        timer->GetId()                  // id
    );
    timer->Start(DELTA_TIME, wxTIMER_CONTINUOUS);

    wxButton* testBtn = new wxButton(this, wxID_ANY, "Test", wxPoint(20, 20));
    testBtn->Bind(wxEVT_BUTTON, &MainFrame::OnButtonClick, this);
}

MainFrame::~MainFrame()
{
    if (timer->IsRunning())
    {
        timer->Stop();
    }
}

// application class
class Main : public wxApp
{
public:
    virtual bool OnInit();
};

IMPLEMENT_APP(Main)

bool Main::OnInit()
{
    MainFrame* mainFrame = new MainFrame();
    SetTopWindow(mainFrame);
    mainFrame->Show();
    return true;
}
1个回答

5

您应该进行更改。

timer->Bind(...

只是

Bind(...

代码行timer = new wxTimer(this, wxID_ANY);将主窗口设置为计时器的所有者,因此当计时器触发时,主窗口将被通知。然而,代码行 timer->Bind(... 设置计时器来处理计时器事件。但是,事件不会发送到计时器,而是发送到主窗口。 因此,有必要将主窗口设置为事件处理程序,这就是上述更改所做的。


谢谢您的解释。我从现在开始会这样做了。我对wx还比较新,如果在bind()调用中没有提供ID,是否意味着将所有计时器事件绑定到该回调函数?并且您能否将多个回调函数绑定到某个事件?如果可以,它们的触发顺序是什么? - FalcoGer
抱歉,我混淆了自己的思维,我的第一条评论是错误的,所以我不得不删除它。但是 testBtn->Bind(wxEVT_BUTTON 这行代码是正确的。按钮和其他控件没有像计时器那样的所有者类似物。事件直接抛给它们。因此,testBtn->Bind(wxEVT_BUTTON,... 这行代码将指向 this 对象的 MainFrame 类的 OnButtonClick 方法设置为处理特定按钮的 wxEVT_BUTTON 类型事件的方法。由于这些事件在单击按钮时被抛出,它们将由该方法处理。 - New Pagodi
然而(这是我在现已删除的评论中让自己困惑的部分),wxCommandEvents在特殊之处在于它们会向上过滤,因此,如果需要,您可以让主框架处理事件。例如,您可以像这样处理该事件 Bind(wxEVT_BUTTON,&MainFrame::OnButtonClick,this,testBtn->GetId(),testBtn->GetId()); 在这种情况下,这意味着主框架应该通过调用OnButtonClick方法来处理类型为wxEVT_BUTTON并且事件ID为testBtn的事件。 - New Pagodi
这个方法有效的原因是因为wxWidgets首先会将事件抛给testBtn,然后发现它没有处理程序。由于wxEVT_BUTTON是wxCommandEvent的一种类型,并且wxCommandEvents可以向上过滤,因此wxWidgets将传递事件给按钮的父级(即主框架)尝试处理它。如果您使用上面给出的长Bind命令,则主框架将使用OnButtonClick方法来处理它。 - New Pagodi
回答关于在Bind中使用ids的问题,在第一个版本(testBtn->Bind(wxEVT_BUTTON,...)中,我们不需要id,因为按钮应该只接收自己点击产生的wxEVT_BUTTON类型事件。因此,使用一个方法处理所有这样的事件是合理的。然而,当使用主框架来处理wxEVT_BUTTON事件时,如果我们不提供ids(即仅使用Bind(wxEVT_BUTTON,&MainFrame::OnButtonClick,this);),它将使用该方法来处理所有按钮点击事件。 - New Pagodi
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接