wxWidgets trying to fill whole area - c++

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).

Related

Is possible to have a widget that doesnt scroll in a QScrollArea?

Given this example:
Suppose A and B are QWidgets
Is it possible to keep everything starting from B static when the QScrollArea widget is scrolled?
The QScrollArea occupies the entire GUI, B is inside it to let the vertical ScrollBar closer to the right border of the GUI, or it would look like this:
What I can do in this case?
It is possible. You have to make use of the scrollbar being a widget of its own.
Here is the method:
Change your window in Qt Designer to be: spacer, QScrollArea (that will contain widget A), spacer, widget B, spacer; everything is in a QGridLayout (or QHBoxLayout but please read until the end).It is because widget B is outside the scroll area that it will not move during scrolling.
In your widget constructor, after ui->setupUi(); reparent and move the vertical scrollbar of your QScrollArea with these lines of code:
scrollArea->verticalScrollBar()->setParent(w);
layout->addWidget(sa->verticalScrollBar()); //Adds the scrollbar to the right of the layout.
Note about the margins:Obviously, you can easily push the scrollbar to the very right of your window by setting the layout right margin to 0.
If you also want it to cover the entire height of your window, while keeping some space between the other widgets and the window's border, that is where a QHBoxLayout will not suffice and you need a QGridLayout instead, set its top and bottom margin to 0 and add spacers (fixed size) to obtain the same visual result.
The C++ code for such a window would be:
QWidget* widget = new QWidget();
QGridLayout* layout = new QGridLayout(widget);
layout->setSpacing(0);
layout->setContentsMargins(9, 0, 0, 0);
widget->setLayout(layout);
QScrollArea* scrollArea = new QScrollArea(widget);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QWidget* widgetB = new QLabel("Widget B", widget);
//Creates the spacer that will "simulate" the top and bottom margins
QSpacerItem* topSpacer = new QSpacerItem(0, 9, QSizePolicy::Minimum, QSizePolicy::Fixed),
* bottomSpacer = new QSpacerItem(0, 9, QSizePolicy::Minimum, QSizePolicy::Fixed);
layout->addItem(topSpacer, 0, 0);
layout->addWidget(scrollArea, 1, 0);
layout->addItem(bottomSpacer, 2, 0);
layout->addWidget(widgetB, 1, 1);
//Moves the scrollbar outside the scroll area
scrollArea->verticalScrollBar()->setParent(widget);
layout->addWidget(scrollArea->verticalScrollBar(), 0, 2, 3, 1);
QLabel* innerLabel = new QLabel("Some big label to force the scrolling");
scrollArea->setWidget(innerLabel);
innerLabel->setMinimumHeight(500);
widget->show();

wxWidgets GridSizer puts all buttons in the same position

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);

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).

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.