wxGridSizerを使うとコントロールをタイル状に並べられる。
#include <wx/wx.h> #include <wx/sizer.h> #include <wx/frame.h> #include <wx/panel.h> class MyFrame : public wxFrame { public:
MyFrame() : wxFrame(NULL, wxID_ANY, "wxGridSizer サンプル", wxDefaultPosition, wxSize(400, 300)) { int row = 3; // 行数 int col = 4; // 列数 int vgap = 2;// 垂直方向の間隔 int hgap = 2;// 水平方向の間隔 wxGridSizer* gridSizer = new wxGridSizer(row, col, vgap, hgap); for (int i = 0; i < row*col; ++i) { // 新しいパネルを作成 wxPanel* panel = new wxPanel(this/*MyFrameで管理*/, wxID_ANY); // パネルの背景色を設定 panel->SetBackgroundColour(wxColour(100, 100, 100 * (i + 1) % 256)); // パネルをgridSizer追加 gridSizer->Add(panel, 1, wxEXPAND); // パネルにイベントハンドラを設定 panel->Bind(wxEVT_LEFT_DOWN, &MyFrame::OnClick, this); } // MyFrameにgridSizerを指定 SetSizer(gridSizer); Centre();// ウィンドウを画面中央に表示 }
// wxPanelをクリックしたときのイベントハンドラ void OnClick(wxMouseEvent& event) { wxPanel* panel = dynamic_cast<wxPanel*>(event.GetEventObject()); if (panel) { wxColour color = panel->GetBackgroundColour(); wxLogMessage("%d", color.Blue()); } }
}; class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame(); frame->Show(true); return true; } }; wxIMPLEMENT_APP(MyApp);
// wxPanelをクリックしたときのイベントハンドラ void OnClick(wxMouseEvent& event) { wxGridSizer * gridSizer = dynamic_cast<wxGridSizer*>(this->GetSizer()); // gridSizerの全ての要素へアクセスするfor文 for (int i = 0; i < gridSizer->GetItemCount(); ++i) { wxSizerItemList& item = gridSizer->GetChildren(); wxWindow* panel = item.Item(i)->GetData()->GetWindow(); panel->SetBackgroundColour(wxColour(255, 0, 0));// パネルを着色 panel->Refresh(); // 再描画指示 panel->Update(); // 再描画を即座に実行 } }
まず、MyFrameのコンストラクタでメニューを作成して、OnClickにBindする。
// 画面上部にメニューバーを作成 wxMenuBar* menuBar = new wxMenuBar(); wxMenu* menu = new wxMenu(); menu->Append(wxID_DELETE, "削除"); menuBar->Append(menu, "メニュー"); SetMenuBar(menuBar); Bind(wxEVT_MENU, &MyFrame::OnClick, this, wxID_DELETE);
Sizerの削除は要素の管理関係が厄介。管理自体は親(MyFrame)がしているのだが、Sizerを外したりdeleteしたりすると一緒に削除されたり、削除されたことが親に知らされずに不正なアクセスになったりする。
まずはGetChildren()でSizerでサイズ管理しているアイテム一覧を取り出し、個別に削除してからSizerを削除することになる。
// 「削除」メニューをクリックしたときのイベントハンドラ void OnClick(wxCommandEvent& event) { wxGridSizer* gridSizer = dynamic_cast<wxGridSizer*>(this->GetSizer()); if (gridSizer) { // gridSizer->Clear(true); // gridSizerから全ての要素を削除する // trueを指定した場合、全ての子要素は削除されるが、親がそれを知らないので後で知らせる必要がある // falseを指定した場合、子要素は削除されないので、後で削除する必要がある // 使いにくいので今回は使用しない // gridSizerから全てのウィジェットを取り出し、破棄する const wxSizerItemList list = gridSizer->GetChildren(); for (wxSizerItem* item : list) { wxWindow* window = item->GetWindow();// パネルを取得 if (window) { window->Destroy();// パネルを削除 delete ではなくDestroy()を呼び出すべき } } // MyFrameからgridSizerを取り外す // 第二引数がtrueのときは、現在のSizerを削除する // 第二引数がfalseのときは、削除しない this->SetSizer(nullptr,true); } }
パネルだけをほかに移行するような場合、パネルのポインタを保存しておき、MyFrame側のSetSizerの第二引数にfalseを渡すと、gridSizerが勝手に削除されなくなるので、後で自分でgridSizerだけを削除する。
// 「削除」メニューをクリックしたときのイベントハンドラ void OnClick(wxCommandEvent& event) { wxGridSizer* gridSizer = dynamic_cast<wxGridSizer*>(this->GetSizer()); if (gridSizer) { std::vector<wxWindow*> panels; // パネルのバックアップ先 // gridSizerから全てのウィジェットを取り出し、破棄する const wxSizerItemList list = gridSizer->GetChildren(); for (wxSizerItem* item : list) { wxWindow* window = item->GetWindow(); if (window) { panels.push_back(window);// パネルをバックアップ //window->Destroy(); // 削除はしないようにする } } // 第二引数がfalseのときは、現在のSizerを削除しない this->SetSizer(nullptr,false); }
}