wx* layout in wxNotebookPage - c++

i'm trying to program the front-end for a schedule-application. In my main tab should be two lists (one above the other one). But this seems to be impossible (I've sat yesterday the half day and today for three more hours). So this is my result:
And this is the common part of my Code:
// LayoutManagement - Main
sizer = new wxBoxSizer(wxVERTICAL);
this->SetSizer(sizer);
// Tabpane
tab_pane = new wxNotebook(this,TAB_PANE);
sizer->Add(tab_pane,1,wxEXPAND);
// Overview tab
wxBoxSizer *ot_sizer = new wxBoxSizer(wxVERTICAL);
overview_tab = new wxNotebookPage(tab_pane,TAB_OVERVIEW);
overview_tab->SetSizer(ot_sizer);
tab_pane->AddPage(overview_tab,_("Countdown und Nachrichten"),true);
// Schedule
schedule_list = new wxListBox(overview_tab, SCHEDULE_LIST);
ot_sizer->Add(schedule_list,1,wxEXPAND);
// NewsList
news_list = new wxListBox(overview_tab,NEWS_LIST);
ot_sizer->Add(news_list,3,wxEXPAND);
And my header (part):
wxListBox *schedule_list;
wxBoxSizer *sizer;
wxListBox *news_list;
wxNotebook *tab_pane;
wxNotebookPage *overview_tab, *settings_tab, *about_end_tab;
What i'm doing wrong?
Greetings
Michael
(Sorry for my bad english)

The reason this code is not working is that you have called SetSizer() BEFORE adding the widgets to the sizer. You should either move the call to SetSizer AFTER the addition of widgets to the sizer, or force the sizer to redo the layout after the adds.
ot_sizer->Add(news_list,3,wxEXPAND);
// all widgest added to sizer, now arrange layout as specified
ot_sizer->Layout();
This should fix your code for the moment.
A final comment on the structure of your code: you seem to be constructing everything in one large code section. This is not a great idea. As you continue to add elements to your GUI it is going to become very confusing. It is better to subclass each page and create the widgets that belong to each page in the constructor of that page.
class cOverviewTab : public wxNotebookPage
{
public:
cOverviewTab() : wxNotebookPage(tab_pane,TAB_OVERVIEW)
{
wxBoxSizer *ot_sizer = new wxBoxSizer(wxVERTICAL);
ot_sizer->Add( new wxListBox(overview_tab, SCHEDULE_LIST) );
ot_sizer->Add( new wxListBox(overview_tab,NEWS_LIST) );
SetSizer( ot_sizer );
}
};

I had the same problem, and it took me about 2 hours to fix it.
There is no reason to change other lines in your code. You have just to use the wxPanel class instead of wxNotebookPage to create new pages for the wxNotebook object. This is because wxNotebookPage is just a typedef of wxWindow and wxWindow doesn't have some necessary functionality that the wxPanel has.
Your other code doesn't need modifications for this to work, except that it would be really good as noted above to subclass your pages to avoid GUI mess.

Related

Scroll Area Added and Set Up but No Scrollbar Appears

I've seen and tried various QT scrollArea solutions over the past 2 days but none of them work for me. Here's my scroll area setup code as it stands in the MainWindow constructor. This builds and runs without error but doesn't do anything. The scrollArea and ui->Contents have already been set up in the form using QTcreator and the needed widgets have been moved into the scrollArea.
ui->scrollArea->installEventFilter(this);
ui->scrollArea->setMouseTracking(true);
ui->scrollArea->setWidget(ui->Contents);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setSizeConstraint(QLayout::SetMinimumSize);
ui->scrollArea->setLayout(layout);
The last line seems interchangeable with:
layout->addWidget(ui->scrollArea)
but neither one changes the result, which is a fully-functioning application but without the scroll area I need.
I had similar problem which i solved by creating scrollArea and it's contents via code rather than form and only then using setWidget() method. I described the problem in this thread.
In your case code should look something like this:
QScrollArea *scrollArea;
scrollArea = new QScrollArea(this);
scrollArea->installEventFilter(this);
scrollArea->setMouseTracking(true);
scrollArea->setWidget(Contents);//whatever Contents is, i recommend creating it via code
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setSizeConstraint(QLayout::SetMinimumSize);
scrollArea->setLayout(layout);

Qt remove nested layout

I've got several QHBoxLayout objects nested inside a single QVBoxLayout. I've looked through a number of stackoverflow questions and answers, but I've not been able to find a way to completely remove the layout for the contents of the QScrollArea widget. All the answers I've seen have only made it possible to set the layout again, but when the layout does get set a second time, the objects are still present.
This is the code that I'm working with:
QSignalMapper* sMap = new QSignalMapper(this);
QVBoxLayout* vBox = new QVBoxLayout();
outerVector = 0;
for (vector<vector<QPushButton*>>::iterator o_iter = buttonGrid.begin(); o_iter < buttonGrid.end(); o_iter++) {
int innerVector = 0;
QHBoxLayout* hBox = new QHBoxLayout();
for (vector<QPushButton*>::iterator i_iter = (*o_iter).begin(); i_iter < (*o_iter).end(); i_iter++) {
hBox->addWidget(buttonGrid.at(outerVector).at(innerVector));
sMap->setMapping(buttonGrid.at(outerVector).at(innerVector), ((outerVector * 100) + innerVector));
connect(buttonGrid.at(outerVector).at(innerVector), SIGNAL(clicked()), sMap, SLOT(map()));
innerVector++;
}
vBox->addLayout(hBox);
outerVector++;
}
ui->GameAreaWidgetContents->setLayout(vBox);
connect(sMap, SIGNAL(mapped(int)), this, SLOT(on_buttonGrid_clicked(int)));
Right now, I have this for clearing the layout:
delete hBox;
delete vBox;
ui->GameAreaWidgetContents->layout();
What is the best, and most effective way to clear the contents of the widget?
I believe I've fixed this, This is less of a Qt issue, but more of a lack of clearing the vector<vector<QPushButton*>> buttonGrid object. It looked like the layout wasn't being cleared, because the additional QPushButton objects were being added onto the vector<vector<QPushButton*>> object.
It's a fairly rookie mistake on my behalf.
Updated:
I infer that GameAreaWidgetContents is a QScrollArea. To clear its layout manager, you can do:
delete ui->GameAreaWidgetContents->layout();
The vbox will no longer be the widget's layout manager and any nested children will be deleted automatically by the Qt parenting system.
From the docs on QWidget::setLayout():
If there already is a layout manager installed on this widget, QWidget won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.

How to logically group widgets in QT for easy show/hide?

I'm grouping a set of widgets in a parent and then I control the visibility/flow of these widgets by hiding/showing the parent. Is this a good way to achieve what I'm trying to do? Here is the code:
QVBoxLayout* l = new QVBoxLayout(this);
// .....
QWidget* toolset_frame = new QWidget(this);
{
QVBoxLayout* l = new QVBoxLayout(toolset_frame);
l->addWidget(new QLabel(tr("Stuff")));
this->Toolset = new QLineEdit(toolset_frame);
l->addWidget(this->Toolset);
}
l->addWidget(toolset_frame);
// Call toolset_frame->hide() and this hides everything inside the parent
The problem with this solution is that the children shrink in size slightly, I think this is due to some padding or border in the parent. Ideally the children should appear as if they are not contained in an intermediate object, but rather flow with the parent. In this case the horizontal size of the children should not be affected.
http://doc.qt.io/qt-5/qtwidgets-dialogs-extension-example.html
This example shows that your approach is correct. Using a widget to contain the elements you want to hide, and so on.
If you want the margins/content margins/padding to be less, then change it.
// in finddialog.cpp
extensionLayout->setMargin(0);
To quickly prototype what properties to change to get it to look right, try laying it out in the Qt Designer, and modify the property editor to get the look and feel you want.
Hope that helps.

How to remove widgets from layout in Qt

I have a piece of code which does this: a method named prepareUI makes the UI ready to be able to load search results that are fed into it. A method named onClear that is called when the results that are already showing needs to be cleared. And a method named populateSearchResults that takes the search data and loads the UI with it. The container that holds the data is a publicly available pointer, since it is needed to clear the results from onClear:
void MyClass::prepareSearchUI() {
//there may be many search results, hence need a scroll view to hold them
fResultsViewBox = new QScrollArea(this);
fResultsViewBox->setGeometry(28,169,224,232);
fSearchResultsLayout = new QGridLayout();
}
void MyClass::onClear() {
//I have tried this, this causes the problem, even though it clears the data correctly
delete fSearchResultContainer;
//tried this, does nothing
QLayoutItem *child;
while ((child = fSearchResultsLayout->takeAt(0)) != 0) {
...
delete child;
}
}
void MyClass::populateWithSearchesults(std::vector<std::string> &aSearchItems) {
fSearchResultContainer = new QWidget();
fSearchResultContainer->setLayout(fSearchResultsLayout);
for (int rowNum = 0; rowNum < aSearchItems.size(); rowNum++) {
QHBoxLayout *row = new QHBoxLayout();
//populate the row with some widgets, all allocated through 'new', without specifying any parent, like
QPushButton *loc = new QPushButton("Foo");
row->addWidget(loc);
fSearchResultsLayout->addLayout(row, rowNum, 0,1,2);
}
fResultsViewBox->setWidget(fSearchResultContainer);
}
Problem is, when I call onClear which internally calls delete, it does remove all the results that were showing. But after that, if I call populateWithSearchesults again, my app crashes, and the stack trace shows this method as where it crashed.
How do I fix this problem?
It seems that you have some misconceptions about ownership. A QLayout takes ownership of any item that is added to it: http://doc.qt.io/qt-5/qlayout.html#addItem
That means the QLayout is responsible for deleting these items. If you delete them then the QLayout will also try to delete them and then you get the crash you're seeing now.
QLayout doesn't have good functionality for deleting contents and re-adding them (for example removeWidget probably doesn't work as you would hope.) But there's a reason for this.
QLayout is not intended to be used as a list view.
What you do want is a, wait for it, QListView. Which will even handle the scroll functionality for you, and make adding and removing elements a possibility.
Actually you can solve this issue easily, even if you are using QGridLayout or any other Layouts :
subLayout->removeWidget(m_visibleCheckBox);//removes and then it causes some zigzag drawing at (0,0)
m_visibleCheckBox->setVisible(false);//asks to redraw the component internally
setLayout(subLayout);//for safety as we may use that layout again and again
If you just use the first line it will cause this :

How to tackle this design obstacle in Qt?

I need to draw a form that would have some options on the left, this can be seen in the following diagram:
Now, when a user clicks on OptionA, a separate related layout would be shown on the right.
and when a user clicks on OptionB, a separate related layout would be shown on the right.
My current design approach for such type of a problem is as follows:
Have a form with a horizontal layout with two frames. One frame has the options, while the other frame would be hosting other forms. So, in my case optionA would have a separate form, say formA, OptionB would have its own form, say formB, and OptionC would have its own form, say formC.
Now when ever a user clicks on OptionA the formA would be displayed inside FrameHost, similarly when user clicks on OptionC, formC would be displayed inside FrameHost. My question is if this is a good approach giving each option an independent form?
My other thought is the opposite which is to have all the forms (A, B and C) layouts inside a separate frame inside one form and when user clicks on OptionA the frame that has FormA content would be made visible while others would be hidden. So, one frame would be shown at a time. What would be the best approach to tackle this kind of a problem?
The first approach is OK, but lacks the concept of QStackWidget. I would personally use QStackedWidget for showing the forms depending on the exclusive radiobutton clicked.
The second approach is very hackish because it is trying to immitate the fact that these forms would be displayed in the same place, yet, it would render them vertically tricking on the visible/hidden property.
I would write the code like this:
MyWidget *formA = new MyWidget;
MyWidget *formB = new MyWidget;
MyWidget *formC = new MyWidget;
QStackedWidget *stackedWidget = new QStackedWidget;
stackedWidget->addWidget(formA);
stackedWidget->addWidget(formB);
stackedWidget->addWidget(formC);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(stackedWidget);
setLayout(layout);
connect(myButtonGroup, SIGNAL(clicked(int)), SLOT(setCurrentIndex(int)));