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.
Related
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 show another frame after button click event?
like this
My code here show window OnInit. but what to do next?
I did not find how to do this. little experience with this.
I comment the window that should be.
enum
{
wxID_First_Load = 5000,
wxID_First_Frame,
wxID_First_Panel
};
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
void fileLoad(wxCommandEvent& event);
private:
int file_count = 0;
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(wxID_First_Load, MyFrame::fileLoad)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(MyApp);
bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame("Hello World", wxDefaultPosition, wxSize(450, 250));
frame->SetWindowStyle(wxCAPTION | wxSYSTEM_MENU );
frame->Show(true);
return true;
}
MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame(NULL, wxID_First_Frame, title, pos, size)
{
wxBoxSizer *first_sizer = new wxBoxSizer(wxVERTICAL);
wxPanel *firstPanel = new wxPanel(this, wxID_First_Panel);
wxButton *firstButton_Load = new wxButton(firstPanel, wxID_First_Load, "Choose file");
firstPanel->SetSizer(first_sizer);
first_sizer->Add(firstButton_Load, 1, wxEXPAND | wxALL, 10);
firstPanel->SetSizer(first_sizer);
}
void MyFrame::fileLoad(wxCommandEvent& WXUNUSED(event))
{
file_count = 2;
}
Second Frame or window:
wxPanel *firstPanel = new wxPanel(this, wxID_First_Panel);
wxBoxSizer *second_sizer = new wxBoxSizer(wxVERTICAL);
for (int i = 0; i < file_count; i++)
{
second_sizer->Add(new wxTextCtrl(firstPanel, wxWindowID(i), "Hello", wxDefaultPosition, wxSize(235, 60)), wxSizerFlags(0).Border(wxALL, 5));
}
firstPanel->SetSizer(second_sizer);
To create a new frame you need to create a new object of wxFrame class or a class deriving from it. Typically, you want to put some data and logic into your new frame, so you would create some class, e.g. MySecondaryFrame (but hopefully with a better name) , inheriting from wxFrame in a similar way to your existing MyFrame class.
Then to show it you would do the same thing that you do in MyApp::OnInit(), i.e. create a new object of this class and call Show() to actually show it.
P.S. Note that your SetWindowStyle(wxCAPTION | wxSYSTEM_MENU ) call is unnecessary, these styles are already on by default. Also, hard coding the frame size in pixels is a bad idea, consider using sizers to determine the best size suitable for the frame contents or just leave it unspecified if it really doesn't matter.
I have written code for MyFrame. I want MyFrame CONTENTS to be part of MyPageInWizard1 in wxWizard. Is there any way that I can create an object of MyFrame and use it in the MyPageInWizard1 of wxWizard? Or I have to copy code of MyFrame in that page?
MyFrame::MyFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style)
I am doing this: but that MyFrame contents are not displayed.
MyPageInWizard1::MyPageInWizard1(wxWizard *parent, wxWizardPage *prev, wxWizardPage *next)MyWizardPage(1, parent, prev, next)
{
SetBackgroundColour(wxColour(255, 255, 255));
wxBoxSizer* bSizer3;
bSizer3 = new wxBoxSizer(wxVERTICAL);
MyHeading = new wxStaticText(this, wxID_ANY, wxT("Some Heading"), wxDefaultPosition, wxDefaultSize, 0);
MyHeading->Wrap(-1);
MyHeading->SetFont(wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxT("Arial")));
bSizer3->Add(MyHeading, 0, wxALIGN_CENTER | wxALL, 5);
frame = new MyFrame(NULL);
bSizer3->Add(frame, 0, wxALIGN_CENTER | wxALL, 5);
this->SetSizer(bSizer3);
}
wxFrame is always a top level window, meaning that you can't put it inside any other window, including wxWizard.
To do what you want, you need to refactor your code and put the frame contents in some new MyPanel class deriving from wxPanel. Then your frame would contain just a MyPanel and you could also create (another) MyPanel inside the wizard.
I have an issue with spacer inside of the inner wxBoxSizer.
Here is the sample code.
class Frame : public wxFrame
{
public:
Frame()
: wxFrame(nullptr,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxSize(600, 200))
{
wxPanel* panel = new wxPanel(this);
// Some OUTER SIZER
wxBoxSizer* mainS = new wxBoxSizer(wxVERTICAL);
// INNER HORIZONTAL SIZER
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText* text = new wxStaticText(panel, wxID_ANY, wxT("Some Text"));
sizer->Add(text, 0, wxALL, 5);
wxComboBox* comboBox = new wxComboBox(panel, wxID_ANY, wxT("Combo"));
sizer->Add(comboBox, 0, wxALL, 5);
// SPACER
sizer->Add(0, 0, 1, wxEXPAND, 5);
wxButton* button = new wxButton(panel, wxID_ANY, wxT("Some button"));
sizer->Add(button, 0, wxALL, 5);
mainS->Add(sizer, 0, wxALL, 5);
panel->SetSizer(mainS);
// PANEL SIZER
wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
panelSizer->Add(panel, 1, wxEXPAND, 5);
SetSizer(panelSizer);
Layout();
Centre(wxBOTH);
}
};
class WxguiApp
: public wxApp
{
public:
bool OnInit() override
{
Frame* w = new Frame();
w->Show();
SetTopWindow(w);
return true;
}
};
IMPLEMENT_APP(WxguiApp);
If I remove outer sizer spacer starts working fine. Is it bug in my code? Or maybe some issue with wxWidgets?
I couldn't find answer, maybe someone help? wxWidgets 3.0.2
Your inner sizer (sizer) won't expand in the horizontal direction because you didn't tell its parent sizer (mainS) to do it when adding it, so your spacer will always be 0 pixels wide, which is its initial and minimal width.
To fix this, you just need to change the mainS->Add(sizer, 0, wxALL, 5) line to use wxALL | wxEXPAND.
Additionally, I strongly encourage you to look at using wxSizerFlags instead of the cryptic syntax shown above. And, last and really least, but still worth mentioning, prefer using much more readable AddStretchSpacer() to the confusing Add(0, 0, 1, wxEXPAND, 5) call (it's confusing because expanding your spacer doesn't do anything and border size is not taken into account without any border flags).
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.