Search code examples
c++segmentation-faultwxwidgets

wxStaticText::SetLabel() segmentation fault


I'm new to wxWidgets and I wanted to make a simple GUI application (on ubuntu) for reference.

I created a simple window, added some buttons and in a button event handler I want to update a wxStaticText object's text on screen to indicate which button has been pressed.

However when I call the SetLabel or SetLabelText functions that wxStaticText inherits, then I get a segmentation fault.

Maybe I'm missing something and wxStaticText is just not able to have their label set and I need another control. But I couldn't find anything like that in the class list.

I'm using wxWidgets 3.1.4 from the codelite repositories.

MainFrame.cpp

#include "MainFrame.h"
#include <iostream>

// segfaults/bugs with simple constants, needs to be static
static const char* TITLE;
const int MainFrame::PX = 30;
const int MainFrame::PY = 30;
const int MainFrame::SX = 400;
const int MainFrame::SY = 300;

MainFrame::MainFrame()
    : wxFrame(nullptr, static_cast<wxWindowID>(IDs::MainFrame), wxString(TITLE), wxPoint(PX, PY), wxSize(SX, SY))
{
    // create bar
    // no need to call delete. new is overloaded for wx elements.
    menuBar = new wxMenuBar();
    // create menues
    menuFile = new wxMenu();

    // menuitems file
    menuItemClose = new wxMenuItem(menuFile, static_cast<int>(IDs::MenuItemClose), "Exit", "Close the application");
    Connect(menuItemClose->GetId(), wxEVT_MENU, wxCommandEventHandler(MainFrame::onClickMenuItemClose));
    menuFile->Append(menuItemClose);

    // construct menu bar from menues
    menuBar->Append(menuFile, "File");

    this->SetMenuBar(menuBar);

    // misc
    lbl_Text = new wxStaticText(this, wxID_ANY, "Press a button!");

    // create sizers
    gridSizer = new wxGridSizer(1, 5, wxSize(5,5));

    boxSizer = new wxBoxSizer(wxVERTICAL);
    boxSizer->Add(lbl_Text,
                    0, // make vectically un-stretchable
                    wxALL, // implicit top alignment, border all around
                    10 // border 10 px
    );
    boxSizer->Add(gridSizer, 1, wxEXPAND, 0);

    this->SetSizerAndFit(boxSizer);

    // create buttons
    v_btn_Buttons = std::make_unique<std::vector<wxButton*>>(5);
    for (int i = 0; i < 5; i++)
    {
        wxString txt = wxString(fmt::format("Button{0}", (i+1)).c_str());
        std::cout << "Creating Button #" << i+1 << ", with label: " << txt << std::endl;
        wxButton* btn = new wxButton(this, static_cast<int>(IDs::Button1) + i, txt);
        btn->Connect(wxEVT_BUTTON, wxCommandEventHandler(MainFrame::onClickButton));
        v_btn_Buttons->push_back(btn);
        gridSizer->Add(btn);
    }
}

MainFrame::~MainFrame()
{
}

void MainFrame::onClickMenuItemClose(wxCommandEvent& evt)
{
    Close();
    // no call of evt.Skip() because we're done with it.
}

void MainFrame::onClickButton(wxCommandEvent& evt)
{
    wxString txt = wxString(fmt::format(
        "Pressed Button #{0} (ID: {1})",
        evt.GetId() - static_cast<int>(IDs::Button1),
        evt.GetId()
    ));
    lbl_Text->SetLabelText(txt);
}

MainFrame.h

#pragma once
#include "pch.h"

class MainFrame : public wxFrame
{
private:
    // constants
    const char* TITLE = "My awesome title!";
    static const int PX;
    static const int PY;
    static const int SX;
    static const int SY;

private:
    // member variables

private:
    // Layout and Windows

    // layout using sizers
    /*
     * +-----------------+
     * | MENU            |
     * +-----------------+
     * |+---------------+|
     * ||BoxSizer (col) ||
     * ||TextLabel      ||
     * |+---------------+|
     * ||+-------------+||
     * |||GridSizer    |||
     * |||BTN|BTN|...  |||
     * ||+-------------+||
     * |+---------------+|
     * +-----------------+
    */

    // wxMenuBar { wxMenu {wxMenuItem,...}, ... }

    wxMenuBar*                                  menuBar         = nullptr;
    wxMenu*                                     menuFile        = nullptr;
    wxMenuItem*                                 menuItemClose   = nullptr;

    wxBoxSizer*                                 boxSizer        = nullptr;
    wxGridSizer*                                gridSizer       = nullptr;

    wxStaticText*                               lbl_Text        = nullptr;
    std::unique_ptr<std::vector<wxButton*>>     v_btn_Buttons   = nullptr;

protected:
    // Event handlers
    void onClickMenuItemClose(wxCommandEvent& evt);
    void onClickButton(wxCommandEvent& evt);

public:
    MainFrame();
    virtual ~MainFrame();

    // used to identify elements
    // can be extracted from events
    enum class IDs : wxWindowID
    {
        MainFrame       = 10000,
        MenuItemClose   = 10100,
        Button1         = 10500,
        Button2,
        Button3,
        Button4,
        Button5
    };

};

pch.h:

#pragma once

#include <wx/wx.h>
#include <vector>
#include <memory> // smart pointers

// will be in c++20 as
// #include <format>
#include <fmt/format.h>

main.cpp

#include "pch.h"

#include "MainFrame.h"

// application class
class Main : public wxApp
{
public:
    // function called at the application initialization
    virtual bool OnInit();
};

IMPLEMENT_APP(Main);

bool Main::OnInit()
{
    // create a new frame and set it as the top most application window
    MainFrame* mainFrame = new MainFrame();
    SetTopWindow(mainFrame);

    // show main frame
    mainFrame->Show();

    // enter the application's main loop
    return true;
}

Compiled with

/bin/sh -c '/usr/bin/make -j4 -e -f  Makefile'
----------Building project:[ wxTest - Debug ]----------
make[1]: Entering directory '/home/paul/Documents/Projects/wxTest'
/usr/bin/g++-10 -c  pch.h -I/usr/lib/x86_64-linux-gnu/wx/include/gtk3-unicode-3.1-unofficial3 -I/usr/include/wx-3.1-unofficial -D_FILE_OFFSET_BITS=64 -DWXUSINGDLL -D__WXGTK__ -pthread
pch.h:1:9: warning: #pragma once in main file
    1 | #pragma once
      |         ^~~~
make[1]: Leaving directory '/home/paul/Documents/Projects/wxTest'
make[1]: Entering directory '/home/paul/Documents/Projects/wxTest'
/usr/bin/g++-10 -include pch.h  -c  "/home/paul/Documents/Projects/wxTest/MainFrame.cpp" -g -O0 -pedantic -W -std=c++20 -fcoroutines -Wall -I/usr/lib/x86_64-linux-gnu/wx/include/gtk3-unicode-3.1-unofficial3 -I/usr/include/wx-3.1-unofficial -D_FILE_OFFSET_BITS=64 -DWXUSINGDLL -D__WXGTK__ -pthread  -o ../build-Debug/wxTest/MainFrame.cpp.o -I.
/usr/bin/g++-10 -include pch.h  -c  "/home/paul/Documents/Projects/wxTest/main.cpp" -g -O0 -pedantic -W -std=c++20 -fcoroutines -Wall -I/usr/lib/x86_64-linux-gnu/wx/include/gtk3-unicode-3.1-unofficial3 -I/usr/include/wx-3.1-unofficial -D_FILE_OFFSET_BITS=64 -DWXUSINGDLL -D__WXGTK__ -pthread  -o ../build-Debug/wxTest/main.cpp.o -I.
/home/paul/Documents/Projects/wxTest/main.cpp:13:20: warning: extra ';' [-Wpedantic]
   13 | IMPLEMENT_APP(Main);
      |                    ^
/home/paul/Documents/Projects/wxTest/MainFrame.cpp: In member function 'void MainFrame::onClickMenuItemClose(wxCommandEvent&)':
/home/paul/Documents/Projects/wxTest/MainFrame.cpp:63:54: warning: unused parameter 'evt' [-Wunused-parameter]
   63 | void MainFrame::onClickMenuItemClose(wxCommandEvent& evt)
      |                                      ~~~~~~~~~~~~~~~~^~~
/usr/bin/g++-10 -o ../build-Debug/bin/wxTest @../build-Debug/wxTest/ObjectsList.txt -L.  -lfmt  -L/usr/lib/x86_64-linux-gnu -pthread   -lwx_gtk3u_unofficial3_xrc-3.1 -lwx_gtk3u_unofficial3_html-3.1 -lwx_gtk3u_unofficial3_qa-3.1 -lwx_gtk3u_unofficial3_core-3.1 -lwx_baseu_unofficial3_xml-3.1 -lwx_baseu_unofficial3_net-3.1 -lwx_baseu_unofficial3-3.1
make[1]: Leaving directory '/home/paul/Documents/Projects/wxTest'
====0 errors, 3 warnings, total time: 00:00:18 seconds====

I get the following stacktrace when I click a button:

0  0x0000555555950050  ??    
1  0x0000555555566e85  MainFrame::onClickButton  /home/paul/Documents/Projects/wxTest/MainFrame.cpp  77
2  0x00007ffff758f091  wxEvtHandler::ProcessEventIfMatchesId  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../include/wx/app.h  462
3  0x00007ffff758f091  wxEvtHandler::ProcessEventIfMatchesId  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/event.cpp  1400
4  0x00007ffff758f52e  wxEvtHandler::SearchDynamicEventTable  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/event.cpp  1897
5  0x00007ffff758f8b4  wxEvtHandler::TryHereOnly  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/event.cpp  1618
6  0x00007ffff758f95f  wxEvtHandler::TryBeforeAndHere  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../include/wx/event.h  3927
7  0x00007ffff758f95f  wxEvtHandler::ProcessEventLocally  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/event.cpp  1555
8  0x00007ffff758fa61  wxEvtHandler::ProcessEvent  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/event.cpp  1528
9  0x00007ffff7590f8b  wxEvtHandler::SafelyProcessEvent  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/event.cpp  1646
10  0x00007ffff7be5db0  wxWindowBase::HandleWindowEvent  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../include/wx/window.h  852
11  0x00007ffff7a1ba02  wxgtk_button_clicked_callback  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/gtk/button.cpp  38
12  0x00007ffff62dba56  ??    
13  0x00007ffff62fab28  g_signal_emit_valist    
14  0x00007ffff62fb0d3  g_signal_emit    
15  0x00007ffff68f92ae  ??    
16  0x00007ffff68f9318  ??    
17  0x00007ffff62db802  g_closure_invoke    
18  0x00007ffff62ef962  ??    
19  0x00007ffff62fab9e  g_signal_emit_valist    
20  0x00007ffff62fb0d3  g_signal_emit    
21  0x00007ffff68f7754  ??    
22  0x00007ffff6ba8a71  ??    
23  0x00007ffff62dba56  ??    
24  0x00007ffff62fab28  g_signal_emit_valist    
25  0x00007ffff62fb0d3  g_signal_emit    
26  0x00007ffff69bff5c  ??    
27  0x00007ffff62dec56  g_cclosure_marshal_VOID__BOXEDv    
28  0x00007ffff62dba56  ??    
29  0x00007ffff62fab28  g_signal_emit_valist    
30  0x00007ffff62fb0d3  g_signal_emit    
31  0x00007ffff69bcfa2  ??    
32  0x00007ffff69be5eb  ??    
33  0x00007ffff69c15d6  ??    
34  0x00007ffff6988bb0  gtk_event_controller_handle_event    
35  0x00007ffff6b4b0fd  ??    
36  0x00007ffff6ba248b  ??    
37  0x00007ffff62db802  g_closure_invoke    
38  0x00007ffff62eef96  ??    
39  0x00007ffff62fa45d  g_signal_emit_valist    
40  0x00007ffff62fb0d3  g_signal_emit    
41  0x00007ffff6b4cbb3  ??    
42  0x00007ffff6a080b8  ??    
43  0x00007ffff6a0a36b  gtk_main_do_event    
44  0x00007ffff66f2f79  ??    
45  0x00007ffff6726106  ??    
46  0x00007ffff61effbd  g_main_context_dispatch    
47  0x00007ffff61f0240  ??    
48  0x00007ffff61f0533  g_main_loop_run    
49  0x00007ffff6a0930d  gtk_main    
50  0x00007ffff79b0e95  wxGUIEventLoop::DoRun  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/gtk/evtloop.cpp  64
51  0x00007ffff7445731  wxEventLoopBase::Run  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/evtloopcmn.cpp  90
52  0x00007ffff740fa2a  wxAppConsoleBase::MainLoop  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../src/common/appbase.cpp  380
53  0x00007ffff749a83a  wxEntry  /home/david/devel/packages/wx/3.1.4/wxwidgets3.1-3.1.4/objs_gtk_sh/../include/wx/app.h  462
54  0x000055555558c082  main  /home/paul/Documents/Projects/wxTest/main.cpp  13
55  0x00007ffff6f910b3  __libc_start_main  /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c  308
56  0x000055555556625e  _start    

Solution

  • It's difficult to see because there is too much code here (you should try to reduce your examples as much as possible, by removing all the unrelated parts), but the root cause of your problem is this line:

            btn->Connect(wxEVT_BUTTON, wxCommandEventHandler(MainFrame::onClickButton));
    

    Here you're connecting a method of MainFrame to a wxButton object, which ends up calling a class method on an object of a wrong class with the expectedly fatal results.

    You need to provide the frame pointer (i.e. this) as Connect() eventSink argument in order to call it on the right object. Alternatively, you could call this->Connect() as then the object being connected to and the object used for the call is the same.

    Finally, to ensure that you don't make such mistakes in the future, don't use Connect() at all, but use the type-safe Bind() instead. If you used it here, this call wouldn't have compiled, rather than crashed during run-time.