Search code examples
c++11wxwidgets

wxTimer not calling overriden Notify()


I'm running into an issue where I implemented a derived wxTimer class to override the Notify() call since I'm not using an owner implementation as described in the documentation.

When I debug the run, I can see

  • the timer is being instantiated
  • my_timer_instance->IsRunning() returns true
  • MyTimer::Notify() is never called

This leads me to believe that the timer is being set and running, but when it expires it's calling the base class Notify() procedure and not my override it's not calling notify() but I'm not sure why.

EDIT: I added frame->getTimer()->Notify(); to my app and the correct procedure was called. Therefore, the timer just isn't calling Notify when it expires.

EDIT2: Added this minimal working example, and the timer works as expected. I'll try to compare the two and see what the problem is.

MyApp.hpp

#pragma once

#ifndef __NONAME_H__
#define __NONAME_H__

#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/statusbr.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/frame.h>
#include <wx/timer.h>
///////////////////////////////////////////////////////////////////////////


class MyTimerClass : public wxTimer
{
    wxFrame* MyFrame;
public:
    MyTimerClass(wxFrame* frame): MyFrame(frame) {};

    void Notify() override;
};
///////////////////////////////////////////////////////////////////////////////
/// Class MyFrame1
///////////////////////////////////////////////////////////////////////////////
class MyFrame1 : public wxFrame
{
private:

protected:
    wxStatusBar* m_statusBar1;
    MyTimerClass* MyTimer;
public:
    void StartTimer(int TimeInSeconds);
    MyFrame1(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(500, 300), long style = wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL);

    ~MyFrame1();

};

#endif //__NONAME_H__

MyApp.cpp

#include "MyApp.hpp"
#include "wx/wxprec.h"
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

///////////////////////////////////////////////////////////////////////////
void MyTimerClass::Notify()
{
    MyFrame->SetStatusText("Timer popped", 0);
}
MyFrame1::MyFrame1(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style)
{
    MyTimer = new MyTimerClass(this);
    this->SetSizeHints(wxDefaultSize, wxDefaultSize);

    m_statusBar1 = this->CreateStatusBar(1, wxSTB_SIZEGRIP, wxID_ANY);

    this->Centre(wxBOTH);

    this->StartTimer(5);
}
void MyFrame1::StartTimer(int TimeInSeconds)
{
    SetStatusText("Timer started with " + std::to_string(TimeInSeconds) + " seconds.");
    MyTimer->Start(TimeInSeconds * 1000);
}
MyFrame1::~MyFrame1()
{
}


// ----------------------------------------------------------------------------
// resources
// ----------------------------------------------------------------------------

// the application icon (under Windows it is in resources and even
// though we could still include the XPM here it would be unused)
#ifndef wxHAS_IMAGES_IN_RESOURCES
#include "../sample.xpm"
#endif

// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------

class MyApp : public wxApp
{
public:
    virtual bool OnInit() wxOVERRIDE;
};


enum
{
    // menu items
    Minimal_Quit = wxID_EXIT,
    Minimal_About = wxID_ABOUT
};

wxIMPLEMENT_APP(MyApp);

bool MyApp::OnInit()
{
    // call the base class initialization method, currently it only parses a
    // few common command-line options but it could be do more in the future
    if (!wxApp::OnInit())
        return false;

    // create the main application window
    MyFrame1 *frame = new MyFrame1(NULL, -1, "Test Frame");
    frame->Show(true);

    return true;
}

Solution

  • So after mimicking the code provided in the question, the following changes were made:

    Instead of using a getter and setter to access the private timer member, I instead use
    void refreshTimer(int time_in_seconds) in my parent frame class and create the timer in the parent frame's constructor rather than letting the app create it and pass it in.

    I don't see why either of those two things would change the behavior of the timer but the timer now works as expected. I apologize for not being able to identify a concrete bug as the source of the problem.

    NOTE: This behavior was caused by the timer being invoked outside the wxwindow's thread. Be careful when creating multithreaded programs using wxwidgets as a GUI. To circumvent this issue since I needed the timer to be invoked in a different thread, I created my own timer class that works correctly.