wxStaticText::SetLabel() 分段错误

wxStaticText::SetLabel() segmentation fault

我是 wxWidgets 的新手,我想做一个简单的 GUI 应用程序(ubuntu)以供参考。

我创建了一个简单的 window,添加了一些按钮,并且在按钮事件处理程序中我想更新屏幕上 wxStaticText 对象的文本以指示按下了哪个按钮。

然而,当我调用 wxStaticText 继承的 SetLabelSetLabelText 函数时,我遇到了分段错误。

也许我遗漏了什么,wxStaticText 无法设置标签,我需要另一个控件。但是我在 class 列表中找不到类似的东西。

我正在使用来自 codelite 存储库的 wxWidgets 3.1.4。

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;
}

编译为

/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====

单击按钮时我得到以下堆栈跟踪:

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    

很难看清,因为这里的代码太多(你应该尽量减少你的例子,删除所有不相关的部分),但问题的根本原因是这一行:

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

在这里,您将 MainFrame 的方法连接到 wxButton 对象,最终会在错误的 class 对象上调用 class 方法预期的致命结果。

您需要提供帧指针(即 this)作为 Connect() eventSink 参数,以便在正确的对象上调用它。或者,您可以调用 this->Connect(),因为连接到的对象和用于调用的对象是相同的。

最后,为了保证以后不再犯这样的错误,完全不要使用Connect(),而是使用类型安全的Bind()。如果你在这里使用它,这个调用将不会编译,而不是在 运行 时间期间崩溃。