Interaction between wxPanel and wxFrame - c++

I am trying to understand how wxFrame and wxPanel work together. I am having a hard time explaining the following code behavior:
I am trying to set up the GUI for my application. I have a function that creates a form for data entry:
void MyFrame::Initialize_Project_Info() {
//Info_Panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(600, 1000), wxTAB_TRAVERSAL, _T(""));
//Info_Panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T(""));
Info_Box = new wxBoxSizer(wxHORIZONTAL);
Info_Staticbox = new wxStaticBoxSizer(wxHORIZONTAL, Info_Panel, _T("Project Information"));
Info_Grid = new wxFlexGridSizer(4, 2, 0, 0);
//Note wsSize(width,height).
Project_Name = new wxStaticText(Info_Panel, wxID_ANY, _T("Project:"));
Engineer_Name = new wxStaticText(Info_Panel, wxID_ANY, _T("Engineer:"));
CrossSection_Name = new wxStaticText(Info_Panel, wxID_ANY, _T("Cross Section ID:"));
Additional_Notes = new wxStaticText(Info_Panel, wxID_ANY, _T("Additional Notes:"));
Enter_PN = new wxTextCtrl(Info_Panel, ENTER_PN, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB, wxDefaultValidator, _T(""));
Enter_EN = new wxTextCtrl(Info_Panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB, wxDefaultValidator, _T(""));
Enter_CSN = new wxTextCtrl(Info_Panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB, wxDefaultValidator, _T(""));
Enter_AN = new wxTextCtrl(Info_Panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, 100), wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxTE_MULTILINE, wxDefaultValidator, _T(""));
Info_Grid->Add(Project_Name,
wxSizerFlags().Align(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL)
.Border(wxTOP | wxBOTTOM | wxLEFT));
Info_Grid->Add(Enter_PN, wxSizerFlags().Expand().Border(wxALL));
Info_Grid->Add(Engineer_Name,
wxSizerFlags().Align(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL)
.Border(wxBOTTOM | wxLEFT));
Info_Grid->Add(Enter_EN, wxSizerFlags().Expand().Border(wxBOTTOM | wxLEFT | wxRIGHT));
Info_Grid->Add(CrossSection_Name,
wxSizerFlags().Align(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL)
.Border(wxBOTTOM | wxLEFT));
Info_Grid->Add(Enter_CSN,
wxSizerFlags().Expand().Border(wxBOTTOM | wxLEFT | wxRIGHT));
Info_Grid->Add(Additional_Notes,
wxSizerFlags().Align(wxALIGN_LEFT).Border(wxBOTTOM | wxLEFT));
Info_Grid->Add(Enter_AN, wxSizerFlags().Expand().Border(wxBOTTOM | wxLEFT | wxRIGHT));
Info_Staticbox->Add(Info_Grid);
Info_Box->Add(Info_Staticbox, wxSizerFlags().Border(wxALL));
Info_Panel->SetSizer(Info_Box);
}
This code works if I append all the objects to a wxPanel when only defined in the scope of the function. If I define the wxPanel in the scope of the constructor of the wxFrame derived class, it does not work.
For example, doing this does not generate the form as intended:
MyFrame::MyFrame() : wxFrame(NULL,wxID_ANY,"Cross Sectional Beam Properties",wxDefaultPosition,wxSize(1200,600),wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX)) {
Info_Panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T(""));
Initialize_Project_Info();
}
So, my question is how does wxPanel interface with wxFrame. Why would one moving one line of code (i.e. wxPanel initialization) change the outcome?

From the wx docs:
A frame is a window whose size and position can (usually) be changed
by the user. It usually has thick borders and a title bar, and can
optionally contain a menu bar, toolbar and status bar.
A panel is a window on which controls are placed. It is usually
placed within a frame. Its main feature over its parent class wxWindow
is code for handling child windows and TAB traversal.
wxWindow is a generic class used by any type of window. wxDialog is mainly used as a form where the user enters data.
Usually an application shows a frame as the main window of the app.
==> 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.
This panel is a child of the main wxFrame and you can buid it at wxFrame's ctor.
Because in wxWidgets if a wxFrame has an unique child then this child is expanded to fit the whole client size of the wxFrame, then you don't use wxsizers for this panel-to-frame fitting.
When you create the controls (e.g. at wxPanel ctor) you set this wxPanel as the parent of the controls. And use sizers to layout them into the panel.
So each control has your panel as parent; an your panel has the frame as parent. Don't forget that 'this' pointers may be used as parents, or pass the parent as a parameter in the function that creates the windows (panel, controls).
Finally, the pointers to any wxWindow-derived class are created with "new", but you don't need to delete them, wxWigets internals takes care of their deletion.

Related

Why does the second wxSizer fail to center the button?

I created a global panel and called a method that creates a sizer and a button. The button clears the sizer (i.e., also the panel), and then deletes is. Then, another method is called, using the same logic, it creates another sizer and another button. This time they don't work.
my code(windows, vs studio):
#include "MainFrame.h"
#include <wx/wx.h>s
MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title)
{
panel = new wxPanel(this);
StartParty(panel);
}
void MainFrame::ClearButtonClicked(wxCommandEvent& evt)
{
panel->GetSizer()->Clear(true);
panel->SetSizerAndFit(nullptr);
ChooseMode(panel);
}
void MainFrame::StartParty(wxPanel* parent)
{
wxButton* start_button = new wxButton(parent, wxID_ANY, "Start the Party!", wxDefaultPosition, wxSize(200, 70));
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->AddStretchSpacer(1);
sizer->Add(
start_button,
0,
wxALL | wxALIGN_CENTER,
0);
sizer->AddStretchSpacer(1);
parent->SetSizerAndFit(sizer);
start_button->Bind(wxEVT_BUTTON, &MainFrame::ClearButtonClicked, this);
}
void MainFrame::ChooseMode(wxPanel* parent)
{
wxButton* select_button = new wxButton(parent, wxID_ANY, "Choose", wxDefaultPosition, wxSize(200, 70));
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->AddStretchSpacer(1);
sizer->Add(
select_button,
0,
wxALL | wxALIGN_CENTER,
0);
sizer->AddStretchSpacer(1);
parent->SetSizer(sizer);
}
You do need Layout(), as mentioned in the comments, as this is what actually repositions the windows -- SetSizer() just specifies the sizer to use for doing it, but doesn't do anything on its own immediately (it will when the window is resized the next time, as this results in a call to Layout()).
However, even if it's somewhat unrelated to the question itself, I think you shouldn't be doing this at all and use wxSimplebook instead. This simple (sic) class allows you to add a few pages to it and then easily switch between them.

How to resize frame after hiding a component in wxwidgets

I have a frame which has 4 textboxes and one link(change no of textboxes). when user clicks on link I am hiding 4th textbox. but what happens is after hiding 4th textbox, it's space remains blank. I don't want to keep its space blank. That space should disappear and frame should get resized. Is there any way that I can achieve this? when user again clicks on the link(change no of textboxes I am again displaying 4th textbox.
code:
m_ComputerNameText1 = new wxTextCtrl(m_panel, wxID_ANY, "computerName", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_ComputerNameText2 = new wxTextCtrl(m_panel, wxID_ANY, "computerName1", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_ComputerNameText3 = new wxTextCtrl(m_panel, wxID_ANY, "computerName3", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_ComputerNameText4 = new wxTextCtrl(m_panel, wxID_ANY, "computerName4", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_hyperLinkOption = new wxHyperlinkCtrl(m_panel, wxID_ANY, "Change no of textboxes", wxT(""), wxDefaultPosition, wxSize(135, wxDefaultSize.GetHeight()));
m_userPassSizer->Add(m_ComputerNameText1, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_ComputerNameText2, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_ComputerNameText3, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_ComputerNameText4, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_hyperLinkOption , 0, wxALIGN_LEFT | wxALL, 0);
Following function executes when hyperlink is clicked.
void OnAuthCodeOptionLinkClicked(wxHyperlinkEvent& event)
{
if (!m_hyperlinkOptionSelected)
{
m_hyperlinkOptionSelected= true;
m_ComputerNameText4->Hide();
m_hyperLinkOption->SetLabel("Go back");
}
else
{
m_hyperlinkOptionSelected= false;
m_ComputerNameText4->Show();
m_hyperLinkOption->SetLabel("change no of textboxes");
}
}
in this way I have to hide and show 4th textbox. After hiding some blank space remains. How can I avaoid that blank space and take link in the place of 4th textbox ?
Whenever I need to dynamically update the size of a control, I always call Layout() and Refresh().
You can find more info about Layout here:
http://docs.wxwidgets.org/3.1/classwx_window.html#a1b143c3e72bd0af533b76db4830a6113
As well as some good info on window sizing in general here:
http://docs.wxwidgets.org/3.1/overview_windowsizing.html
Calling Layout assumes that your class where you construct and manage your textboxes, etc. is a wxWindow derived control itself (like a panel, frame, whatever). If not, like you are making the changes on a view-model, you will need to call Layout and Refresh on the owning window:
void UpdateView()
{
view->Layout();
view->Refresh();
}
You may need to play around with your sizers, minimum size of windows, etc. to get the exact behavior you want.

How to set the size of a toolbar button

I am trying to make a toolbar with some radio buttons. The Problem is that when I add a button in the toolbar, the button is too big,bigger than the height of the toolbar. I have already tried to set the size of the bitmap with not any result. However there is not any function to set the size of the tool itself. Is there any way to make it?
this->m_ToolBar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxNO_BORDER | wxTB_TEXT | wxTB_NOICONS);
this->m_ToolBar->AddRadioTool(this->LastToolBarId, Name, wxNullBitmap, wxNullBitmap, toolTip, wxT(""), Data);
this->m_ToolBar->ToggleTool (this->LastToolBarId, true);
this->m_ToolBar->Realize();
Ok I found it. I didn't know that there is a white bitmap(16x15) over the text of the tool. So the answer is quite simple. I use SetToolBitmapSize with size (1,1)
this->m_ToolBar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxNO_BORDER | wxTB_TEXT | wxTB_NOICONS);
this->m_ToolBar->SetToolBitmapSize(wxSize(1,1));
this->m_ToolBar->AddRadioTool(this->LastToolBarId, Name, wxNullBitmap, wxNullBitmap, toolTip, wxT(""), Data);
this->m_ToolBar->ToggleTool (this->LastToolBarId, true);
this->m_ToolBar->Realize();

Enter closes wxWidgets application when wxPanel added

The wxWidgets hello world tutorial exhibits a strange behavior. As soon as I add a panel to the application using the following line to the MyFrame constructor:
wxPanel *panel = new wxPanel(this);
The [Return] and [Keypad Enter] keys cause the program to exit (Clean Close event detected).
Why is this? Without the wxPanel, the keys do nothing.
Here is the code with the added line:
// hworld.cpp
// Version using dynamic event routing
#include <wx/wx.h>
class MyApp : public wxApp
{
virtual bool OnInit();
};
IMPLEMENT_APP(MyApp)
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
};
enum
{
ID_Quit=1,
ID_About
};
bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame( _("Hello World"), wxPoint(50, 50),
wxSize(450, 350));
frame->Connect( ID_Quit, wxEVT_COMMAND_MENU_SELECTED,
(wxObjectEventFunction) &MyFrame::OnQuit );
frame->Connect( ID_About, wxEVT_COMMAND_MENU_SELECTED,
(wxObjectEventFunction) &MyFrame::OnAbout );
frame->Show(true);
SetTopWindow(frame);
return true;
}
MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame( NULL, -1, title, pos, size )
{
wxMenuBar *menuBar = new wxMenuBar;
wxMenu *menuFile = new wxMenu;
menuFile->Append( ID_About, _("&About...") );
menuFile->AppendSeparator();
menuFile->Append( ID_Quit, _("E&xit") );
menuBar->Append(menuFile, _("&File") );
// Added line causing failure.
wxPanel *panel = new wxPanel(this);
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText( _("Welcome to wxWidgets!") );
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox( _("wxWidgets Hello World example."),
_("About Hello World"),
wxOK|wxICON_INFORMATION, this );
}
It's probably crashing....
I know this already but check here: http://docs.wxwidgets.org/trunk/classwx_panel.html
It'd mention it is panels did that on purpose.
Any wxWindow can use a wxSizer, this is a thing that lays stuff out (like box sizers and stuff).
While the parent of a wxWindow is the thing it is in (unless it is top-level), it must go in a sizer inside the parent.
you want something like:
wxSizer* mySizer = new wxBoxSizer(WX_VERTICAL);
setSizer(mySizer);
wxWindow newWindow = new wxPanel(this);
mySizer->Add(newWindow,1,WX_EXPAND);
the 1 is the ratio of extra space this window will get of the total (in this case 1), in the direction of the sizer (vertically)
the expand flag means "fill up your space in the axis the sizer isn't" in this case horizontally. So it will fill up all the space the sizer gives it.
If you have a horizontal sizer (puts things across) with 2 buttons, if one has weight 1 and the other 2, the one with the 2 weight will get 2/3rds of whatever space the sizer has available but doesn't use.
Each window has a desired size, a size it wants to be at least, if we have 300px and the buttons only want 100 each, the weight 2 one will get 66px extra.
You get the idea.
My names here (WX_EXPAND) could be wrong, I used wxPython first (wx.EXPAND) then came to wxWidgets, I mainly use the IDE to get the names right.
If you are looking to learn wx, do consider wxPython.
It's behaving now, but it's a big of a guess as to why. The program was never crashing. I traced it in debug and the close event was being called.
This closes when [Enter] is pressed:
wxPanel *panel = new wxPanel(this);
As do all of these:
wxPanel *panel = new wxPanel(this, wxID_ANY);
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition);
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
This however doesn't close with [Enter] is pressed:
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND);
Nor do these close:
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND);
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxCLIP_CHILDREN);
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND | wxCLIP_CHILDREN);
But this closes again:
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND | wxCLIP_CHILDREN | wxTAB_TRAVERSAL);
Apparently if wxTAB_TRAVERSAL is involved in the style (it's the default for the constructor BTW) it will propagate your [Enter] keypress to the wxFrame and click the [X] close button.
The only thing even close to a hint is a cryptic and seemingly irrelevant note in the documentation:
Tab traversal is implemented through an otherwise undocumented intermediate wxControlContainer class from which any class can derive in addition to the normal wxWindow base class. Please see wx/containr.h and wx/panel.h to find out how this is achieved.
if not all characters are being intercepted by your OnKeyDown or OnChar handler, it may be because you are using the wxTAB_TRAVERSAL style, which grabs some keypresses for use by child controls.
This is my best guess. I don't know if this is intentional behavior or a bug.

wxWidgets ribbonBar taking up entire window

I've spent the past day figuring out how to get a wxRibbon bar. It took some work, but I finally got a ribbon bar working. Except that the ribbon bar takes up the entire window, except for a tiny space on the right side of my application.
The code I'm using to test in my main window is as follow:
Win_app::Win_app(const wxString& title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(640, 480))
{
wxRibbonBar* mainMenu = new wxRibbonBar(this,-1,wxDefaultPosition,wxSize(20,40));
wxRibbonPage* home = new wxRibbonPage(mainMenu, wxID_ANY, wxT("Home"));
wxRibbonPanel *test_panel1 = new wxRibbonPanel(home, wxID_ANY, wxT("Panel 1"),
wxNullBitmap, wxDefaultPosition, wxSize(320,60));
wxRibbonPanel *test_panel2 = new wxRibbonPanel(home, wxID_ANY, wxT("Panel 2"),
wxNullBitmap, wxDefaultPosition, wxSize(320,60));
wxRibbonPage* page = new wxRibbonPage(mainMenu, wxID_ANY, wxT("Another Page"));
wxRibbonPanel *test_panel3 = new wxRibbonPanel(page, wxID_ANY, wxT("Panel 3"),
wxNullBitmap, wxDefaultPosition, wxSize(640,60));
mainMenu->Realize();
Centre();
}
I'm using wxWidgets 2.9.4. Any help would be much appreciated! Thanks for reading.
I would suggest taking a look at the ribbon sample in your wxWidgets install located at wxdir\samples\ribbon. It looks to be doing roughly the same as your code with a key difference in that it adds the wxRibbonBar to a sizer:
wxSizer *s = new wxBoxSizer(wxVERTICAL);
s->Add(m_ribbon, 0, wxEXPAND);
s->Add(m_logwindow, 1, wxEXPAND);
s->Add(m_togglePanels, wxSizerFlags().Border());
SetSizer(s);
You need to do this because if there is a single control on a dialog it automatically fills the dialog, which in your case is unwanted behaviour.