3

Summary:

After adapting the code in the wxwidgets Hello World tutorial into a "module" in the CppMicroServices framework, events registered using the Event Table or Bind() do not appear to fire, but events registered using Connect() do.

i.e. When I click on menu items, I don't get the pop up. If I use Connect(...) to register the event handler, the pop up does work.

Why does this happen, and how can I fix it?

Environment:

  • Ubuntu 14.04
  • g++ 4.9
  • wxWidgets 3.0.2
  • biicode 3.0

Code:

Github repository - for the full source. It doesn't build easily (ask me for compilation steps if it will help to diagnose this).

// The following are excerpts, I haven't included the namespace declarations / #includes

// === BlankDisplayService.cpp === //
// I know the constructor is "heavy", but this is just a spike
// For this issue, this is the entry point
BlankDisplayService::BlankDisplayService() {
    wxApp::SetInstance( new BlankApplication() );
    openWindow(0, nullptr);
}

BlankDisplayService::~BlankDisplayService() {
    wxTheApp->OnExit();
    wxEntryCleanup();
}

int BlankDisplayService::openWindow(int argc, char** argv) {
    wxEntryStart(argc, argv);
    wxTheApp->OnInit();
    wxTheApp->OnRun();
    return 0;
}



// === BlankApplication.cpp === //
bool BlankApplication::OnInit() {
    BlankWindow *window = new BlankWindow( "Hello World", wxPoint(50, 50), wxSize(450, 340));
    window->Show(true);
    return true;
}


// === BlankWindow.h === //
class BlankWindow : public wxFrame {
private:
    wxStaticText *st1;
    wxStaticText *st2;

public:
    enum Command {
        ID_Hello = 1
    };

    BlankWindow(const wxString& title, const wxPoint& pos, const wxSize& size);
    virtual ~BlankWindow();

private:
    void OnMove(wxMoveEvent & event);
    void OnHello(wxCommandEvent& event);
    void OnExit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);
    wxDECLARE_EVENT_TABLE();
};


// === BlankWindow.cpp === //

wxBEGIN_EVENT_TABLE(sl::desktop::demo::wx::BlankWindow, wxFrame)
    EVT_MENU(ID_Hello,   BlankWindow::OnHello)
    EVT_MENU(wxID_EXIT,  BlankWindow::OnExit)
    EVT_MENU(wxID_ABOUT, BlankWindow::OnAbout)
wxEND_EVENT_TABLE()

BlankWindow::BlankWindow(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) {
    wxMenu *menuFile = new wxMenu;
    menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
    menuFile->AppendSeparator();
    menuFile->Append(wxID_EXIT);
    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append(wxID_ABOUT);
    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menuFile, "&File" );
    menuBar->Append( menuHelp, "&Help" );
    SetMenuBar( menuBar );
    CreateStatusBar();
    SetStatusText( "Welcome to wxWidgets!" );


    wxPanel* panel = new wxPanel(this, -1);
    panel->SetSize(200, 100);

    st1 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 10));
    st2 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 30));

    Connect(wxEVT_MOVE, wxMoveEventHandler(BlankWindow::OnMove));

    Centre();

    //Connect(wxEVT_MENU, wxCommandEventHandler(BlankWindow::OnHello));
}

BlankWindow::~BlankWindow() {
}

void BlankWindow::OnMove(wxMoveEvent& event) {
    wxPoint size = event.GetPosition();
    st1->SetLabel(wxString::Format(wxT("x: %d"), size.x ));
    st2->SetLabel(wxString::Format(wxT("y: %d"), size.y ));
}

void BlankWindow::OnHello(wxCommandEvent& WXUNUSED(event)) {
    wxLogMessage("Hello world from wxWidgets!");
}

void BlankWindow::OnExit(wxCommandEvent& WXUNUSED(event)) {
    Close(true);
}

void BlankWindow::OnAbout(wxCommandEvent& event) {
    wxMessageBox("This is a wxWidgets' Hello world sample", "About Hello World", wxOK | wxICON_INFORMATION );
}

Notes:

I'm using biicode to manage my dependencies, so the imports may not be what you're used to seeing.

I am confident that all of that runs in the main thread.


Update:

Shifting the openWindow(...) call from the constructor of BlankDisplayService into the test class caused the events declared using the event table to fire correctly. However I haven't been able to determine why - the threadIds are exactly the same whether it is called from within the test, or within the constructor:

When it doesn't work:

[MAIN] Thread Id: 140460630185856
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from SlDesktopDemoWxBundle
[ RUN      ] SlDesktopDemoWxBundle.DisplaysWindow
[TEST] Thread Id: 140460630185856
[CSTR] Thread Id: 140460630185856
[OPEN] Thread Id: 140460630185856
^C

When it does work:

[CSTR] Thread Id: 139695227554368
[MAIN] Thread Id: 139695227554368
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from SlDesktopDemoWxBundle
[ RUN      ] SlDesktopDemoWxBundle.DisplaysWindow
[TEST] Thread Id: 139695227554368
[OPEN] Thread Id: 139695227554368
[       OK ] SlDesktopDemoWxBundle.DisplaysWindow (3165 ms)
[----------] 1 test from SlDesktopDemoWxBundle (3165 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (3166 ms total)
[  PASSED  ] 1 test.
Azriel
  • 386
  • 2
  • 13
  • 1
    I don't have experience with wxWidgets, but maybe your problem is related to static initialization order problems. If your `BlankDisplayService` is instantiated within a `Activator::Load()` method of your CppMicroServices module, that code is executed during static initialization of your module. If you call into external libraries, these calls might not be safe at this point in time. – Sascha May 31 '15 at 17:58
  • I think that is the case that's happening, thanks – Azriel May 31 '15 at 22:01

1 Answers1

0

The problem is probably in your initialization code. It's hard to see what exactly are you trying to achieve here, as you end up calling OnRun(), which enters the normal event loop anyhow, why don't you just use the normal pattern?

I also don't see where/how does your code exit the event loop. If it does too soon, it could explain why you don't get the events.

VZ.
  • 21,740
  • 3
  • 39
  • 42
  • By "normal pattern" do you mean using one of the macro calls to start the application? The constraint is I have is wxwidgets has to live within a CppMicroServices module, which means it musn't contain the main entry point. The code doesn't exit the event loop; I was just checking how I would go about getting wxwidgets to run as a module and not lose any of its intended functionality. – Azriel Apr 21 '15 at 04:23
  • 1
    If you can enter the main loop, you should just use `wxIMPLEMENT_APP()` as usual and then call `wxEntry()` from whatever entry point you do define, it doesn't have to be `main()`. – VZ. Apr 21 '15 at 11:56