【问题标题】:wxTimer not calling callback after binding with Bind()wxTimer 与 Bind() 绑定后未调用回调
【发布时间】:2025-12-30 19:35:12
【问题描述】:

我想在我的应用程序中使用 wxTimer,但我不想使用预处理器宏来绑定事件。相反,我想在 CTOR 调用期间使用 Bind() 将事件绑定到我的 ui 元素。
绑定我的计时器后,它不启动或不调用事件处理程序。根据this,它应该像我想的那样工作。我也不想使用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;
}

【问题讨论】:

    标签: c++ callback wxwidgets


    【解决方案1】:

    你应该改变

    timer->Bind(...
    

    只是

    Bind(...
    

    timer = new wxTimer(this, wxID_ANY); 行将主框架设置为计时器的所有者,因此这是触发计时器时通知的项目。但是,timer-&gt;Bind(... 行设置了计时器来处理计时器事件。但是事件不会发送到计时器,而是发送到主框架。所以有必要将主框架设置为事件处理程序,这就是上面给出的更改所做的。

    【讨论】:

    • 感谢您的回答。这是否意味着我也应该将 button->Bind 更改为 Bind()?
    • 感谢您的解释。从现在开始我会这样做。我对 wx 很陌生。这是否也意味着如果您在 bind() 调用中不提供 ID,您会将所有计时器事件绑定到该回调?您可以将多个回调绑定到某个事件吗?如果是这样,他们会按什么顺序被解雇?
    • 对不起,我把自己弄糊涂了,我的第一条评论太错误了,我不得不删除它。但是testBtn-&gt;Bind(wxEVT_BUTTON 行很好。按钮和其他控件没有任何类似于计时器的所有者。事件被直接抛出给他们。所以testBtn-&gt;Bind(wxEVT_BUTTON,... 行从this 指向的对象的类MainFrame 中设置方法OnButtonClick 来处理这个特定按钮的wxEVT_BUTTON 类型的事件。由于这些事件在按钮被触发时被抛出,它们将由该方法处理。
    • 但是(这是我在现在删除的评论中让自己感到困惑的部分),wxCommandEvents 的特殊之处在于它们 filter up 因此,如果您愿意,可以让主框架处理事件如果你想。例如,您可以像 Bind(wxEVT_BUTTON,&amp;MainFrame::OnButtonClick,this, testBtn-&gt;GetId(),testBtn-&gt;GetId()); 这样处理事件。在这种情况下,这意味着主框架应该处理 wxEVT_BUTTON 类型的事件,其中事件的 id 为 testBtn,方法是调用 OnButtonClick 方法。
    • 之所以有效是因为 wxWidgets 将首先将事件抛出到 testBtn 并看到它没有处理程序。然后由于 wxEVT_BUTTON 是 wxCommandEvent 的一种类型,并且 wxCommandEvents 过滤起来,wxWidgets 会将事件传递给按钮的父级(即主框架)来尝试处理它。如果使用上面给出的long Bind命令,主框架会使用OnButtonClick方法来处理。
    最近更新 更多