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();
}
Related
I am trying to set an image as the background but I have no idea how to do it.
It has been hours. The first lines of code I copied from somebody and they seemed alright but it still doesn't work.
The error that I have appears in a separate window and says it cannot load/find the image.
#include "MainFrame.h"
#include <wx/wx.h>
#include "FrameTwo.h"
#include "wx/custombgwin.h"
#include <wx/dcclient.h>
enum IDs {
BUTTON_ID=2
};
wxBEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_BUTTON(BUTTON_ID, MainFrame::OnLoginClicked)
wxEND_EVENT_TABLE()
MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title) {
wxPanel* panel = new wxPanel(this);
wxClientDC dc(panel);
wxMemoryDC mdc;
int w, h;
w = 600;
h = 600;
dc.GetSize(&w, &h);
wxImage img(wxT("C:\\Users\\ALEX\\Desktop\\background_uno.jpeg"), wxBITMAP_TYPE_JPEG);
wxBitmap cat(img.Scale(w, h, wxIMAGE_QUALITY_HIGH));
mdc.SelectObject(cat);
dc.Blit(0, 0, cat.GetWidth(), cat.GetHeight(), &mdc, 0, 0, wxCOPY, 0);
mdc.SelectObject(wxNullBitmap);
//panel->SetBackgroundColour(wxColour(255, 102, 102)); //light orange-red
//panel->SetBackgroundColour(wxColour(155, 202, 62)); //fresh green
wxButton* login = new wxButton(panel, BUTTON_ID, "Log in", wxPoint(340, 350), wxSize(100, 35));
CreateStatusBar(); //creates a bar in the bottom of the frame
//wxBoxSizer* sizer = new wxBoxSizer(wxALIGN_CENTER_VERTICAL);
//sizer -> Add(login, 1, wxEXPAND | wxALL, 10);
wxButton* exit = new wxButton(panel, wxID_OK, "Exit", wxPoint(340, 390), wxSize(100, 35));
exit->Bind(wxEVT_BUTTON, &MainFrame::OnExitClicked, this);
panel->SetFont(panel->GetFont().Scale(2.5));
wxStaticText* staticText = new wxStaticText(panel, wxID_ANY, "UNO DELUXE", wxPoint(300, 40));
panel->SetFont(panel->GetFont().Scale(0.5));
wxStaticText* username = new wxStaticText(panel, wxID_ANY, "Enter username: ", wxPoint(180, 250));
wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, " ", wxPoint(300, 250), wxSize(250, -1));
wxStaticText* password = new wxStaticText(panel, wxID_ANY, "Enter password: ", wxPoint(180, 290));
wxTextCtrl* textCtrl2 = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(300, 290), wxSize(250, -1), wxTE_PASSWORD);
}
I can't help you with the program not being able to find the image - the image needs to exist either in the application's working folder or on an absolute path.
You can get around this by including the image in the executable. Unfortunately there is no consistent cross-platform way to do this - windows and mac have separate ways of doing this but GTK has no way at all. Here are some suggestions from the wiki for some possible workarounds.
Regardless of the method used to get the image into the application, you should not draw it with a client DC. Instead you should handle the paint event for the panel and draw the image in the handler for that event. Here's an example:
#include <wx/wx.h>
class ImageBgFrame: public wxFrame
{
public:
ImageBgFrame(wxFrame *frame, const wxString& title);
private:
void OnImagePanelPaint(wxPaintEvent&);
void OnExit(wxCommandEvent&);
void CreateScaledBg();
wxPanel* m_imagePanel;
wxImage m_image;
wxBitmap m_scaledBg;
};
ImageBgFrame::ImageBgFrame(wxFrame *frame, const wxString& title)
:wxFrame(frame, wxID_ANY, title)
{
::wxInitAllImageHandlers();
// Try to load the image.
m_image = wxImage("Thinking-of-getting-a-cat.png", wxBITMAP_TYPE_PNG);
if ( !m_image.IsOk() )
{
return;
}
// Create the controls.
m_imagePanel = new wxPanel(this, wxID_ANY);
CreateScaledBg();
wxButton* exit = new wxButton(m_imagePanel, wxID_OK, "Exit");
// Arrang the controls with a sizer.
wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
szr->Add(exit,wxSizerFlags().Border(wxALL));
m_imagePanel->SetSizer(szr);
Layout();
// Bind Event Handlers
m_imagePanel->Bind(wxEVT_PAINT, &ImageBgFrame::OnImagePanelPaint, this);
exit->Bind(wxEVT_BUTTON, &ImageBgFrame::OnExit, this);
}
void ImageBgFrame::OnImagePanelPaint(wxPaintEvent&)
{
if ( m_imagePanel->GetSize() != m_scaledBg.GetSize() )
{
CreateScaledBg();
}
wxPaintDC dc(m_imagePanel);
dc.DrawBitmap(m_scaledBg,0,0);
}
void ImageBgFrame::OnExit(wxCommandEvent&)
{
Close();
}
void ImageBgFrame::CreateScaledBg()
{
wxSize sz = m_imagePanel->GetSize();
m_scaledBg = wxBitmap(m_image.Scale(sz.GetWidth(), sz.GetHeight(),
wxIMAGE_QUALITY_NORMAL));
}
class ImageBgApp : public wxApp
{
public:
bool OnInit()
{
ImageBgFrame* frame = new ImageBgFrame(NULL, "Image BG");
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(ImageBgApp);
This assumes that the image "Thinking-of-getting-a-cat.png" is in the application's working folder. On GTK it looks like this:
I've also made a few other changes to the code you gave:
I'm using Bind to connect the event handlers instead of static event tables.
I'm using default sizes for the controls instead of setting the size explicitly and laying out the controls in a sizer instead of setting explicit location.
Both of these changes are a more modern way of doing things.
I'm using horizontal and vertical boxsizers to build my GUI, and so far I've had success just adding everything to sizers using expand on each sizer, then adjusting a min or max width/height of diff elements to get what I want. I've run into a strange problem though.
There are 2 gaps in the contents of my GUI i don't understand. Here is an image:
Gap #1
I don't understand gap #1 because it seems unnecessary. The entire GUI starts out with a vertical boxsizer with 2 elements. All the contents above the red StaticText go into the top part, and the red StaticText is contained in the second element.
Among the sub-sizers in the top strip, the most height that should be required is for the "Castables" and "Lands" columns of TextCtrls. This leads me to strange gap #2...
Gap #2
This gap was not here until i set a max height for the 4 buttons above it. I've drawn purple outlines in paint so you can see how the sizers fit together (or how they SHOULD fit together unless I've made a mistake). The gap is at the start of a vertical sizer that then contains some other sizers... Before i set the max heights for those 4 buttons, the height of those 4 buttons filled this gap area exactly. When i set the max height for them, I want all of the contents below to slide up, not down.
Here is all of my code in the constructor that builds the entire GUI
// mainPanel
wxPanel *mainPanel = new wxPanel(this, wxID_ANY);
// mainPanelSizer (vertical)
wxBoxSizer *mainPanelSizer = new wxBoxSizer(wxVERTICAL);
// progSizer (horizontal)
// contains program along top, and log window is along the bottom
// program contains 3 "columns"
wxBoxSizer *progSizer = new wxBoxSizer(wxHORIZONTAL);
mainPanelSizer->Add(progSizer, wxSizerFlags(1).Expand());
// 1. DECK
// deckSizer (horizontal)
wxBoxSizer *deckSizer = new wxBoxSizer(wxHORIZONTAL);
progSizer->Add(deckSizer, wxSizerFlags(1).Expand());
// castables column
wxBoxSizer *deckSizerCasts = new wxBoxSizer(wxVERTICAL);
deckSizer->Add(deckSizerCasts, wxSizerFlags(1).Expand());
// castables title
wxStaticText *deckCastsTitle = new wxStaticText(mainPanel, wxID_ANY, "Castables", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
deckCastsTitle->SetBackgroundColour(*wxYELLOW);
deckCastsTitle->SetMaxSize(wxSize(-1, 25));
deckSizerCasts->Add(deckCastsTitle, wxSizerFlags(1).Expand());
// castables content
for ( int i = 0; i < 25; i++ )
{
wxTextCtrl *textCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString);
textCtrl->SetMinSize(wxSize(200, -1));
textCtrl->SetMaxSize(wxSize(-1, 25));
deckInputCastables.push_back(textCtrl);
deckSizerCasts->Add(textCtrl, wxSizerFlags(1).Expand());
}
// lands column
wxBoxSizer *deckSizerLands = new wxBoxSizer(wxVERTICAL);
deckSizer->Add(deckSizerLands, wxSizerFlags(1).Expand());
// lands title
wxStaticText *deckLandsTitle = new wxStaticText(mainPanel, wxID_ANY, "Lands", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
deckLandsTitle->SetBackgroundColour(*wxGREEN);
deckLandsTitle->SetMaxSize(wxSize(-1, 25));
deckSizerLands->Add(deckLandsTitle, wxSizerFlags(1).Expand());
// lands content
for ( int i = 0; i < 25; i++ )
{
wxTextCtrl *textCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString);
textCtrl->SetMinSize(wxSize(200, -1));
textCtrl->SetMaxSize(wxSize(-1, 25));
deckInputLands.push_back(textCtrl);
deckSizerLands->Add(textCtrl, wxSizerFlags(1).Expand());
}
// 2. HAND
// handSizer (vertical)
wxBoxSizer *handSizer = new wxBoxSizer(wxVERTICAL);
progSizer->Add(handSizer, wxSizerFlags(1).Expand());
// hand title
wxStaticText *deckHandTitle = new wxStaticText(mainPanel, wxID_ANY, "Hand", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
deckHandTitle->SetBackgroundColour(*wxCYAN);
deckHandTitle->SetMaxSize(wxSize(-1, 25));
handSizer->Add(deckHandTitle, wxSizerFlags(1).Expand());
// hand content
for ( int i = 0; i < 7; i++ )
{
wxTextCtrl *textCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString);
textCtrl->SetMinSize(wxSize(200, -1));
textCtrl->SetMaxSize(wxSize(-1, 25));
handInputs.push_back(textCtrl);
handSizer->Add(textCtrl, wxSizerFlags(1).Expand());
}
// 3. OPTS
wxBoxSizer *optsSizer = new wxBoxSizer(wxVERTICAL);
progSizer->Add(optsSizer, wxSizerFlags(1).Expand());
// a. OPTS INSTRUCTIONS (horizontal)
// contains buttons for things can do,
// add or remove needs, reset all, or calculate
wxBoxSizer *optsInstructSizer = new wxBoxSizer(wxHORIZONTAL);
optsSizer->Add(optsInstructSizer, wxSizerFlags(1).Expand());
wxButton *addNeedButton = new wxButton(mainPanel, wxID_ANY, "+ Need");
//addNeedButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(addNeedButton, wxSizerFlags(1).Expand());
wxButton *removeNeedButton = new wxButton(mainPanel, wxID_ANY, "- Need");
//removeNeedButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(removeNeedButton, wxSizerFlags(1).Expand());
wxButton *resetAllButton = new wxButton(mainPanel, wxID_ANY, "Reset All");
//resetAllButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(resetAllButton, wxSizerFlags(1).Expand());
wxButton *calculateButton = new wxButton(mainPanel, wxID_ANY, "Calculate");
//calculateButton->SetMaxSize(wxSize(-1, 50));
optsInstructSizer->Add(calculateButton, wxSizerFlags(1).Expand());
// b. NEEDS WRAP SIZER
// contains all the needs
wxBoxSizer *needsWrapSizer = new wxBoxSizer(wxVERTICAL);
optsSizer->Add(needsWrapSizer, wxSizerFlags(1).Expand());
// b1. a single need we can copy to make more needs
wxBoxSizer *needsItemSizer = new wxBoxSizer(wxVERTICAL);
needsWrapSizer->Add(needsItemSizer, wxSizerFlags(1).Expand());
// each need has 3 horrizontal components
// i. filters check area
// we'll do 3 columns of filters so 12 possible.
// this is
wxBoxSizer *needsItemFiltersSizer = new wxBoxSizer(wxHORIZONTAL);
needsItemSizer->Add(needsItemFiltersSizer, wxSizerFlags(1).Expand());
// 4 filters per column: col 1
wxBoxSizer *needsItemFiltersCol1Sizer = new wxBoxSizer(wxVERTICAL);
needsItemFiltersSizer->Add(needsItemFiltersCol1Sizer, wxSizerFlags(1).Expand());
for ( int i = 0; i < 4; i++ )
{
wxCheckBox *filterCheck = new wxCheckBox(mainPanel, wxID_ANY, "filter " + wxString::Format(wxT("%d"), (int)(i + 1)));
filterCheck->SetBackgroundColour(*wxGREEN);
needsItemFiltersCol1Sizer->Add(filterCheck, wxSizerFlags(1).Expand());
}
// 4 filters per column: col 2
wxBoxSizer *needsItemFiltersCol2Sizer = new wxBoxSizer(wxVERTICAL);
needsItemFiltersSizer->Add(needsItemFiltersCol2Sizer, wxSizerFlags(1).Expand());
for ( int i = 4; i < 8; i++ )
{
wxCheckBox *filterCheck = new wxCheckBox(mainPanel, wxID_ANY, "filter " + wxString::Format(wxT("%d"), (int)(i + 1)));
needsItemFiltersCol2Sizer->Add(filterCheck, wxSizerFlags(1).Expand());
}
// 4 filters per column: col 3
wxBoxSizer *needsItemFiltersCol3Sizer = new wxBoxSizer(wxVERTICAL);
needsItemFiltersSizer->Add(needsItemFiltersCol3Sizer, wxSizerFlags(1).Expand());
for ( int i = 8; i < 12; i++ )
{
wxCheckBox *filterCheck = new wxCheckBox(mainPanel, wxID_ANY, "filter " + wxString::Format(wxT("%d"), (int)(i + 1)));
needsItemFiltersCol3Sizer->Add(filterCheck, wxSizerFlags(1).Expand());
}
// ii. build line (3 variations: land/land+fixing, specific card, and other
// should put in a ramp, but for now just do "land"
wxBoxSizer *needsItemBuildLineSizer = new wxBoxSizer(wxHORIZONTAL);
needsItemSizer->Add(needsItemBuildLineSizer, wxSizerFlags(1).Expand());
// line item -> "("
wxButton *needsItemOpenBrackButton = new wxButton(mainPanel, wxID_ANY, "(");
needsItemBuildLineSizer->Add(needsItemOpenBrackButton, wxSizerFlags(1).Expand());
// line item -> cards to draw combo box
wxComboBox *cardsToDrawCombo = new wxComboBox(mainPanel, wxID_ANY);
cardsToDrawCombo->Append("1 to Draw");
cardsToDrawCombo->Append("2 to Draw");
cardsToDrawCombo->Append("3 to Draw");
cardsToDrawCombo->Append("4 to Draw");
cardsToDrawCombo->Append("5 to Draw");
cardsToDrawCombo->Append("6 to Draw");
cardsToDrawCombo->Append("7 to Draw");
cardsToDrawCombo->Append("8 to Draw");
cardsToDrawCombo->Append("9 to Draw");
cardsToDrawCombo->Append("10 to Draw");
needsItemBuildLineSizer->Add(cardsToDrawCombo, wxSizerFlags(1).Expand());
// line item -> filters to apply (any/all)
wxComboBox *filtersApplyTypeCombo = new wxComboBox(mainPanel, wxID_ANY);
filtersApplyTypeCombo->Append("Any");
filtersApplyTypeCombo->Append("All");
needsItemBuildLineSizer->Add(filtersApplyTypeCombo, wxSizerFlags(1).Expand());
// line item -> cost colors combo
wxComboBox *filtersCostColorsCombo = new wxComboBox(mainPanel, wxID_ANY);
filtersCostColorsCombo->Append("Any");
filtersCostColorsCombo->Append("Black");
filtersCostColorsCombo->Append("Blue");
filtersCostColorsCombo->Append("Green");
filtersCostColorsCombo->Append("Red");
filtersCostColorsCombo->Append("White");
needsItemBuildLineSizer->Add(filtersCostColorsCombo, wxSizerFlags(1).Expand());
// line item -> cost amount combo
wxComboBox *filtersCostAmountCombo = new wxComboBox(mainPanel, wxID_ANY);
filtersCostAmountCombo->Append("<= 1");
filtersCostAmountCombo->Append("<= 2");
filtersCostAmountCombo->Append("<= 3");
filtersCostAmountCombo->Append("<= 4");
filtersCostAmountCombo->Append("<= 5");
filtersCostAmountCombo->Append("<= 6");
needsItemBuildLineSizer->Add(filtersCostAmountCombo, wxSizerFlags(1).Expand());
// line item -> ")"
wxButton *needsItemCloseBrackButton = new wxButton(mainPanel, wxID_ANY, ")");
needsItemBuildLineSizer->Add(needsItemCloseBrackButton , wxSizerFlags(1).Expand());
// line item -> "AND"
wxButton *needsItemAndButton = new wxButton(mainPanel, wxID_ANY, "AND");
needsItemBuildLineSizer->Add(needsItemAndButton, wxSizerFlags(1).Expand());
// line item -> "OR"
wxButton *needsItemOrButton = new wxButton(mainPanel, wxID_ANY, "OR");
needsItemBuildLineSizer->Add(needsItemOrButton, wxSizerFlags(1).Expand());
// iii. line buttons (reset, + row below, - this row
wxBoxSizer *needsItemButtonSizer = new wxBoxSizer(wxHORIZONTAL);
needsItemSizer->Add(needsItemButtonSizer, wxSizerFlags(1).Expand());
// buttons
wxButton *needsItemInlineAddButton = new wxButton(mainPanel, wxID_ANY, "+");
needsItemButtonSizer->Add(needsItemInlineAddButton, wxSizerFlags(1).Expand());
wxButton *needsItemInlineRemoveButton = new wxButton(mainPanel, wxID_ANY, "-");
needsItemButtonSizer->Add(needsItemInlineRemoveButton, wxSizerFlags(1).Expand());
wxButton *needsItemInlineResetButton = new wxButton(mainPanel, wxID_ANY, "R");
needsItemButtonSizer->Add(needsItemInlineResetButton, wxSizerFlags(1).Expand());
// b2. At the bottom of all needs is a
// test control that has the build string
wxBoxSizer *needsBuildDisplaySizer = new wxBoxSizer(wxVERTICAL);
needsWrapSizer->Add(needsBuildDisplaySizer, wxSizerFlags(1).Expand());
// outputSizer (horizontal)
wxStaticText *buildDisplay = new wxStaticText(mainPanel, wxID_ANY, "Build Display");
buildDisplay->SetBackgroundColour(*wxBLUE);
needsBuildDisplaySizer->Add(buildDisplay, wxSizerFlags(1).Expand());
// at the bottom of the main panel stretches the log output
wxBoxSizer *outputSizer = new wxBoxSizer(wxHORIZONTAL);
mainPanelSizer ->Add(outputSizer, wxSizerFlags(1).Expand());
// logOutput StaticText
logOutput = new wxStaticText(mainPanel, wxID_ANY, "Output Terminal");
logOutput->SetBackgroundColour(*wxRED);
outputSizer ->Add(logOutput, wxSizerFlags(1).Expand());
mainPanel->SetSizerAndFit(mainPanelSizer);
this->Fit();
Thanks for your help.
With the code you posted, I'm not seeing gap2. Instead, I'm seeing the buttons enlarged to fill the entire space. I guess you changed the code slightly after the image was created. But both the enlargement of the buttons and gap2 will have the same cause.
You have optsSizer which contains 2 other sizers optsInstructSizer and needsWrapSizer. Both of those sizers are added to optsSizer with proportion 1. That means that both optsInstructSizer and needsWrapSizer will be sized to have the same size. Since the contents of needsWrapSizer are much larger, that means that will have gap2 in the code that generated the image.
To solve this, simply add optsInstructSizer to optsSizer with proportion 0 like so:
optsSizer->Add(optsInstructSizer, wxSizerFlags(0).Expand());
or since 0 is the default value for the proportion argument, you can just use
optsSizer->Add(optsInstructSizer, wxSizerFlags().Expand());
(I always always use 0 even though it's not necessary to explicitly remind me that the item being added has proportion 0.) When an item is added to a sizer with proportion 0, it means that should be sized (verically in a vertical sizer or horizontally in a horizontal sizer) to its minimum size.
When you get rid of gap2, gap1 is removed for similar reasons. You have progSizer which contains handSizer and optsSizer. Both are added with proportion 1 which means they will have the same size. When gap2 is removed, the vertical size optsSizer shrinks which removes gap1 as well since handSizer has the same vertical size.
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.
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 )
//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).