wxWidgets GridSizer puts all buttons in the same position - c++

I'm learning wxWidgets and am trying to make minesweeper using wxButtons. I use the following code to create and position the buttons:
int length = 10;
wxGridSizer *grid = new wxGridSizer(length, length, 0, 0);
wxButton *buttons[length*length];
for (int i=0; i<length*length; i++){
buttons[i] = new wxButton(this, wxID_ANY);
grid->Add(buttons[i], 1, wxEXPAND | wxALL);
if (mineField[i]){
Bind(wxEVT_BUTTON, &MainWindow::isMine, this);
} else {
Bind(wxEVT_BUTTON, &MainWindow::notMine, this);
}
}
I expect this to generate a 10x10 grid of buttons, but instead it positions all of the buttons at (0,0). I have been Googling the problem for a while now but I can't find the issue. How do I position the buttons in a 10x10 grid? Thanks.

To make the grid sizer layout the buttons, you need to either
set it to be the sizer for a window or
add it to another sizer.
Assuming the code above is from the constructor of your main frame window, you would set grid to be the sizer for the frame like this
SetSizer(grid);
On the other hand, if you have other controls in the frame and need to add grid to another sizer, you could do that something like this:
otherSizer->Add(grid,wxSizerFlags(0));
Obviously, 'otherSizer' should be replaced with the name of the other sizer. There are many options that can be used with wxSizerFlags to get the exact layout you want.
In addition, as mentioned above, it's sometimes helpful to end all the code the creates and organizes the controls your using with a call to the Layout() method.
Another tactic you can use is to have a single panel be the only child of the frame and then set a sizer for the panel. This works because in wxWidgets whenever a top level window such as a frame has a single child, that child is automatically sized to fill all of the available area.
Using this technique, a layout with a text control along the top and the grid underneath it could be created like this:
// Create the controls
wxPanel* bgPanel = new wxPanel(this, wxID_ANY);
wxTextCtrl* text = new wxTextCtrl(bgPanel, wxID_ANY);
wxGridSizer *grid = new wxGridSizer(length, length, 0, 0);
wxButton *buttons[length*length];
for (int i=0; i<length*length; i++){
buttons[i] = new wxButton(bgPanel, wxID_ANY);
grid->Add(buttons[i], 1, wxEXPAND | wxALL);
if (mineField[i]){
Bind(wxEVT_BUTTON, &MainWindow::isMine, this);
} else {
Bind(wxEVT_BUTTON, &MainWindow::notMine, this);
}
}
// Create a sizer for the panel and add the text control and grid to it.
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
mainSizer->Add(text, wxSizerFlags(0).Expand().Border(wxALL));
mainSizer->Add(grid,wxSizerFlags(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM));
bgPanel->SetSizer(mainSizer);

Related

wxWidgets fitting big wxGrid in wxFlexGridSizer

Problem
I have a wxGrid with many rows (>200) placed inside a wxFlexGridSizer. The problem is that my button below the grid disappears. Same thing with a wxBoxSizer works using the proportion setting.
The result should look like the wxBoxSizer solution.
Is there a way to use a wxFlexGridSizer in such situation?
wxBoxSizer (working)
wxGrid *grid = new wxGrid(this, wxID_ANY);
grid->CreateGrid(0, 2);
grid->SetDefaultRowSize(20);
grid->AppendRows(200);
wxButton *button = new wxButton(this, wxID_ANY, "button");
wxBoxSizer *bsMain = new wxBoxSizer(wxHORIZONTAL);
bsMain->Add(grid, 1, wxALL, 5);
bsMain->Add(button, 0, wxALL, 5);
SetSizer(bsMain);
wxGridSizer (not working)
wxGrid *grid = new wxGrid(this, wxID_ANY);
grid->CreateGrid(0, 2);
grid->SetDefaultRowSize(20);
grid->AppendRows(200);
wxButton *button = new wxButton(this, wxID_ANY, "button");
wxFlexGridSizer *fgsMain = new wxFlexGridSizer(1, 0, 0);
fgsMain->Add(grid, 1, wxALL, 5);
fgsMain->Add(button, 0, wxALL, 5);
fgsMain->AddGrowableRow(0);
fgsMain->AddGrowableCol(0);
SetSizer(fgsMain);
I tried to use AddGrowableRow for both rows, proportion setting, wxEXPAND flag.
There is a similar question here, but the solution is a workaround:
Fitting a big grid (wxGrid) in a dialog (wxDialog)
(Screenshoots are made with wxFormBuilder v3.8.1)
By default, wxGrid will try to show all of its 200 rows on screen, which won't leave enough place for the button. You may call SetInitialSize() (or specify the initial size when creating the grid in its ctor) to change this.
Unrelated to this question, but I strongly advise you to use more readable wxSizerFlags-based API rather than the old Add() overloads. You also shouldn't hardcode the border size in pixels, this is always wrong for some platforms and gets even worse on high DPI displays (and using wxSizerFlags would have taken care of this automatically).

Custom widgets in scroll area not respecting minimum?

I'm getting closer to getting a QScrollArea working, but it's still shrinking my custom widgets as they are added. Everything is resizing fluidly, and if the scroll area is too small, then the scroll bar appears, so it clearly has some idea of a minimum size.
At start, with two custom widgets in the scroll area, you can see some shrinking:
Here's the same window below the critical point. The text is now completely gone, but it won't shrink the QLineEdit, so it finally adds a scrollbar. (the scroll area has a blue background, the content widget is the purple)
I started in Design, but everything below the scroll area's widget is in code, as I was having trouble getting the vertical layout to work right using design.
Here's the starting point:
There's a page in a StackWidget that has two elements in a vertical layout. The scroll area has a QWidget. The constructor for MainWindow defines a vertical layout, assigning it to the member scrollWidgetLayout, and giving that layout to the scroll area's widget:
scrollWidgetLayout = new QVBoxLayout(ui->scrollAreaWidgetContents);
ui->scrollAreaWidgetContents->setLayout(scrollWidgetLayout);
The app starts on the first page of the stack widget, the user logs in, and the app runs off to fetch records from the server. Each record is turned into a widget:
RecordFolderWidget::RecordFolderWidget(Record *r, QWidget *parent) : QWidget(parent)
{
record = r;
//setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
QGridLayout *layout = new QGridLayout();
pathLineEdit = new QLineEdit();
finderButton = new QPushButton("...");
QLabel *nameLabel = new QLabel(record->name);
layout->setSpacing(5);
layout->setMargin(3);
layout->addWidget(nameLabel, 0, 0, 0, 1, Qt::AlignCenter);
layout->addWidget(pathLineEdit, 1, 0);
layout->addWidget(finderButton, 1, 1);
setLayout(layout);
//setMinimumHeight(sizeHint().height());
}
Note that there are some lines commented out, these are things I have been playing with to try to get it to work. The sizeHint, btw, appears to be correct, and does not change.
Once that Widget is created, it gets added to the scroll area's widget:
RecordFolderWidget *rf = new RecordFolderWidget(record);
rf->setParent(ui->scrollAreaWidgetContents);
scrollWidgetLayout->addWidget(rf);
I tried here to also resize the scroll areas contents, with no luck:
ui->scrollAreaWidgetContents->resize(rfSize.width(), rfSize.height() * records.count());
where rfSize was pulled from the custom widget's sizeHint after it was created, and this line was called once after the loop to create/add all of the widgets.
Other than the setMinimumHeight and resize above, I've tried changing the SizePolicy for the scrollAreaWidgetContents from preferred to expanding to minimumexpanding and did not see any difference. I'm sure I've missed something trivial, but just can't find it.
The problem that I see in your code is when adding the QLabel you are setting the rowSpan to 0, I have changed it and I can observe it correctly. In the next part I show my test code and the result:
QVBoxLayout *scrollWidgetLayout = new QVBoxLayout(ui->scrollAreaWidgetContents);
for(int i=0; i<10; i++){
QWidget *widget = new QWidget;
QGridLayout *layout = new QGridLayout(widget);
QLineEdit *pathLineEdit = new QLineEdit;
QPushButton *finderButton = new QPushButton("...");
QLabel *nameLabel = new QLabel(QString("name %1").arg(i));
layout->setSpacing(5);
layout->setMargin(3);
layout->addWidget(nameLabel, 0, 0, 1, 1, Qt::AlignCenter);
layout->addWidget(pathLineEdit, 1, 0);
layout->addWidget(finderButton, 1, 1);
scrollWidgetLayout->addWidget(widget);
}

wxWidgets trying to fill whole area

I've got a problem when I'm trying to prepare gui using wxWidgets.
I want to create some buttons which will cover whole area under the textbox.
It looks pretty nice when program starts, but when I want to resize it horizontally the buttons are not moving. Any ideas?
mainFrame::mainFrame(std::string title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(800, 600))
{
wxBoxSizer* vertSizer = new wxBoxSizer(wxVERTICAL);
vertSizer->Add(
new wxTextCtrl(this, -1, "My text", wxDefaultPosition, wxSize(100,80), wxTE_MULTILINE),
1, // vertically stretchable
wxEXPAND | wxALL, // horizontally stretchable, borders all around
10); // Add text box to parent sizer
wxBoxSizer* horizSizer = new wxBoxSizer(wxHORIZONTAL); // Make child sizer, will contain buttons
wxSizerFlags ButtonFlags(1); // Make controls stretch hoizontally (to cover entire sizer area)
ButtonFlags.Expand().Center().Border(wxALL,10); // Make controls expand vertically, add border
horizSizer->Add(new wxButton(this,123,"OK"), ButtonFlags); // Add first button
horizSizer->Add(new wxButton(this, 124,"Abort"), ButtonFlags); // Add second button
horizSizer->Add(new wxButton(this, 125,"Ignore"), ButtonFlags); // Add third button
vertSizer->Add(horizSizer); // Add child sizer to parent sizer
SetSizerAndFit(vertSizer);
}
This happens because you didn't tell your main sizer (vertSizer) to grow the horizSizer to fill all the available space, you need to add wxSizerFlags().Expand() to the one but last line to fix this.
Additionally, using ButtonFlags.Expand().Center() doesn't make sense: you can either expand or centre, but not both at once, so choose one. Finally, DoubleBorder() is preferable to Border(wxALL, 10).

GUI layout with wxSizer, wxSashWindow

I'm trying to create a window which is splitted into 2, 3, 4 etcc different resizable views in mainwindow and I'd like to implement that with wxWidgets in C::B. Actually th canvas itself splits the windows in the requested numbers, but it doesn't place any sash alongn the border of views so that it's very diffciult to notice which view starts where and ends where.
I create everything on run time and I was planing to place the sash, or panels around borders of views and hoping to work in my case but the main frame is reluctant to place the panels, and sashwidnwos at the correct position and stops processing the OnSize event. That is previously working codes is not functioning properly if I add the below code with sizers and panels during resizing.
MainFrame *frame = new MainFrame(NULL, wxT("wxWidgets OSG Sample"),
wxDefaultPosition, wxSize(width, height));
wxToolBar* toolbar = new wxToolBar(frame, wxID_ANY,wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL);
//wxSashLayoutWindow* win = new wxSashLayoutWindow(frame, ID_WINDOW_TOP,wxPoint(50,10), wxSize(200, 30),
// wxSW_3D | wxCLIP_CHILDREN);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxPanel* panel = new wxPanel(frame, ID_TBbutton, wxPoint(300,0), wxSize(5,500));
panel->SetWindowStyle(wxDOUBLE_BORDER);
// panel->SetStyle(wx_3D)
wxPanel* panel1 = new wxPanel(frame, ID_TBbutton);
sizer->Add( panel, 0, wxALL, 0 );
frame->SetSizer(sizer);
frame->SetToolBar(toolbar);
Please find below very basic example of MDI form with wxSashLayoutWindow:
bool SashWindowTestApp::OnInit()
{
wxMDIParentFrame* mainFrame = new wxMDIParentFrame(NULL, ID_TEST_FRAME,
wxT("Sash window test"), wxPoint(0, 0), wxSize(500, 400),
wxDEFAULT_FRAME_STYLE);
// top window
wxSashLayoutWindow* topWindow = new wxSashLayoutWindow(mainFrame,
ID_WINDOW_TOP, wxDefaultPosition, wxSize(200, 100),
wxSW_3D);
topWindow->SetDefaultSize(wxSize(1000, 100));
topWindow->SetAlignment(wxLAYOUT_TOP);
topWindow->SetBackgroundColour(*wxGREEN);
topWindow->SetSashVisible(wxSASH_BOTTOM, true);
// bottom window
wxSashLayoutWindow* bottomWindow = new wxSashLayoutWindow(mainFrame,
ID_WINDOW_BOTTOM, wxDefaultPosition, wxSize(200, 200),
wxSW_3D);
bottomWindow->SetDefaultSize(wxSize(1000, 200));
bottomWindow->SetAlignment(wxLAYOUT_BOTTOM);
bottomWindow->SetBackgroundColour(*wxYELLOW);
bottomWindow->SetSashVisible(wxSASH_TOP, true);
wxLayoutAlgorithm layout;
layout.LayoutMDIFrame(mainFrame);
mainFrame->Show(true);
return true;
}
In order to have a fully working sash window your frame has to react on EVT_SASH_DRAGGED

wxScrolledWindow scrollbar not working

I have the following child-parent relasions in my application:
The problem is that there is no visible ( or working ) scrollbar in wxScrolledWindow.
Code:
wxCollapsiblePane* collpane = new wxCollapsiblePane(this, ID_COLLPANE, "Tiles\t\t\t\t", wxDefaultPosition, wxDefaultSize, wxCP_NO_TLW_RESIZE);
wxSizer* sz = new wxBoxSizer(wxVERTICAL);
sz->Add(collpane,0,wxALIGN_RIGHT,10);
SetSizer(sz);
wxWindow* cPane = collpane->GetPane();
wxSizer* panesz = new wxBoxSizer(wxVERTICAL);
wxBitmap pic("test.bmp",wxBITMAP_TYPE_BMP);
wxScrolledWindow* scr = new wxScrolledWindow(cPane,ID_PANEL,wxDefaultPosition, wxSize(150,300));
scr->SetScrollbars(2,2,10,10);
wxClientDC dc(this);
scr->DoPrepareDC(dc);
panesz->Add( new wxBitmapButton(scr,-1,pic, wxDefaultPosition, wxSize(50,50) ), 1, wxALIGN_CENTER, 0 );
/* more button adding here */
cPane->SetSizer(panesz);
panesz->SetSizeHints(cPane);
How can I get the scrollbar to show and scroll the buttons?
To have scrollbars you need to indicate to wxScrolledWindow its full logical (as opposed to possibly smaller physical) size using SetVirtualSize() method. This can be done either just by calling it directly or by associating a sizer with the scrolled window, adding elements to this sizer and calling FitInside().
In the default state, for the scroll bars to appear the virtual size of the window has to be larger than the actual size of the window.
scr->SetVirtualSize(wxSize(2000,2000));
However, in version 3.1 this is not enough to get the scroll bars to appear. One also has to set the scroll rate. (This feels like a bug to me as it seems there ought to be a default setting that works.)
scr->SetScrollRate(1, 1);
You should modify the
wxWindow* cPane = collpane->GetPane();
wxSizer* panesz = new wxBoxSizer(wxVERTICAL);
wxBitmap pic("test.bmp",wxBITMAP_TYPE_B ...
You should put a jpg. After modifying this, it will work for sure ;) Trust me.