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
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.