wxWidgets C++ panel is invisible - c++

I am starting to practise my skills with wxWidgets and I wanted to add some panel with two different texts above this slider, but when I tried, the result is invisible:
MainWindow::MainWindow(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition) {
wxMenuBar *menubar;
wxMenu *file;
menubar = new wxMenuBar;
file = new wxMenu;
file->Append(wxID_OPEN, wxT("&Open"));
menubar->Append(file, wxT("&File"));
SetMenuBar(menubar);
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
this->SetSizer(vbox);
player_widget = new wxWindow(this, wxID_ANY);
player_widget->SetBackgroundColour(wxColour(wxT("black")));
vbox->Add(player_widget, 1, wxEXPAND | wxALIGN_TOP);
wxBoxSizer* bs = new wxBoxSizer(wxHORIZONTAL);
wxPanel* p1 = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(0,20));
p1->SetSizer(bs);
p1->Enable(true);
p1->Show(true);
vbox->Add(p1,0,wxEXPAND);
wxStaticText* text1 = new wxStaticText(this,11, "text 1");
wxStaticText* text2 = new wxStaticText(this,12, "text 2");
bs->Add(text1);
bs->Add(text2);
timeline = new wxSlider(this, myID_TIMELINE, 0, 0, TIMELINE_MAX);
vbox->Add(timeline, 0, wxEXPAND);
wxPanel *controlPanel = new wxPanel(this, wxID_ANY);
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
controlPanel->SetSizer(hbox);
vbox->Add(controlPanel, 0, wxEXPAND);
playpause_button = new wxButton(controlPanel, myID_PLAYPAUSE, wxT("Play"));
stop_button = new wxButton(controlPanel, myID_STOP, wxT("Stop"));
volume_slider = new wxSlider(controlPanel, myID_VOLUME, VOLUME_MAX, 0, VOLUME_MAX, wxDefaultPosition, wxSize(100, -1));
hbox->Add(playpause_button);
hbox->Add(stop_button);
hbox->AddStretchSpacer();
hbox->Add(volume_slider);
}
Any ideas what I did wrong?

Change parent of text1 and text2 to p1.

It is simpler to use just one panel that holds everything.
Something like this ( a complete program )
#include <wx/wx.h>
#include <wx/app.h>
class cApp : public wxApp
{
public:
virtual bool OnInit();
};
#define TIMELINE_MAX 100
#define VOLUME_MAX 100
enum {
myID_TIMELINE,
myID_PLAYPAUSE,
myID_STOP,
myID_VOLUME
};
class cFrame: public wxFrame
{
wxWindow * player_widget;
wxSlider * timeline;
wxSlider * volume_slider;
wxButton * playpause_button;
wxButton * stop_button;
public:
cFrame(wxFrame *frame, const wxString& title)
: wxFrame(frame, -1, title, wxPoint(-1,-1),wxSize(600,600))
{
wxMenuBar *menubar;
wxMenu *file;
menubar = new wxMenuBar;
file = new wxMenu;
file->Append(wxID_OPEN, wxT("&Open"));
menubar->Append(file, wxT("&File"));
SetMenuBar(menubar);
// define one panel to hold everything
// make it big enough to fill the frame
wxPanel* p1 = new wxPanel(this,wxID_ANY, wxPoint(-1,-1),wxSize(600,600) );
// top level sizer to hold everything
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
// add window widget at top
player_widget = new wxWindow(p1, wxID_ANY);
player_widget->SetBackgroundColour(wxColour(wxT("black")));
vbox->Add(player_widget, 1, wxEXPAND | wxALIGN_TOP);
// add some texts in a horizontal row
wxBoxSizer* bs = new wxBoxSizer(wxHORIZONTAL);
wxStaticText* text1 = new wxStaticText(p1,11, "text 1");
wxStaticText* text2 = new wxStaticText(p1,12, "text 2");
bs->Add(text1);
bs->Add(text2);
// add texts just below window widget
vbox->Add( bs );
// add slider below texts
timeline = new wxSlider(this, myID_TIMELINE, 0, 0, TIMELINE_MAX);
vbox->Add(timeline, 0, wxEXPAND);
// some more controls in a row
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
playpause_button = new wxButton(p1, myID_PLAYPAUSE, wxT("Play"));
stop_button = new wxButton(p1, myID_STOP, wxT("Stop"));
volume_slider = new wxSlider(p1, myID_VOLUME, VOLUME_MAX,
0, VOLUME_MAX, wxDefaultPosition, wxSize(100, -1));
hbox->Add(playpause_button);
hbox->Add(stop_button);
hbox->AddStretchSpacer();
hbox->Add(volume_slider);
// add controls below big slider
vbox->Add( hbox );
// make everything happen
SetSizer(vbox);
}
};
IMPLEMENT_APP(cApp);
bool cApp::OnInit()
{
cFrame* frame = new cFrame(0L, _("wx Starter"));
frame->Show();
return true;
}
This gives:
Here's the thing about computer programming: at some time you always have to settle down and write some code. All those applications, like wxFormBuilder, that promise to write code for you have to be abandoned at some point and you actually have to do some work ( Here is a link to more about this )

Related

c++ wxWidgets Crash When Trying to Access wxTextCtrl Pointer outside of main

I'm getting a very mysterious error that just makes no sense to me. I'm using CodeBlocks with a wxWidgets project. Inside the frame constructor, i create an object of the class fileManager. I then call a fileManager method that passes a TextCtrl from the GUI to fileManager for storage, so I'll be able to access the TextCtrl from within fileManager. The TextCtrl is called log_output. When I do this, i set a pointer in fileManager to the TextCtrl called local_log_output. Then when I try to call a different fileManager method and use that pointer in the method, the program crashes.
The mysterious part is the following. If this is my frame constructor:
MullSimple_3Frame::MullSimple_3Frame(wxFrame *frame, const wxString& title)
: wxFrame(frame, -1, title, wxPoint(0, 0) )
{
iniGUI();
log_output->SetValue("HUH?");
FileManager fileManager;
fileManager.setLogOutput(log_output);
std::map<int, std::string> prog_vars_deck_pairs = fileManager.getMenuPairs( "deck", "" );
}
the program works fine and does not crash. The fileManager.getMenuPairs method is called fine, which uses local_log_output. If however I move the call to fileManager.getMenuPairs to a seperate function in the frame, then call that function like so:
MullSimple_3Frame::MullSimple_3Frame(wxFrame *frame, const wxString& title)
: wxFrame(frame, -1, title, wxPoint(0, 0) )
{
iniGUI();
log_output->SetValue("HUH?");
FileManager fileManager;
fileManager.setLogOutput(log_output);
testMethod();
}
void MullSimple_3Frame::testMethod()
{
std::map<int, std::string> prog_vars_deck_pairs = fileManager.getMenuPairs( "deck", "" );
}
The program crashes on the call to testMethod, and it stops on the line in getMenuPairs where I try to set the value of local_log_output.
The only thing I can think of is maybe the order that I'm declaring variables/functions in the frame constructor header matters? What other possible difference could there be where its not crashing in the first case but is crashing in the second case?
This is the header file for the frame
#ifndef MULLSIMPLE_2MAIN_H
#define MULLSIMPLE_2MAIN_H
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <vector>
#include "MullSimple_3App.h"
#include "non-wx/my_help_fxns.h"
#include "non-wx/file_manager.h"
#include "non-wx/data_vars.h"
#include "non-wx/simulator.h"
class MullSimple_3Frame: public wxFrame
{
public:
// constructor and destructor
MullSimple_3Frame(wxFrame *frame, const wxString& title);
~MullSimple_3Frame();
// logging output to terminal at bottom of gui
wxTextCtrl *log_output;
// reloads the deck load menu
void menuDeckOptsReload();
// just for trying to solve problem
void testMethod();
// VARIABLES
int unique_wx_id = 1000;
int curr_need_id = 0;
int last_active_need_row_id = -1;
std::vector<int> existing_needs_ids;
std::vector<wxBoxSizer*> existing_needs_sizers;
std::vector<need_layout_struct> existing_needs_items;
std::vector<std::vector<int>> existing_needs_widget_ids;
// WX GUI OBJECTS
wxPanel *main_panel;
wxMenu *deck_options_menu;
wxMenu *load_deck_sub_menu;
wxMenu *mull_options_menu;
wxMenu *mull_load_all_sub_menu;
wxMenu *mull_load_this_sub_menu;
wxStaticText *deck_info_id;
wxTextCtrl *deck_info_name;
wxTextCtrl *deck_info_set;
wxStaticText *deck_info_if_saved;
wxStaticText *mull_info_id;
wxTextCtrl *mull_info_name;
wxStaticText *mull_info_if_saved;
wxStaticText *deck_casts_title;
wxStaticText *deck_lands_title;
std::vector<wxTextCtrl*> deck_castable_inputs;
std::vector<wxButton*> deck_castable_info_buts;
std::vector<wxTextCtrl*> deck_land_inputs;
std::vector<wxButton*> deck_land_info_buts;
std::vector<wxTextCtrl*> hand_inputs;
wxButton *mull_opts_but_add_need;
wxButton *mull_opts_but_remove_need;
wxButton *mull_opts_but_reset_needs;
wxButton *mull_opts_but_simulate;
wxBoxSizer *needs_sizer_wrap;
// EVENT HANDLERS
void eMenuDeckOptsNew(wxCommandEvent &event);
void eMenuDeckOptsSave(wxCommandEvent &event);
void eMenuDeckOptsLoad(wxCommandEvent &event);
void eMenuMullOptsNew(wxCommandEvent &event);
void eMenuMullOptsSave(wxCommandEvent &event);
void eMenuMullOptsLoad(wxCommandEvent &event);
void eMenuHandOptsClear(wxCommandEvent &event);
// FUNCTIONS
void iniMenu();
void menuMullOptsReload();
void iniGUI();
void resetDeckInfo();
void resetDeckInputs();
void resetDeckInfoButs();
void resetHandInputs();
void resetMullInfo();
void resetMullInputs();
void deckCountCards();
bool deckCheckSaved();
void deckSortInputs(std::vector<std::string> deck_castable_strings, std::vector<std::string> deck_land_strings );
void deckUpdateInfoButs();
bool mullCheckSaved();
void mullAddNeed(int insert_after_count);
void mullNeedLineProcessRadiosAndChecks(need_layout_struct this_need_layout, std::string filter_class );
// CLASS REFERENCES
// file manager can save and load data
FileManager fileManager;
// data vars holds info about the cards in deck
DataVars dataVars;
// simulator sets up and runs sims
Simulator simulator;
private:
// we don't use the event table for handling events, instead we use
// connect for menu items and bind for controls, but still declare the table
DECLARE_EVENT_TABLE()
};
#endif // MULLSIMPLE_2MAIN_H
And here is the file manager header file:
#ifndef FILE_MANAGER_H_INCLUDED
#define FILE_MANAGER_H_INCLUDED
#include <vector>
#include <map>
#include "prog_structs.h"
class FileManager
{
private:
public:
FileManager();
// pointer to the gui log_output
wxTextCtrl *local_log_output;
// passed log_output from gui, sets a pointer to it for access within this class
void setLogOutput(wxTextCtrl *log_output);
// gets id/name pairs for saved decks to populate menu items
std::map<int, std::string> getMenuPairs(
std::string type, // "deck" or "mull"
std::string deck_id // only needed for mull, if we want only mull data related to a specific deck
);
};
#endif // FILE_MANAGER_H_INCLUDED
And the file manager .cpp file
#include <sstream>
#include <fstream>
#include <filesystem>
#include <map>
#include "file_manager.h"
#include "my_help_fxns.h"
FileManager::FileManager()
{
}
void FileManager::setLogOutput(wxTextCtrl *log_output)
{
local_log_output = log_output;
}
std::map<int, std::string> FileManager::getMenuPairs( std::string type, std::string deck_id )
{
// PROGRAM IS CRASHING HERE, WHY I DON"T KNOW
local_log_output->SetValue("PRINTING B...");
std::map<int, std::string> menu_pairs;
return menu_pairs;
}
EDIT: iniGUI()
// mainPanel
main_panel = new wxPanel(this, wxID_ANY);
// mainPanelSizer (vertical)
wxBoxSizer *main_panel_sizer = new wxBoxSizer(wxVERTICAL);
// info sizer
// deck info (id, name etc)
// mull info (id, name etc)
wxBoxSizer *info_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer->Add(info_sizer, wxSizerFlags(0).Expand());
// deckInfo (horizontal)
// deck data along top like deck name, deck id, deck set
wxBoxSizer *deck_info_sizer = new wxBoxSizer(wxHORIZONTAL);
info_sizer->Add(deck_info_sizer, wxSizerFlags(1).Expand());
// DECK ID
// label
wxStaticText *label1 = new wxStaticText(main_panel, wxID_ANY, "DECK ID: ");
deck_info_sizer->Add(label1, wxSizerFlags(0).Expand());
// value
deck_info_id = new wxStaticText(main_panel, wxID_ANY, "-1");
deck_info_sizer->Add(deck_info_id, wxSizerFlags(0).Expand());
// DECK NAME
// label
wxStaticText *label2 = new wxStaticText(main_panel, wxID_ANY, ", NAME: ");
deck_info_sizer->Add(label2, wxSizerFlags(0).Expand());
// value
deck_info_name = new wxTextCtrl(main_panel, wxID_ANY, "");
deck_info_name->Bind(wxEVT_TEXT, &MullSimple_3Frame::eDeckInfoChangeValue, this);
deck_info_sizer->Add(deck_info_name, wxSizerFlags(0).Expand());
// DECK SET
// label
wxStaticText *label3 = new wxStaticText(main_panel, wxID_ANY, ", SET: ");
deck_info_sizer->Add(label3, wxSizerFlags(0).Expand());
// value
deck_info_set = new wxTextCtrl(main_panel, wxID_ANY, "");
deck_info_set->Bind(wxEVT_TEXT, &MullSimple_3Frame::eDeckInfoChangeValue, this);
deck_info_set->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::eDeckInfoBlurSet, this);
deck_info_sizer->Add(deck_info_set, wxSizerFlags(0).Expand());
// DECK SAVED
// can't do stuff unless the deck is saved. saving allows us to set data_vars.
// label
wxStaticText *label4 = new wxStaticText(main_panel, wxID_ANY, ", SAVED: ");
deck_info_sizer->Add(label4, wxSizerFlags(0).Expand());
// value
deck_info_if_saved = new wxStaticText(main_panel, wxID_ANY, "FALSE");
deck_info_sizer->Add(deck_info_if_saved, wxSizerFlags(0).Expand());
wxBoxSizer *mull_info_sizer = new wxBoxSizer(wxHORIZONTAL);
info_sizer->Add(mull_info_sizer, wxSizerFlags(1).Expand());
// MULL ID
// label
wxStaticText *label5 = new wxStaticText(main_panel, wxID_ANY, "MULL ID: ");
mull_info_sizer->Add(label5, wxSizerFlags(0).Expand());
// value
mull_info_id = new wxStaticText(main_panel, wxID_ANY, "-1");
mull_info_sizer->Add(mull_info_id, wxSizerFlags(0).Expand());
// MULL NAME
// label
wxStaticText *label6 = new wxStaticText(main_panel, wxID_ANY, ", NAME: ");
mull_info_sizer->Add(label6, wxSizerFlags(0).Expand());
// value
mull_info_name = new wxTextCtrl(main_panel, wxID_ANY, "");
mull_info_name->Bind(wxEVT_TEXT, &MullSimple_3Frame::eMullInfoChangeValue, this);
mull_info_sizer->Add(mull_info_name, wxSizerFlags(0).Expand());
// MULL SAVED
// can't do stuff unless the deck is saved. saving allows us to set data_vars.
// label
wxStaticText *label7 = new wxStaticText(main_panel, wxID_ANY, ", SAVED: ");
mull_info_sizer->Add(label7, wxSizerFlags(0).Expand());
// value
mull_info_if_saved = new wxStaticText(main_panel, wxID_ANY, "FALSE");
mull_info_sizer->Add(mull_info_if_saved, wxSizerFlags(0).Expand());
// progSizer (horizontal)
// program contains 3 "columns"
wxBoxSizer *prog_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer->Add(prog_sizer, wxSizerFlags(1).Expand());
// PROGRAM COLUMN 1: DECK CASTABLES
// castables column
wxBoxSizer *deck_casts_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(deck_casts_sizer, wxSizerFlags(1).Expand());
// castables title
deck_casts_title = new wxStaticText(main_panel, wxID_ANY, "Castables (0)", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL|wxST_NO_AUTORESIZE);
deck_casts_title->SetBackgroundColour(*wxYELLOW);
deck_casts_sizer->Add(deck_casts_title, wxSizerFlags(0).Expand());
// castables content
// before we had just a single input arranged inside the sizer, but now i think
// we want each row itself to be a sizer, with a button and an input
for ( int i = 0; i < 25; i++ )
{
wxBoxSizer *card_item_sizer = new wxBoxSizer(wxHORIZONTAL);
deck_casts_sizer->Add(card_item_sizer, wxSizerFlags(1).Expand());
// input
this->unique_wx_id++;
wxTextCtrl *card_input = new wxTextCtrl(main_panel, this->unique_wx_id, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
//card_input->Bind(wxEVT_TEXT, &MullSimple_3Frame::GUIDeckCardsChangeValue, this);
//card_input->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::GUICardsChangeAutoComplete, this);
card_input->SetMinSize(wxSize(100, -1));
deck_castable_inputs.push_back(card_input);
card_item_sizer->Add(card_input, wxSizerFlags(1).Expand());
// button
this->unique_wx_id++;
wxButton *card_info_but = new wxButton(this->main_panel, this->unique_wx_id, "", wxDefaultPosition, wxSize(50, -1));
card_info_but->SetName("deck_cast_info_but_" + myHelpFxns::ToWxString(std::to_string(i + 1)));
//card_info_but->Bind(wxEVT_BUTTON, &MullSimple_3Frame::GUIDeckPrintCardInfo, this);
deck_castable_info_buts.push_back(card_info_but);
card_item_sizer->Add(card_info_but, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 2: DECK LANDS
wxBoxSizer *deck_lands_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(deck_lands_sizer, wxSizerFlags(1).Expand());
// lands title
deck_lands_title = new wxStaticText(main_panel, wxID_ANY, "Lands (0)", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL|wxST_NO_AUTORESIZE);
deck_lands_title->SetBackgroundColour(*wxGREEN);
deck_lands_sizer->Add(deck_lands_title, wxSizerFlags(0).Expand());
// lands content
for ( int i = 0; i < 25; i++ )
{
wxBoxSizer *card_item_sizer = new wxBoxSizer(wxHORIZONTAL);
deck_lands_sizer->Add(card_item_sizer, wxSizerFlags(1).Expand());
// input
this->unique_wx_id++;
wxTextCtrl *card_input = new wxTextCtrl(main_panel, this->unique_wx_id, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
//card_input->Bind(wxEVT_TEXT, &MullSimple_3Frame::GUIDeckCardsChangeValue, this);
//card_input->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::GUICardsChangeAutoComplete, this);
card_input->SetMinSize(wxSize(100, -1));
deck_land_inputs.push_back(card_input);
card_item_sizer->Add(card_input, wxSizerFlags(1).Expand());
// button
this->unique_wx_id++;
wxButton *card_info_but = new wxButton(this->main_panel, this->unique_wx_id, "", wxDefaultPosition, wxSize(50, -1));
card_info_but->SetName("deck_land_info_but_" + myHelpFxns::ToWxString(std::to_string(i + 1)));
//card_info_but->Bind(wxEVT_BUTTON, &MullSimple_3Frame::GUIDeckPrintCardInfo, this);
deck_land_info_buts.push_back(card_info_but);
card_item_sizer->Add(card_info_but, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 3: HAND
wxBoxSizer *hand_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(hand_sizer, wxSizerFlags(1).Expand());
// hand title
wxStaticText *hand_title = new wxStaticText(main_panel, wxID_ANY, "Hand", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL);
hand_title->SetBackgroundColour(*wxBLUE);
hand_sizer->Add(hand_title, wxSizerFlags(0).Expand());
// hand content
for ( int i = 0; i < 7; i++ )
{
this->unique_wx_id++;
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, this->unique_wx_id, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
//text_ctrl->Bind(wxEVT_KILL_FOCUS, &MullSimple_3Frame::GUICardsChangeAutoComplete, this);
text_ctrl->SetMinSize(wxSize(100, -1));
hand_inputs.push_back(text_ctrl);
hand_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 4: MULL OPTS
// we'll set proportion 0 here and use the mull_opts_buttons to
// set a fixed width for the sizer
wxBoxSizer *mull_opts_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(mull_opts_sizer, wxSizerFlags(0).Expand());
// MULL OPTS HORIZONTAL 1: MULL_OPTS_BUTTONS
wxBoxSizer *mull_opts_but_sizer = new wxBoxSizer(wxHORIZONTAL);
mull_opts_sizer->Add(mull_opts_but_sizer, wxSizerFlags(0).Expand());
// add need button
mull_opts_but_add_need = new wxButton(main_panel, wxID_ANY, "+ Need");
mull_opts_but_add_need->SetMinSize(wxSize(200, 50));
//mull_opts_but_add_need->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButAdd, this);
mull_opts_but_sizer->Add(mull_opts_but_add_need, wxSizerFlags(1).Expand());
// remove need button
mull_opts_but_remove_need = new wxButton(main_panel, wxID_ANY, "- Need");
mull_opts_but_remove_need->SetMinSize(wxSize(200, 50));
//mull_opts_but_remove_need->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButRemove, this);
mull_opts_but_sizer->Add(mull_opts_but_remove_need, wxSizerFlags(1).Expand());
// reset all button
mull_opts_but_reset_needs = new wxButton(main_panel, wxID_ANY, "Reset All");
mull_opts_but_reset_needs->SetMinSize(wxSize(200, 50));
//mull_opts_but_reset_needs->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButReset, this);
mull_opts_but_sizer->Add(mull_opts_but_reset_needs, wxSizerFlags(1).Expand());
// calculate button
mull_opts_but_simulate = new wxButton(main_panel, wxID_ANY, "Simulate");
mull_opts_but_simulate->SetMinSize(wxSize(200, 50));
//mull_opts_but_simulate->Bind(wxEVT_BUTTON, &MullSimple_3Frame::globalNeedButSimulate, this);
mull_opts_but_sizer->Add(mull_opts_but_simulate, wxSizerFlags(1).Expand());
// needs sizer wrap, contains the needs, starts out empty
needs_sizer_wrap = new wxBoxSizer(wxVERTICAL);
mull_opts_sizer->Add(needs_sizer_wrap, wxSizerFlags(0).Expand());
// MULL OPTS HORIZONTAL 2: NEEDS (in a stack)
// Make on base need we can use as a model
// at the bottom of the main panel stretches the log output
// fixed height so set proportion 0
wxBoxSizer *output_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer ->Add(output_sizer, wxSizerFlags(0).Expand());
log_output = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE);
log_output->SetBackgroundColour(*wxYELLOW);
output_sizer ->Add(log_output, wxSizerFlags(1).Expand());
// TAB ORDER CHANGES
// tab order is annoying, no way to simply disable tabbing into an element.
// so for things i don't want to tab into easily, i use this to put them after the log_ouput
// which should be last in the panel
mull_info_name->MoveAfterInTabOrder(log_output); // mull info name input for the mull set
for ( unsigned int i = 0; i < deck_castable_info_buts.size(); i++ )
{
deck_castable_info_buts[i]->MoveAfterInTabOrder(log_output); // info buttons for each card (castables)
}
for ( unsigned int i = 0; i < deck_land_info_buts.size(); i++ )
{
deck_land_info_buts[i]->MoveAfterInTabOrder(log_output); // info buttons for each card (lands)
}
main_panel->SetSizerAndFit(main_panel_sizer);
this->Fit();
}

wxWidgets Dynamically Create New Sizer after program creation, Not Rendering Properly (Newbie)

I have the following GUI, which is created with an empty sizer at the current location. The sizer is probably collapsed, as its proportion is 0, but i drew it like this so you can see it.
When you click the "+ Need" button, the program creates a new sizer with a bunch of buttons and controls, then adds it to this sizer. However they're not appearing where they should be. The new sizer and its contents is actually collapsed and in the top left corner as you can see here.
Which is obviously not what I want. However when I resize the window by just clicking and dragging one of the edges, everything pops into position:
I've tried calling every combination of ->Layout() and ->Fit() that I can think of but I just can't get it to work. This is the code that creates the GUI when program loads, called by the frame constructor. It creates "needs_sizer_wrap" which is the BoxSizer that starts out as empty and the new dynamic sizers get added to on button click. Note in the image it appears as "needsSizerWrap" but the code is actually "needs_sizer_wrap"
// creates the basic GUI
void MullSimple_2Frame::createGUI()
{
// mainPanel
main_panel = new wxPanel(this, wxID_ANY);
// mainPanelSizer (vertical)
wxBoxSizer *main_panel_sizer = new wxBoxSizer(wxVERTICAL);
// deckInfo (horizontal)
// deck data along top like deck name, deck id, deck set
wxBoxSizer *deck_info_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer->Add(deck_info_sizer, wxSizerFlags(0).Expand());
// DECK ID
// label
wxStaticText *label1 = new wxStaticText(main_panel, wxID_ANY, "DECK ID: ");
deck_info_sizer->Add(label1, wxSizerFlags(0).Expand());
// value
deck_info_id = new wxStaticText(main_panel, wxID_ANY, "-1");
deck_info_sizer->Add(deck_info_id, wxSizerFlags(0).Expand());
// DECK NAME
// label
wxStaticText *label2 = new wxStaticText(main_panel, wxID_ANY, ", NAME: ");
deck_info_sizer->Add(label2, wxSizerFlags(0).Expand());
// value
deck_info_name = new wxTextCtrl(main_panel, wxID_ANY, "");
deck_info_name->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_info_sizer->Add(deck_info_name, wxSizerFlags(0).Expand());
// DECK SET
// label
wxStaticText *label3 = new wxStaticText(main_panel, wxID_ANY, ", SET: ");
deck_info_sizer->Add(label3, wxSizerFlags(0).Expand());
// value
deck_info_set = new wxTextCtrl(main_panel, wxID_ANY, "");
deck_info_set->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_info_sizer->Add(deck_info_set, wxSizerFlags(0).Expand());
// DECK SAVED
// can't do stuff unless the deck is saved. saving allows us to set data_vars.
// label
wxStaticText *label4 = new wxStaticText(main_panel, wxID_ANY, ", SAVED: ");
deck_info_sizer->Add(label4, wxSizerFlags(0).Expand());
// value
deck_info_if_saved = new wxStaticText(main_panel, wxID_ANY, "FALSE");
deck_info_sizer->Add(deck_info_if_saved, wxSizerFlags(0).Expand());
// progSizer (horizontal)
// program contains 3 "columns"
wxBoxSizer *prog_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer->Add(prog_sizer, wxSizerFlags(1).Expand());
// PROGRAM COLUMN 1: DECK CASTABLES
// castables column
wxBoxSizer *deck_casts_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(deck_casts_sizer, wxSizerFlags(1).Expand());
// castables title
wxStaticText *deck_casts_title = new wxStaticText(main_panel, wxID_ANY, "Castables", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL);
deck_casts_title->SetBackgroundColour(*wxYELLOW);
deck_casts_sizer->Add(deck_casts_title, wxSizerFlags(0).Expand());
// castables content
for ( int i = 0; i < 25; i++ )
{
// need to set a size so that they're small enough to all fit on my crappy laptio
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
text_ctrl->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_castable_inputs.push_back(text_ctrl);
deck_casts_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 2: DECK LANDS
wxBoxSizer *deck_lands_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(deck_lands_sizer, wxSizerFlags(1).Expand());
// lands title
wxStaticText *deck_lands_title = new wxStaticText(main_panel, wxID_ANY, "Lands", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL);
deck_lands_title->SetBackgroundColour(*wxGREEN);
deck_lands_sizer->Add(deck_lands_title, wxSizerFlags(0).Expand());
// lands content
for ( int i = 0; i < 25; i++ )
{
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
text_ctrl->Bind(wxEVT_TEXT, &MullSimple_2Frame::ChangeDeckValue, this);
deck_land_inputs.push_back(text_ctrl);
deck_lands_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 3: HAND
wxBoxSizer *hand_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(hand_sizer, wxSizerFlags(1).Expand());
// hand title
wxStaticText *hand_title = new wxStaticText(main_panel, wxID_ANY, "Hand", wxDefaultPosition, wxSize(-1, 30), wxALIGN_CENTER_HORIZONTAL);
hand_title->SetBackgroundColour(*wxBLUE);
hand_sizer->Add(hand_title, wxSizerFlags(0).Expand());
// hand content
for ( int i = 0; i < 7; i++ )
{
wxTextCtrl *text_ctrl = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 20));
hand_inputs.push_back(text_ctrl);
hand_sizer->Add(text_ctrl, wxSizerFlags(0).Expand());
}
// PROGRAM COLUMN 4: MULL OPTS
wxBoxSizer *mull_opts_sizer = new wxBoxSizer(wxVERTICAL);
prog_sizer->Add(mull_opts_sizer, wxSizerFlags(3).Expand());
// MULL OPTS HORIZONTAL 1: MULL_OPTS_BUTTONS
wxBoxSizer *mull_opts_but_sizer = new wxBoxSizer(wxHORIZONTAL);
mull_opts_sizer->Add(mull_opts_but_sizer, wxSizerFlags(0).Expand());
// add need button
mull_opts_but_add_need = new wxButton(main_panel, wxID_ANY, "+ Need");
mull_opts_but_add_need->SetMinSize(wxSize(-1, 50));
mull_opts_but_add_need->Bind(wxEVT_BUTTON, &MullSimple_2Frame::addNeedGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_add_need, wxSizerFlags(1).Expand());
// remove need button
mull_opts_but_remove_need = new wxButton(main_panel, wxID_ANY, "- Need");
mull_opts_but_remove_need->SetMinSize(wxSize(-1, 50));
mull_opts_but_remove_need->Bind(wxEVT_BUTTON, &MullSimple_2Frame::removeNeedGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_remove_need, wxSizerFlags(1).Expand());
// reset all button
mull_opts_but_reset_needs = new wxButton(main_panel, wxID_ANY, "Reset All");
mull_opts_but_reset_needs->SetMinSize(wxSize(-1, 50));
mull_opts_but_reset_needs->Bind(wxEVT_BUTTON, &MullSimple_2Frame::resetNeedsGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_reset_needs, wxSizerFlags(1).Expand());
// calculate button
mull_opts_but_calculate = new wxButton(main_panel, wxID_ANY, "Calculate");
mull_opts_but_calculate->SetMinSize(wxSize(-1, 50));
mull_opts_but_calculate->Bind(wxEVT_BUTTON, &MullSimple_2Frame::calcNeedsGlobal, this);
mull_opts_but_sizer->Add(mull_opts_but_calculate, wxSizerFlags(1).Expand());
// needs sizer wrap, contains the needs, starts out empty
needs_sizer_wrap = new wxBoxSizer(wxVERTICAL);
mull_opts_sizer->Add(needs_sizer_wrap, wxSizerFlags(0).Expand());
// MULL OPTS HORIZONTAL 2: NEEDS (in a stack)
// Make on base need we can use as a model
// at the bottom of the main panel stretches the log output
// fixed height so set proportion 0
wxBoxSizer *output_sizer = new wxBoxSizer(wxHORIZONTAL);
main_panel_sizer ->Add(output_sizer, wxSizerFlags(0).Expand());
log_output = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE);
log_output->SetBackgroundColour(*wxYELLOW);
output_sizer ->Add(log_output, wxSizerFlags(1).Expand());
main_panel->SetSizerAndFit(main_panel_sizer);
this->Fit();
}
And this is the code that inserts the additional sizer when + need button is clicked
void MullSimple_2Frame::addNeedGUI()
{
wxBoxSizer *need_sizer = new wxBoxSizer(wxVERTICAL);
// NEED ITEM HORIZONTAL 1: grid of filters, 4 columns
// we'll put the same filters for every deck (for now),
// and no custom filter ability
std::vector<std::string> filters_in_deck;
filters_in_deck.push_back("Land");
...
// fixed 2 rows with 4 cols in each row
wxGridSizer *base_need_filters_sizer = new wxGridSizer(2, 4, 0, 0);
need_sizer->Add(base_need_filters_sizer, wxSizerFlags(0).Expand());
for ( unsigned int i = 0; i < filters_in_deck.size(); i++ )
{
wxCheckBox *filter_check = new wxCheckBox(this->main_panel, wxID_ANY, myHelpFxns::ToWxString(filters_in_deck[i]));
base_need_filters_sizer->Add(filter_check, wxSizerFlags(1).Expand());
}
// NEED ITEM HORIZONTAL 2: line options
wxBoxSizer *base_need_line_sizer = new wxBoxSizer(wxHORIZONTAL);
need_sizer->Add(base_need_line_sizer, wxSizerFlags(0).Expand());
// line item -> "("
wxButton *need_line_open_brack_but = new wxButton(this->main_panel, wxID_ANY, "(", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_open_brack_but, wxSizerFlags(0).Expand());
// line item -> apply filters logic (any/all)
wxComboBox *need_line_apply_filters_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_apply_filters_combo->Append("Any");
need_line_apply_filters_combo->Append("All");
base_need_line_sizer->Add(need_line_apply_filters_combo, wxSizerFlags(1).Expand());
// line item -> cards to draw combo box
wxComboBox *need_line_cards_draw_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_cards_draw_combo->Append("1 to Draw");
...
base_need_line_sizer->Add(need_line_cards_draw_combo, wxSizerFlags(1).Expand());
// line item -> source colors combo
wxComboBox *need_line_source_colors_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_source_colors_combo->Append("Any");
...
base_need_line_sizer->Add(need_line_source_colors_combo, wxSizerFlags(1).Expand());
// line item -> cost colors combo
wxComboBox *need_line_cost_colors_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_cost_colors_combo->Append("Any");
...
base_need_line_sizer->Add(need_line_cost_colors_combo, wxSizerFlags(1).Expand());
// line item -> cost amount combo
wxComboBox *need_line_cost_amount_combo = new wxComboBox(this->main_panel, wxID_ANY);
need_line_cost_amount_combo->Append("Any");
...
base_need_line_sizer->Add(need_line_cost_amount_combo, wxSizerFlags(1).Expand());
// line item -> specific card input
wxTextCtrl *need_line_specific_card_input = new wxTextCtrl(this->main_panel, wxID_ANY, wxEmptyString);
base_need_line_sizer->Add(need_line_specific_card_input, wxSizerFlags(1).Expand());
// line item -> ")"
wxButton *need_line_close_brack_but = new wxButton(this->main_panel, wxID_ANY, ")", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_close_brack_but, wxSizerFlags(0).Expand());
// line item -> "AND"
wxButton *need_line_and_but = new wxButton(this->main_panel, wxID_ANY, "AND", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_and_but, wxSizerFlags(0).Expand());
// line item -> "OR"
wxButton *need_line_or_but = new wxButton(this->main_panel, wxID_ANY, "OR", wxDefaultPosition, wxSize(50, -1));
base_need_line_sizer->Add(need_line_or_but, wxSizerFlags(0).Expand());
// NEED ITEM HORIZONTAL 3: NEED INLINE BUTTONS
wxBoxSizer *base_need_buts_sizer = new wxBoxSizer(wxHORIZONTAL);
need_sizer->Add(base_need_buts_sizer, wxSizerFlags(0).Expand());
wxButton *need_but_add_need_after_but = new wxButton(this->main_panel, wxID_ANY, "+");
base_need_buts_sizer->Add(need_but_add_need_after_but, wxSizerFlags(0).Expand());
wxButton *need_but_remove_this_but = new wxButton(this->main_panel, wxID_ANY, "-");
base_need_buts_sizer->Add(need_but_remove_this_but, wxSizerFlags(0).Expand());
needs_sizer_wrap->Add(need_sizer, wxSizerFlags(0).Expand().Border(wxALL, 10));
//needs_sizer_wrap->Layout();
//needs_sizer_wrap->Fit(need_sizer);
}
I didn't realize this until just now, but according to the documentation when you call Layout on a top level window
Lays out the children using the window sizer or resizes the only child
of the window to cover its entire area.
This class overrides the base class Layout() method to check if this
window contains exactly one child – which is commonly the case, with
wxPanel being often created as the only child of wxTopLevelWindow –
and, if this is the case, resizes this child window to cover the
entire client area.
So I guess calling Layout on a frame with only 1 child window won't work since that call will only try to resize the panel but the panel doesn't change size so won't try to re-layout its new children.
What seems to work is adding something like
this->main_panel->Layout();
to the end of MullSimple_2Frame::addNeedGUI(). This forces a re-layout on the panel instead of the frame.
Alternately, according to the documentation you could just use a sizer the frame and only add main_panel to it. But as I said in another answer, usually a sizer with only a single window doesn't make much sense.

wxDialog loses stay on top attribute

I have 2 wxDialogs with the button which makes each of them top most dialog(wxSTAY_ON_TOP) after button click.
Workflow:
I make one of them top most, checked - dialog become top most.
I clicked once more and dialog become not top most, checked - ok.
Made both of them top most, checked - ok, both top most.
Made one of them not top most after step 3, checked - accordinly to logs 1 - top most, another is not top most, but if I click somewhere both behaves like not top most. I have tried with wxFrame, with more than 2 wxDialogs - result same. Maybe I missing something or threre is a bug. Please help.
wxWidgets 3.0.2, Windows
Simple frame which owns dialogs
class Frame : public wxFrame
{
public:
Frame()
: wxFrame(nullptr, wxID_ANY, wxEmptyString, wxPoint(1100, 300), wxSize(200, 200))
{
wxBoxSizer* sz = new wxBoxSizer(wxVERTICAL);
wxStaticText* text = new wxStaticText(this, wxID_ANY, "Parent Frame");
sz->Add(text, 0, wxALL, 5);
SetSizer(sz);
Layout();
}
};
Dialog class
class Dialog : public wxDialog
{
public:
Dialog(wxWindow* parent, wxPoint pos)
: wxDialog(parent, wxID_ANY, "", pos, wxSize(200, 200),
wxFRAME_NO_TASKBAR | wxTAB_TRAVERSAL | wxBORDER_NONE)
{
wxBoxSizer* mainS = new wxBoxSizer(wxVERTICAL);
wxButton* button = new wxButton(this, wxID_ANY, "Top Most");
mainS->Add(button, 0, wxALL, 5);
SetSizer(mainS);
Layout();
button->Bind(wxEVT_BUTTON, &Dialog::onButton, this);
}
void onButton(wxCommandEvent&)
{
bool isTopMost = (GetWindowStyle() & wxSTAY_ON_TOP) != 0;
if (isTopMost) {
// Makes not top most dynamically
SetWindowStyle(GetWindowStyle() & ~wxSTAY_ON_TOP);
}
else {
// Makes top most dynamically
SetWindowStyle(GetWindowStyle() | wxSTAY_ON_TOP);
}
}
};
Calling sample
class WxguiApp : public wxApp
{
public:
bool OnInit() override
{
Frame* mainWnd = new Frame();
mainWnd->Show();
SetTopWindow(mainWnd);
Dialog* dlg1 = new Dialog(mainWnd, wxPoint(500, 300));
dlg1->Show();
Dialog* dlg2 = new Dialog(mainWnd, wxPoint(800, 300));
dlg2->Show();
return true;
}
};
IMPLEMENT_APP(WxguiApp);

wxFrame does not process Tab button

I have a simple wxFrame with 3 buttons. After I press Tab nothing happens. In the forum I found that wxFrame should process Tab button events normally and switch focus between controls. I tried with wxTAB_TRAVERSAL and without it, but look like no result.
Here is my code. wxWidgets 3.0.2. Please, help.
class TabWnd
: public wxFrame
{
public:
TabWnd()
: wxFrame(nullptr,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxDefaultSize,
wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL)
{
wxBoxSizer* sz = new wxBoxSizer(wxVERTICAL);
wxButton* b1 = new wxButton(this, wxID_ANY, wxT("First"));
sz->Add(b1, 0, wxALL, 5);
wxButton* b2 = new wxButton(this, wxID_ANY, wxT("Second"));
sz->Add(b2, 0, wxALL, 5);
wxButton* b3 = new wxButton(this, wxID_ANY, wxT("Third"));
sz->Add(b3, 0, wxALL, 5);
SetSizer(sz);
Layout();
Centre(wxBOTH);
}
};
class WxguiApp
: public wxApp
{
public:
bool OnInit() override
{
TabWnd* mainWnd = new TabWnd();
mainWnd->Show();
SetTopWindow(mainWnd);
return true;
}
};
IMPLEMENT_APP(WxguiApp);
Try adding a panel between the frame and the buttons like this:
wxBoxSizer* sz = new wxBoxSizer(wxVERTICAL);
wxPanel* pnl = new wxPanel( this, wxID_ANY );
wxBoxSizer* sz2 = new wxBoxSizer( wxVERTICAL );
wxButton* b1 = new wxButton(pnl, wxID_ANY, wxT("First"));
sz2->Add(b1, 0, wxALL, 5);
wxButton* b2 = new wxButton(pnl, wxID_ANY, wxT("Second"));
sz2->Add(b2, 0, wxALL, 5);
wxButton* b3 = new wxButton(pnl, wxID_ANY, wxT("Third"));
sz2->Add(b3, 0, wxALL, 5);
pnl->SetSizer( sz2 );
sz->Add( pnl, 1, wxEXPAND );
SetSizer(sz);
Layout();
Centre(wxBOTH);

wxTextValidation doesn't copy data from wxTextCtrl to wxString variable

//ctor
test=new wxString[n];
starr=new wxStaticText *[n];
tcarr=new wxTextCtrl *[n];
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
wxStaticBox *st = new wxStaticBox(this, -1, wxT("test"),wxPoint(5, 5), wxSize(240, 150));
for(int i=0;i<n;i++){
starr[i] = new wxStaticText(this,-1,wxString::Format(wxT("test %i:"),i+1),wxPoint(20, 20+(i+1)*20));
tcarr[i] = new wxTextCtrl(this, 32000+i, wxT(""),wxPoint(95, 20+(i+1)*20),wxDefaultSize,0,wxTextValidator(wxFILTER_NUMERIC,&test[i]));
vbox->Add(starr[i],1);
vbox->Add(tcarr[i],1);
}
wxButton *okButton = new wxButton(this, wxID_OK, wxT("Save"),
wxDefaultPosition, wxSize(70, 30));
hbox->Add(okButton, 1);
vbox->Add(hbox, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, 10);
SetSizer(vbox);
Centre();
//header
private:
wxString *test;
wxStaticText **starr;
wxTextCtrl **tcarr;
wxTextValidation doesn't copy data from wxTextCtrl to wxString variable:
When I click wxID_OK button and check value on test[0..n] I get empty wxString in each, What is the problem?
Thanks.
By default wxValidator only copies the content of your control in the associated wxString when the control itself is "dismissed" (no idea whether this means destroyed or what). If you want to copy it as soon as the OK button is pressed, you have to do that explicitly. Try inserting this:
for (int i=0;i<n;i++) {
tcarr[i]->GetValidator()->TransferFromWindow();
wxMessageBox(test[i]); // just for debug
}
It worked fine for me (wxWidgets 3.0.2 on TDM-GCC-64).