通过重载c++operator,实现一种轻松的wxWidgets界面编程风格,如html编写页面一样直观容易。
举一例,一个界面页有四块区,如果是开发html的话,是从头到脚一气书写
<div id='0'> <div id='1'> <input type="button" onclick="handler()"> div> <div id='2'> <input type="button" onclick="handler()"> div> <div id='3'> <input type="button" onclick="handler()"> div> <div id='4'> <input type="button" onclick="handler()"> div> div>
我的目标是,让c++开发的代码书写也一气呵成
Frame* frame = new Frame; layout::begin(new layout) (layout::begin(new layout) (new button) [ onclick = [] (event&) {} ] (layout::end) ) (layout::begin(new layout) (new button) [ onclick = [] (event&) {} ] (layout::end) ) (layout::begin(new layout) (new button) [ onclick = [] (event&) {} ] (layout::end) ) (layout::begin(new layout) (new button) [ onclick = [] (event&) {} ] (layout::end) ) (layout::end, [=] (layout& layout) { frame->SetLayout(layout); });
菜单布局编写直观自然,处理器函数绑定同时到位
Frame* frame = new Frame; menu::begin(new MenuBar) ("File", menu::begin(new Menu)
(ID_OPEN, "open", [=] (event&) { }) (ID_NEW, "new", [=] (event&) { }) (menu::end) ) ("About", menu::begin(new Menu)
(ID_HELP, "help", [=] (event&) { }) (menu::end) ) (menu::end, [=] (MenuBar* mb) { frame->SetMenuBar(mb); });
理想总是美好的,现在回到现实中的界面开发,让人眼痛的c++代码,
1 必须在多处代码操作,步骤烦多,分散在多处代码。
2 布局编写不符合人的思维-将整体拆分再拆分,而是从枝节末端创建一层一层往上添加挂钩。
MFC,WTL与wxWidgets的开发步骤类似,这里只关注wxWidgets,
1 在头文件中,自定义窗口类声明事件(窗口消息)分派函数;
2 在源文件中,添加事件(窗口消息)分派函数的实现宏;
3 添加一个事件(窗口消息)处理器函数,
3-1 在头文件中,声明为自定义窗口类的成员函数
3-2 在源文件中,实现该函数
3-3 在源文件中,在那个TABLE宏里添加事件id,处理器函数
4 在某个窗口容器的初始化回调中,创建事件id对应的子窗口/控件,设置相关的id
5 过了一段时间后,维护某个控件的处理器函数
5-1 在布局代码中找到这个控件,看到它相关的id
5-2 在源代码找到分派事件的窗口类,在它的TABLE宏里众多条目中找出对应项
5-3 最后才定位到目标处理器函数
做一样事情,却要在多处代码文件中跳转切换,只要漏了一步又会浪费时间调试一翻,实在烦人烦心。
// MyFrame.h enum { ID_SOME }; class MyFrame : public wxFrame { public: void hanlde_some_event(wxEvent&); private: wxDECLARE_EVENT_TABLE() }
// MyFrame.cpp wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_XXXX(ID_SOME, &MyFrame::handle_some_event) wxEND_EVENT_TABLE() void MyFrame::handle_some_event(wxEvent&) { // todo } MyFrame::MyFrame() { // .... this->Add(new wxSomeWindow(this, ID_SOME, ...); // .... }
下面是用zwx_helper重写官方layout sample布局的代码的对比
先是官方代码
layout.h https://github.com/wxWidgets/wxWidgets/blob/master/samples/layout/layout.h
layout.cpp https://github.com/wxWidgets/wxWidgets/blob/master/samples/layout/layout.cpp
// ---------------------------------------------------------------------------- // MyFrame // ---------------------------------------------------------------------------- wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(LAYOUT_ABOUT, MyFrame::OnAbout) EVT_MENU(LAYOUT_QUIT, MyFrame::OnQuit) EVT_MENU(LAYOUT_TEST_PROPORTIONS, MyFrame::TestProportions) EVT_MENU(LAYOUT_TEST_SIZER, MyFrame::TestFlexSizers) EVT_MENU(LAYOUT_TEST_NB_SIZER, MyFrame::TestNotebookSizers) EVT_MENU(LAYOUT_TEST_GB_SIZER, MyFrame::TestGridBagSizer) EVT_MENU(LAYOUT_TEST_SET_MINIMAL, MyFrame::TestSetMinimal) EVT_MENU(LAYOUT_TEST_NESTED, MyFrame::TestNested) EVT_MENU(LAYOUT_TEST_WRAP, MyFrame::TestWrap) wxEND_EVENT_TABLE() // Define my frame constructor MyFrame::MyFrame() : wxFrame(NULL, wxID_ANY, "wxWidgets Layout Demo") { SetIcon(wxICON(sample)); // Make a menubar wxMenu *file_menu = new wxMenu; file_menu->Append(LAYOUT_TEST_PROPORTIONS, "&Proportions demo...\tF1"); file_menu->Append(LAYOUT_TEST_SIZER, "Test wx&FlexSizer...\tF2"); file_menu->Append(LAYOUT_TEST_NB_SIZER, "Test ¬ebook sizers...\tF3"); file_menu->Append(LAYOUT_TEST_GB_SIZER, "Test &gridbag sizer...\tF4"); file_menu->Append(LAYOUT_TEST_SET_MINIMAL, "Test Set&ItemMinSize...\tF5"); file_menu->Append(LAYOUT_TEST_NESTED, "Test nested sizer in a wxPanel...\tF6"); file_menu->Append(LAYOUT_TEST_WRAP, "Test wrap sizers...\tF7"); file_menu->AppendSeparator(); file_menu->Append(LAYOUT_QUIT, "E&xit", "Quit program"); wxMenu *help_menu = new wxMenu; help_menu->Append(LAYOUT_ABOUT, "&About", "About layout demo..."); wxMenuBar *menu_bar = new wxMenuBar; menu_bar->Append(file_menu, "&File"); menu_bar->Append(help_menu, "&Help"); // Associate the menu bar with the frame SetMenuBar(menu_bar); #if wxUSE_STATUSBAR CreateStatusBar(2); SetStatusText("wxWidgets layout demo"); #endif // wxUSE_STATUSBAR wxPanel* p = new wxPanel(this, wxID_ANY); // we want to get a dialog that is stretchable because it // has a text ctrl in the middle. at the bottom, we have // two buttons which. wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL ); // 1) top: create wxStaticText with minimum size equal to its default size topsizer->Add( new wxStaticText( p, wxID_ANY, "An explanation (wxALIGN_RIGHT)." ), wxSizerFlags().Align(wxALIGN_RIGHT).Border(wxALL & ~wxBOTTOM, 5)); topsizer->Add( new wxStaticText( p, wxID_ANY, "An explanation (wxALIGN_LEFT)." ), wxSizerFlags().Align(wxALIGN_LEFT).Border(wxALL & ~wxBOTTOM, 5)); topsizer->Add( new wxStaticText( p, wxID_ANY, "An explanation (wxALIGN_CENTRE_HORIZONTAL)." ), wxSizerFlags().Align(wxALIGN_CENTRE_HORIZONTAL).Border(wxALL & ~wxBOTTOM, 5)); // 2) top: create wxTextCtrl with minimum size (100x60) topsizer->Add( new wxTextCtrl( p, wxID_ANY, "My text (wxEXPAND).", wxDefaultPosition, wxSize(100,60), wxTE_MULTILINE), wxSizerFlags(1).Expand().Border(wxALL, 5)); // 2.5) Gratuitous test of wxStaticBoxSizers wxBoxSizer *statsizer = new wxStaticBoxSizer( new wxStaticBox(p, wxID_ANY, "A wxStaticBoxSizer"), wxVERTICAL ); statsizer->Add( new wxStaticText(p, wxID_ANY, "And some TEXT inside it"), wxSizerFlags().Border(wxALL, 30)); topsizer->Add( statsizer, wxSizerFlags(1).Expand().Border(wxALL, 10)); // 2.7) And a test of wxGridSizer wxGridSizer *gridsizer = new wxGridSizer(2, 5, 5); gridsizer->Add(new wxStaticText(p, wxID_ANY, "Label"), wxSizerFlags().Align(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL)); gridsizer->Add(new wxTextCtrl(p, wxID_ANY, "Grid sizer demo"), wxSizerFlags(1).Align(wxGROW | wxALIGN_CENTER_VERTICAL)); gridsizer->Add(new wxStaticText(p, wxID_ANY, "Another label"), wxSizerFlags().Align(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL)); gridsizer->Add(new wxTextCtrl(p, wxID_ANY, "More text"), wxSizerFlags(1).Align(wxGROW | wxALIGN_CENTER_VERTICAL)); gridsizer->Add(new wxStaticText(p, wxID_ANY, "Final label"), wxSizerFlags().Align(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL)); gridsizer->Add(new wxTextCtrl(p, wxID_ANY, "And yet more text"), wxSizerFlags().Align(wxGROW | wxALIGN_CENTER_VERTICAL)); topsizer->Add( gridsizer, wxSizerFlags().Proportion(1).Expand().Border(wxALL, 10)); #if wxUSE_STATLINE // 3) middle: create wxStaticLine with minimum size (3x3) topsizer->Add( new wxStaticLine( p, wxID_ANY, wxDefaultPosition, wxSize(3,3), wxHORIZONTAL), wxSizerFlags().Expand()); #endif // wxUSE_STATLINE // 4) bottom: create two centred wxButtons wxBoxSizer *button_box = new wxBoxSizer( wxHORIZONTAL ); button_box->Add( new wxButton( p, wxID_ANY, "Two buttons in a box" ), wxSizerFlags().Border(wxALL, 7)); button_box->Add( new wxButton( p, wxID_ANY, "(wxCENTER)" ), wxSizerFlags().Border(wxALL, 7)); topsizer->Add(button_box, wxSizerFlags().Center()); p->SetSizer( topsizer ); // don't allow frame to get smaller than what the sizers tell it and also set // the initial size as calculated by the sizers topsizer->SetSizeHints( this ); }
最后是我的代码
std::shared_ptrsptr_delegate; bool MyApp::OnInit() { if (!wxApp::OnInit()) return false; MyFrame* frame = new MyFrame; sptr_delegate = std::shared_ptr (new MyFrameDelegate(frame)); MyFrameDelegate* delegate = sptr_delegate.get(); wxPanel* p = new wxPanel((wxWindow*)frame, wxID_ANY); wxMenuBar* mb = menu::begin(new wxMenuBar) ("&File", menu::begin(new wxMenu) (LAYOUT_TEST_PROPORTIONS, "&Proportions demo...\tF1", &MyFrame::TestMenuCommand, frame) (LAYOUT_TEST_SIZER, "Test wx&FlexSizer...\tF2", &MyFrame::TestMenuCommand, frame) (LAYOUT_TEST_NB_SIZER, "Test ¬ebook sizers...\tF3", &MyFrame::TestMenuCommand, frame) (LAYOUT_TEST_GB_SIZER, "Test &gridbag sizer...\tF4", frame, [=](wxCommandEvent& event) { frame->TestMenuCommand(event); }) (LAYOUT_TEST_SET_MINIMAL, "Test Set&ItemMinSize...\tF5") (LAYOUT_TEST_NESTED, "Test nested sizer in a wxPanel...\tF6", frame, [=](wxCommandEvent& event) { sptr_delegate->TestMenuCommand(event); })(LAYOUT_TEST_WRAP, "Test wrap sizers...\tF7") (menu::end)) ("&Help", menu::begin(new wxMenu) (LAYOUT_ABOUT, "&About", "About layout demo...") (menu::end)) ("level-tree", menu::begin(new wxMenu) (wxID_ANY, "level-1.1", menu::begin(new wxMenu) (wxID_ANY, "level-1.1-level-2.1") (wxID_ANY, "level-1.1-level-2.2") (menu::end)) (wxID_ANY, "level-1.2", menu::begin(new wxMenu) (wxID_ANY, "level-1.2-level-2.1") (wxID_ANY, "level-1.2-level-2.2") (menu::end)) (menu::end)) (menu::end, [=](wxMenuBar* mb) { frame->SetMenuBar(mb); }); layout::begin(new wxBoxSizer(wxVERTICAL)) // 1) top: create wxStaticText with minimum size equal to its default size (new wxStaticText( p, wxID_ANY, "An explanation (wxALIGN_RIGHT)." ), wxSizerFlags().Align(wxALIGN_RIGHT).Border(wxALL & ~wxBOTTOM, 5)) (new wxStaticText( p, wxID_ANY, "An explanation (wxALIGN_LEFT)." ), wxSizerFlags().Align(wxALIGN_LEFT).Border(wxALL & ~wxBOTTOM, 5)) (new wxStaticText( p, wxID_ANY, "An explanation (wxALIGN_CENTRE_HORIZONTAL)." ), wxSizerFlags().Align(wxALIGN_CENTRE_HORIZONTAL).Border(wxALL & ~wxBOTTOM, 5)) // 2) top: create wxTextCtrl with minimum size (100x60) (new wxTextCtrl( p, wxID_ANY, "My text (wxEXPAND).", wxDefaultPosition, wxSize(100,60), wxTE_MULTILINE), wxSizerFlags(1).Expand().Border(wxALL, 5)) // 2.5) Gratuitous test of wxStaticBoxSizers (layout::begin(new wxStaticBoxSizer(new wxStaticBox(p, wxID_ANY, "A wxStaticBoxSizer"), wxVERTICAL)) (new wxStaticText(p, wxID_ANY, "And some TEXT inside it"), wxSizerFlags().Border(wxALL, 30)) (layout::end), wxSizerFlags(1).Expand().Border(wxALL, 10)) // 2.7) And a test of wxGridSizer (layout::begin(new wxGridSizer(2, 5, 5)) (new wxStaticText(p, wxID_ANY, "Label"), wxSizerFlags().Align(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL)) (new wxTextCtrl(p, wxID_ANY, "Grid sizer demo"), wxSizerFlags(1).Align(wxGROW | wxALIGN_CENTER_VERTICAL)) (new wxStaticText(p, wxID_ANY, "Another label"), wxSizerFlags().Align(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL)) (new wxTextCtrl(p, wxID_ANY, "More text"), wxSizerFlags(1).Align(wxGROW | wxALIGN_CENTER_VERTICAL)) (new wxStaticText(p, wxID_ANY, "Final label"), wxSizerFlags().Align(wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL)) (new wxTextCtrl(p, wxID_ANY, "And yet more text"), wxSizerFlags().Align(wxGROW | wxALIGN_CENTER_VERTICAL)) (layout::end), wxSizerFlags().Proportion(1).Expand().Border(wxALL, 10)) #if wxUSE_STATLINE // 3) middle: create wxStaticLine with minimum size (3x3) (new wxStaticLine( p, wxID_ANY, wxDefaultPosition, wxSize(3,3), wxHORIZONTAL), wxSizerFlags().Expand()) #endif // wxUSE_STATLINE // 4) bottom: create two centred wxButtons (layout::begin(new wxBoxSizer( wxHORIZONTAL )) (new wxButton( p, wxID_ANY, "Two buttons in a box" ), wxSizerFlags().Border(wxALL, 7))[onclick = [=](wxCommandEvent& e) { delegate->OnClick(e); } ] (new wxButton( p, wxID_ANY, "(wxCENTER)" ), wxSizerFlags().Border(wxALL, 7)) (layout::end), wxSizerFlags().Center()) (layout::end, [=](wxSizer* s) { p->SetSizer(s); s->SetSizeHints(frame); }); frame->Show(true); return true; }
zwx_helper将会在https://github.com/bbqz007/zhelper-wxWidgets/发布。