如何在事件 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 会处理它。
这里的关键步骤是:
- 继承
wxClientData
class附加您的数据
- 使用堆分配的自定义数据对象调用
wxEvtHandler::SetClientObject()
- 在回调中,使用
wxCommandEvent::GetEventObject()
检索按钮
- 使用
wxEvtHandler::GetClientObject()
检索您的自定义数据
情况
我目前正在使用 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 会处理它。
这里的关键步骤是:
- 继承
wxClientData
class附加您的数据 - 使用堆分配的自定义数据对象调用
wxEvtHandler::SetClientObject()
- 在回调中,使用
wxCommandEvent::GetEventObject()
检索按钮
- 使用
wxEvtHandler::GetClientObject()
检索您的自定义数据