zwx_helper

通過重載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&) {  })
                    (new Menu(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 //github.com/wxWidgets/wxWidgets/blob/master/samples/layout/layout.h

layout.cpp //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 &notebook 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_ptr<MyFrameDelegate> sptr_delegate;

bool MyApp::OnInit()
{
    if (!wxApp::OnInit())
        return false;
    
    MyFrame* frame = new MyFrame;
    sptr_delegate = std::shared_ptr<MyFrameDelegate>(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 &notebook 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將會在//github.com/bbqz007/zhelper-wxWidgets/發布。