如何在事件 wxWidgets 中传递自定义数据

How to pass custom data in event wxWidgets

情况

我目前正在使用 wxPython(wxWidgets for Python)编写应用程序。

在这个应用程序中显示了一个对话列表,每行末尾都有一个 "Open conversation" 按钮。我们称之为 window "All conversations".

单击任何一个按钮都会调用函数 "open_conversation(self, event)",它会显示完整的对话。

我想做的是,在 window "All conversations" 的生成时,在每个 "Bind" 事件或每个 "wxButton" 对象中传递一些自定义数据,例如字符串(例如联系人的姓名);之后,当单击按钮时,它将在 "event" 参数中传递所述数据,在函数中可以使用 contact = event.CustomData["recipient"].

之类的东西访问

问题

参考documentation,我找到了"Bind"方法的"userData"关键字,但在Python中似乎不起作用。

self.openConversation_button.Bind(wx.EVT_BUTTON, self.open_conversation, userData="test")
TypeError: _EvtHandler_Bind() got an unexpected keyword argument 'userData'

更新

我已将 Rolf of Saxony 的解决方案标记为正确,因为它最符合我的需要,但我一般建议使用 VZ 建议的自定义事件系统。

定义自定义事件的建议绝对是正确的做法,一般来说,但如果您只需要一个字符串 and/or 数字,您也可以重新利用现有的 wxCommandEvent class 并使用其 SetString()SetInt() 方法。

将自定义数据传递到 callback 事件的一种简单方法是使用 lambda 函数。
例如

import wx

class ButtonFrame(wx.Frame):
    def __init__(self, value):
        wx.Frame.__init__(self,None)
        self.btn1 = wx.Button(self, -1, ("a"))
        self.btn2 = wx.Button(self, -1, ("b"))
        self.btn3 = wx.Button(self, -1, ("No flag"))
        self.btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.btnSizer.Add(self.btn1 , 0, wx.RIGHT, 10)
        self.btnSizer.Add(self.btn2 , 0, wx.RIGHT, 10)
        self.btnSizer.Add(self.btn3 , 0, wx.RIGHT, 10)
        self.btn1.Bind(wx.EVT_BUTTON,  lambda event: self.OnButton(event, flag="A"))
        self.btn2.Bind(wx.EVT_BUTTON,  lambda event: self.OnButton(event, flag="B"))
        self.btn3.Bind(wx.EVT_BUTTON,  self.OnButton)
        self.SetSizer(self.btnSizer)
        self.Show()

    def OnButton(self,event, flag=None):
        if flag:
            print ("Button pressed was "+flag)
        else:
            print ("A button got pressed")

if __name__ == "__main__":
    app = wx.App()
    ButtonFrame(None)
    app.MainLoop()

对于任何对完整的 C++ 示例感兴趣的人,这里是:

#include <wx/wx.h>

struct CustomData final : public wxClientData {
    std::string myString;
};

struct MyApp final : public wxApp {
    bool OnInit() final;
};

struct MyWindow final : public wxFrame {
    static constexpr int ID_MY_BUTTON{ 42 };

                MyWindow();
                MyWindow( MyWindow const & ) = delete;
    MyWindow &  operator=( MyWindow const & ) = delete;
    void        OnButton( wxCommandEvent & event );

    wxDECLARE_EVENT_TABLE();
};

bool MyApp::OnInit() {
    MyWindow * const window{ new MyWindow() };
    window->Show( true );
    return true;
}

MyWindow::MyWindow() {
    wxButton * const myButton{ new wxButton( this, ID_MY_BUTTON ) };
    CustomData * const myData{ new CustomData() };
    myData->myString = "Hello World!";
    myButton->SetClientObject( myData );
}

void MyWindow::OnButton( wxCommandEvent & event ) {
    wxEvtHandler const * const eventObject{ static_cast< wxEvtHandler * >( event.GetEventObject() ) };
    if ( eventObject == nullptr ) {
        return;
    }

    CustomData const * const myData{ static_cast< CustomData const * >( eventObject->GetClientObject() ) };
    if ( myData == nullptr ) {
        return;
    }

    std::cout << myData << "\n";
}

wxIMPLEMENT_APP( MyApp );

wxBEGIN_EVENT_TABLE( MyWindow, wxFrame )
EVT_BUTTON( MyWindow::ID_MY_BUTTON, MyWindow::OnButton )
wxEND_EVENT_TABLE()

无需删除 CustomData 实例,wxWidgets 会处理它。 这里的关键步骤是:

  • 继承wxClientDataclass附加您的数据
  • 使用堆分配的自定义数据对象调用 wxEvtHandler::SetClientObject()
  • 在回调中,使用 wxCommandEvent::GetEventObject()
  • 检索按钮
  • 使用 wxEvtHandler::GetClientObject()
  • 检索您的自定义数据