I am trying to create a wxPanel to paint lines and points on it.
My program consists of the following windows:
MyFrame : the default frame class which inherits from wxFrame
Final_Layout : an instance of wxPanel with parent MyFrame (i.e. this pointer) , and it contains all of my controls
Now, I am trying to create a new panel, Draw_Panel, that I can draw on it. I would like to append this panel besides Final_Layout using wxBoxSizer.
So, my first step is in initilizing the panel, I have the following code:
void MyFrame::Initialize_Draw_Input() {
Draw_Panel = new wxPanel(this, -1); // QUESTION : Who is the parent here? Should it be Final_Layout or "this" pointer?
Draw_Panel ->Connect(wxEVT_PAINT, wxPaintEventHandler(MyFrame::OnPaint));
}
void MyFrame::OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(Draw_Panel );
wxCoord x1 = 50, y1 = 60;
wxCoord x2 = 100, y2 = 60;
dc.DrawLine(x1, y1, x2, y2);
}
However, this does not work. I think I am misunderstanding how wxPanels work... I would appreciate any help / guidance
Edit:
Based on the help below (which I greatly appreciate), I made the following update to the code. However, now I cannot draw on the panel:
void MyFrame::Initialize_Draw_Input() {
Draw_Panel = new wxPanel(Final_Panel, wxID_ANY,wxDefaultPosition,wxSize(1000,1000),wxTAB_TRAVERSAL,"");
Draw_StaticBox = new wxStaticBoxSizer(wxHORIZONTAL, Draw_Panel, _T("Cross Section"));
Draw_Sizer = new wxBoxSizer(wxHORIZONTAL);
// Draw_Panel->Connect(wxEVT_PAINT, wxPaintEventHandler(MyFrame::OnPaint), NULL, this);
wxStaticText* x = new wxStaticText(Draw_Panel,wxID_ANY,"");
Draw_Sizer->Add(x);
Draw_StaticBox->Add(Draw_Sizer,wxSizerFlags().Expand());
Draw_Panel->SetSizer(Draw_StaticBox);
PaintNow();
}
void MyFrame::PaintNow() {
wxClientDC dc(Draw_Panel);
Render(dc);
}
void MyFrame::Render(wxDC& dc) {
wxCoord x1 = 0, y1 = 60;
wxCoord x2 = 100, y2 = 60;
dc.DrawLine(x1, y1, x2, y2);
}
My constructor:
MyFrame::MyFrame() : wxFrame(NULL, wxID_ANY, "Cross Sectional Properties", wxDefaultPosition, wxSize(1500, 600), wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX)) {
wxBoxSizer* Element_and_Node_hSizer = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* Info_and_Data_Input_vSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* Output_and_Draw_vSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* Main_hSizer = new wxBoxSizer(wxHORIZONTAL);
Final_Panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T(""));
Initialize_ProjectInfo_Input(); //Info_Panel, a wxFrame instance is defined here. It contains a wxGrid and other controls for project info input.
Initialize_Node_Input(); //Node_Panel, a wxFrame instance is defined here. It contains a wxGrid control for node data input.
Initialize_Element_Input(); //Element_Panel, a wxFrame instance is defined here. It contains a wxGrid control for element data input.
Initialize_Output(); //Out_Panel, a wxFrame instance defined here. It contians a wxGrid for cross sectional properties output.
Initialize_Draw_Input();
InitMenu(); //Initializes the menu.
//Combines the Node and element input wxGrid into one sizer - horizontally
Element_and_Node_hSizer->Add(Node_Panel, wxSizerFlags(1).Expand().Border(wxBOTTOM | wxLEFT |wxRIGHT));
Element_and_Node_hSizer->Add(Element_Panel, wxSizerFlags(1).Expand().Border(wxBOTTOM | wxRIGHT));
//Combines the project input and the (Node & Elemetn Input) into one sizer - vertically
Info_and_Data_Input_vSizer->Add(Info_Panel, wxSizerFlags(1).Expand().Border(wxALL));
Info_and_Data_Input_vSizer->Add(Element_and_Node_hSizer,wxSizerFlags(1).Expand());
//Combines output data and draw panel into one sizer - vertically
Output_and_Draw_vSizer->Add(Out_Panel,wxSizerFlags(1).Border(wxALL));
Output_and_Draw_vSizer->Add(Draw_Panel, wxSizerFlags(1).Expand().Border(wxLEFT| wxBOTTOM | wxRIGHT));
//Combines all into the final layout
Main_hSizer->Add(Info_and_Data_Input_vSizer);
Main_hSizer->Add(Output_and_Draw_vSizer);
SetSizer(Main_hSizer);
PaintNow();
}
Also unrelated, would anyone know how to center a wxGrid? the "Coordiates of Nodes" section does not look good.
There are multiple issues here.
wxID_ANY
instead of using -1 in the line Draw_Panel = new wxPanel(this, -1);.
That way in the very, very unlikely event that wxID_ANY is changed to something else, your code will continue to work.
Draw_Panel ->Connect(wxEVT_PAINT, wxPaintEventHandler(MyFrame::OnPaint));
you need to supply 2 more parameters: userData (which will almost always be NULL
) and eventSink (which will almost always be this
). So the line should look like so:
Draw_Panel ->Connect(wxEVT_PAINT, wxPaintEventHandler(MyFrame::OnPaint),NULL,this);
MyFrame::MyFrame...
Info_Panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T(""));
Initialize_Project_Info();
Initialize_Draw_Input();
}
I'm assuming Final_Layout is what was called Info_Panel in previous questions. However, you might notice this causes the 2 panels to be drawn on top of each other. To get around this, add 1 more sizer to MyFrame
MyFrame::MyFrame....
wxBoxSizer* MainSizer = new wxBoxSizer(wxHORIZONTAL);
Info_Panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T(""));
Initialize_Project_Info();
Initialize_Draw_Input();
Info_Panel->Fit();
MainSizer->Add(Info_Panel,wxSizerFlags(0));
MainSizer->Add(Draw_Panel,wxSizerFlags(1).Expand());
SetSizer(MainSizer);
}
This produces this layout:
framePanel
, but it can be called anything you want. Then framePanel
will be the only child of your frame and the other 2 panels will be children of framePanel
.So the new constructor could look like this:
MyFrame::MyFrame
wxBoxSizer* MainSizer = new wxBoxSizer(wxHORIZONTAL);
framePanel = new wxPanel(this, wxID_ANY);
Info_Panel = new wxPanel(framePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T(""));
Initialize_Project_Info();
Initialize_Draw_Input();
Info_Panel->Fit();
MainSizer->Add(Info_Panel,wxSizerFlags(0));
MainSizer->Add(Draw_Panel,wxSizerFlags(1).Expand());
framePanel->SetSizer(MainSizer);
}
And you should also change the defition for Draw_Panel
to have the new parent like this:
Draw_Panel = new wxPanel(framePanel, wxID_ANY);
Finnally this gives this layout:
This is why the answer to your previous question included the part
While you can place controls directly in a wxFrame, because of 'TAB traversal' and a OS's typical background features you better use a wxPanel.
void MyFrame::Initialize_Draw_Input() {
Draw_StaticBox = new wxStaticBoxSizer(wxHORIZONTAL, Final_Panel, _T("Cross Section"));
Draw_Panel = new wxPanel(Draw_StaticBox->GetStaticBox(), wxID_ANY,wxDefaultPosition,wxSize(1000,1000),wxTAB_TRAVERSAL,"");
Draw_Sizer = new wxBoxSizer(wxHORIZONTAL);
Draw_Panel->Connect(wxEVT_PAINT, wxPaintEventHandler(MyFrame::OnPaint), NULL, this);
wxStaticText* x = new wxStaticText(Draw_StaticBox->GetStaticBox(),wxID_ANY,"");
Draw_Sizer->Add(x);
Draw_StaticBox->Add(Draw_Panel,wxSizerFlags(1).Expand());
Draw_StaticBox->Add(Draw_Sizer,wxSizerFlags().Expand());
}
Right now neither Draw_Sizer
nor the empty static text x
seem serve any purpose, but I assume they're place holders for something that will come later.
Finally, you need to change the constructor to add Draw_StaticBox
to Output_and_Draw_vSizer
instead of Draw_Panel
. ie change
Output_and_Draw_vSizer->Add(Draw_Panel, wxSizerFlags(1).Expand().Border(wxLEFT| wxBOTTOM | wxRIGHT));
to
Output_and_Draw_vSizer->Add(Draw_StaticBox, wxSizerFlags(1).Expand().Border(wxLEFT| wxBOTTOM | wxRIGHT));