Nothing showing up in QScrollArea - c++

I have a nice widget that basically looks like a dialog box with a bunch of QSliders on it. The number of sliders varies depending on the situation when the dialog (not an actual QDialog; just a QWidget) is invoked.
Since the varying number of sliders causes the box to be different sizes at different times, I now want to clean things up a bit by confining the sliders to a QScrollArea. If I understand things correctly, such a scroll area would display however many sliders fit within its height, and one could scroll down to see the rest if there were more.
Anyway, I tried a (somewhat complicated) procedure like this:
In constructor of custom QWidget class (m_variableName = member variable):
CustomScrollBox::CustomScrollBox(QWidget* _parent){
setWindowTitle(...);
...
m_scrollArea = new QScrollArea(this);
m_scrollAreaBox = new QGroupBox(m_scrollArea);
m_layout = new QGridLayout();
m_scrollAreaBox->setLayout(m_layout);
m_scrollArea->setWidget(m_scrollAreaBox);
m_scrollArea->setFixedHeight(250);
m_bottomButton = new QPushButton(this); //probably irrelevant
...
[connect calls, etc.]
}
After the constructor, the real, situation-dependent set-up of sliders occurs:
void
CustomScrollBox::SetUpWidgets(){
for([however many sliders the situation calls for]){
CustomSlider* s = new CustomSlider(this, label); //just a QWidget consisting of a
//QSlider and a QLabel to
//the left of it
..
m_layout->addWidget(s, [grid dimensions as needed]);
}
...
[set text on bottom button, etc., and add it as well]
}
This process causes nothing to show up on the overall dialog, except for an immobile scroll bar on the left. What, if possible, is the proper order of initialization steps to make this work? My guess is that I might have given something the wrong parent or set a layout at the wrong time, but the rearrganements I've tried so far haven't worked...

First of all you do not need to create explicit members for child widgets and layout to your CustomScrollBox unless you need to access them later (even then you might track them down through their child relationship to your CustomScrollBox). In particular, having set the layout of a widget you can use QWidget::layout to get QLayout* and downcast it to QGridLayout* or QVBoxLayout*. Secondly you are supplying parents to most of the child widgets ctors. Normally you should not do that as e.g. the layout to which the widget is added will take the ownership i.e. the layout will become parent to the added widget. Below is in principle what I would do. It will point you in a better direction at least.
CustomScrollBox::CustomScrollBox(QWidget* parent)
: QWidget(parent)
{
setWindowTitle(...);
...
QVBoxLayout* vBoxLayout(new QVBoxLayout);
QScrollArea* scrollArea(new QScrollArea);
vBoxLayout->addWidget(scrollArea);
QGroupBox* groupBox(new QGroupBox);
QGridLayout* gridLayout(new QGridLayout);
gridLayout->addWidget(.../*whatever buttons etc*/)
groupBox->setLayout(gridLayout);
scrollArea->setWidget(groupBox);
setLayout(vBoxLayout);
...
[connect calls, etc.]
}

Related

In QT "ui->scrollArea->horizontalScrollBar()->setValue(0)" has no effect

So in my UI designer I have a ScrollArea Widget, I then in my MainWindow.cpp, I create a QGraphicScene and a QGraphics View. I create a new widget and give that widget a QVBoxLayout so that it will auto-size(which is correct to my understanding).
I then use ui->scrollArea->setWidget(widget); to make this new widget the child of my scrollView.
All of this seems correct because I have scroll bars that I can navigate my scene with. However; using the line ui->scrollArea->horizontalScrollBar()->setValue(0); still has no effect on the scroll bar values.
scene = new QGraphicsScene();
scene->setSceneRect(0,0,2500,2500);
view = new QGraphicsView(scene);
QWidget *widget = new QWidget;
view->setBackgroundBrush(Qt::white);
QVBoxLayout* bLayout = new QVBoxLayout(widget);
ui->scrollArea->setWidget(widget);
bLayout->addWidget(view);
widget->show();
ui->scrollArea->horizontalScrollBar()->setValue(0);
ui->scrollArea->verticalScrollBar()->setValue(0);
I just had the this problem. Then, after debugging with ui->scrollArea->verticalScrollBar()->value() I realized that the scrolling area has no size before the component is show on the screen, i.e., it does not change the scrolling because it is not visible yet.
This is a sample Python code, but the is the same for C++, except the Language Syntax.
from PyQt5.QtWidgets import QDialog
...
dialog = QDialog()
...
verticalScrollBar = dialog.QPlainTextEdit.verticalScrollBar()
horizontalScrollBar = dialog.QPlainTextEdit.horizontalScrollBar()
# Has no effect, if you print the values, you will see always 0
verticalScrollBar.setValue( horizontalScrollBar.maximum() )
horizontalScrollBar.setValue( horizontalScrollBar.minimum() )
dialog.show()
# Now it has effect
verticalScrollBar.setValue( horizontalScrollBar.maximum() )
horizontalScrollBar.setValue( horizontalScrollBar.minimum() )
Autoscroll PyQT QTextWidget
If you are sure to address the correct scrollbar (as pointed in the comments by thuga), maybe check if your scroll area is modified after that init. I mean I'm not sure of the bahaviour, but if you modified some text in your widget for example, I think the scrollbar will be impacted.
You may need to catch some of your widget's event to reapply those:
ui->scrollArea->horizontalScrollBar()->setValue(0);
ui->scrollArea->verticalScrollBar()->setValue(0);
If it doesn't help, you should try to debug by tracking scrollbar value with
ui->scrollArea->verticalScrollBar()->value()
and see if your set is well applied, and maybe when it is overriden
Just in case, you may also try some of the methods indicated here, but it's probably not relevant to your issue: setValue of ScrollBar Don't work at first time

Placing QWidgets at specified coordinates?

Context: I'm making Risk (the popular board game) in C++/Qt, and I've run into a problem. I decided to make the map interactive by placing buttons on every country, which could then be clicked on. After some experimenting, I've subclassed QGraphicsPixmapItem for the buttons, and stuck them inside a QGraphicsScene and a QGraphicsView. I've made the world map a background image via CSS, so that buttons could be overlaid without much hassle.
My problem: I want to place those buttons at specific coordinates. (If it matters, those coordinates would be absolute.) All of the interfaces that I've made so far, I've done in code - I'm not familiar with the Qt Designer, so I'm looking for a function or set of functions that'd let me place my buttons (more or less) where I want them.
What I've tried: I looked in the documentation, but couldn't find a function that let me control where items were placed, just ones that organized items in various ways - columns, horizontal boxes, vertical boxes, etc.
When I designed QWidgets before, I'd done so by placing buttons, other widgets, etc. in QLayouts, but there don't seem to be layouts that allow me the control I'd like. The only one I can see that'd do something similar, is QGridLayout, and experiments with that so far haven't worked the way I wanted them to - the buttons don't get anywhere near the edges of the map, no matter how many columns or rows I add.
The easiest solution would be giving up and placing the buttons beside the map, of course, but that's a last-ditch solution.
Thanks in advance.
EDIT: Added example source code, for clarity.
QHBoxLayout* layout = new QHBoxLayout;
TerritoryButton* test = new TerritoryButton(QPixmap("img.png"));
TerritoryButton* test2 = new TerritoryButton(QPixmap("img.png"));
QGraphicsScene* scene = new QGraphicsScene;
QGraphicsScene* scene2 = new QGraphicsScene;
scene->addItem(test);
scene2->addItem(test2);
QGraphicsView* view = new QGraphicsView(scene);
QGraphicsView* view2 = new QGraphicsView(scene2);
layout->addWidget(view);
layout->addWidget(view2);
setLayout(layout);
setFixedSize(1000, 512);
QGraphicsPixmapItem inherits QGraphicsItem, so you can call setPos(x, y) (after inserting the pixmap item into the scene with addItem).
void QGraphicsItem::setPos(const QPointF &pos)
Sets the position of the item to pos, which is in parent coordinates. For
items with no parent, pos is in scene coordinates.
The position of the item describes its origin (local coordinate (0, 0)) in parent coordinates.

How to expand widgets with window resize?

I have a simple qt application with a QTabWidget inside the main window. I also have a few QPushButton(s) and QRadioButton(s).
What I want is that when I resize the window either manually or by maximizing/minimizing it should resize the containers in the same way.
In other words, what I want is equivalent of DockStyle.Fill in qt C++
How can I do that ?
In Qt you have to use Layouts:
The Qt layout system provides a simple and powerful way of automatically arranging child widgets within a widget to ensure that
they make good use of the available space.
In short, all components in a layout will be relocated to new places after the window, to which the layout belongs, is resized.
If you are using deisgner:
1. Click the empty space of a widget to select itself(or a main Window, I use just a base widget here for demonstration), and the layout option will be hightlighted:
2. Choose a desired layout
Here is what object monitor looks like after a QVBoxLayout is used:
If your widget doesn't use layout, it will look like this:
What we have done here is to make the base widget/mainWindow equip a main layout. You can see that the buttons are automatically aligned, when you resize the widget, those component will be relocated according to the layout:
Perhaps you will find it nettlesome of those expanding space, so the next move is to add a Spacer to the layout; so when layout is resized, only the spacer will stretch.
(Another option is to make your widgets expandable, see ** at the end of this post)
3. Besides, you can add a layout into another to create a nested layout
For example, first I choose A and B (by pressing Ctrl) and use QVBoxLayout. This additional layout is not base layout and hence highlighted by red rectangle.
Then I choose C and the layout which contains A & B, and use QHBoxLayout on them,
Finally I use another QVBoxLayout as my main layout on the base widget, just like what we did previously.
And the object monitor:
If you like the special feeling of hitting keyboard and always handcraft the code:
For the last example:
QWidget *Form = new QWidget;
QPushButton *pushButton_A = new QPushButton("A");
QPushButton *pushButton_B = new QPushButton("B");
QPushButton *pushButton_C = new QPushButton("C");
QVBoxLayout *verticalLayout = new QVBoxLayout;
QHBoxLayout *horizontalLayout = new QHBoxLayout;
QVBoxLayout *mainLayout = new QVBoxLayout;
verticalLayout->addWidget(pushButton_A);
verticalLayout->addWidget(pushButton_B);
horizontalLayout->addWidget(pushButton_C);
horizontalLayout->addLayout(verticalLayout);
mainLayout->addLayout(horizontalLayout);
Form->setLayout(mainLayout);
Form->show();
In your case
Here is an example of layout:
Notice that QMainWidget has a centralwidget as a base widget. Besides, each tab of QTabWidget has it's own base widget (tab and tab_2 in the picture) which adopts another base layout.
*Don't forget to add Spacer in layouts to shape them as you like.
** You can set size policy on each widget (QTabWidget, QPushButton etc) to make them horizontally/vertically expandable or fixed, this cooperates with the layout strategy. For example, in the very begin example if we set
button A to be vertically fixed, horizontally expanding
button B to be vertically expanding, horizontally expanding
button C to be vertically expanding, horizontally fixed
It will look like this when resizing:
you need to look into how to use layouts in your application
http://qt-project.org/doc/qt-4.8/layout.html
As a quick and simple first try, in the Designer you can right-click on the main window, and choose "layout" from the drop-down menu. Here you can pick the grid layout, for instance.

How to add a widget to a pre-existing QLayout?

I'm looking for a super simple example, and can't seem to find one. I have a MainWindow. When a button gets pressed I want to create a new window that gets opened up in the layout of MainWindow, to become a part of the mainwindow.
I have the code that sets up when a button is pushed to call this slot...when it gets called my QLabel shows up, but my QWidget does not
QWidget *test = new QWidget();
test->setGeometry(QRect(100,100,100,100));
layout->addWidget(test,0,0)
//Operation Mode
QLabel *operationalModeLabel1 = new QLabel("TEST");
layout->addWidget(operationalModeLabel1,2,1);
The reason for "lack of examples" is that you think of it wrong. What you describe is done all the time, by every single Qt example that uses layouts! I mean it. It doesn't matter when you add a widget to a layout. There's nothing magical about adding widgets "now" vs. adding them "later".
Just think of the title of the question: it makes no sense. All widgets must be added to layouts that already exist! By definition, no less. If there's no layout, how could you add a widget to it?
Your code is wrong, that's all. It's always pointless to set a geometry on a widget that is to be managed by a layout. As soon as you add it to the layout, the layout will change the geometry.
Since you're adding an empty widget into the layout, you most likely won't be able to see it. That's why the label shows up - it's not an empty widget.
If you want a widget that has a fixed size, to make it easier to notice, just set the fixed size on it. Even better, make it red so that it stands out.
QWidget * test = new QWidget();
test->setStyleSheet("QWidget { background-color: red }");
test->setFixedSize(100, 100);
layout->addWidget(test, 0, 0);

QSpacerItem In QFormLayout - Vertical Expand

I would like to force an expanding space in my QFormLayout, but no matter what QFormLayout only uses the QSpaceItem::sizeHint(). Does anyone know a way around this, or the proper way to handle this?
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
SetupLayout();
}
void MyWidget::SetupLayout()
{
QFormLayout * layout = new QFormLayout();
layout->addRow("Something1", new QComboBox());
layout->addRow("Something2", new QSpinBox());
//Spacer
layout->addItem(new QSpacerItem(0,10, QSizePolicy::Expanding, QSizePolicy::Expanding));
layout->addRow(QPushButton("Something3"));
setLayout(layout);
}
So there were a few different issues:
QFormLayout do not expand like other layouts. My widgets (a few of them) were being placed into a QFormLayout. This prevented them from expanding. I switched my main parent layout from QFormLayout to QVBoxLayout. This made me have to use QLayout::setAlignment(Qt::AlignTop)
This fixed a few problems with a few of my other widgets not expanding. However these widgets used QVBoxLayout. The widget above uses a QFormLayout. To get this expand, I had to use the following line in my QSpacerItem:
QSpacerItem * my_spacer = new QSpacerItem(0,1000, QSizePolicy::Expanding, QSizePolicy::Expanding);
I am supplying some example code. The goal is to show the hierarchy, and where QFormLayout would cause trouble.
Example code:
//A main Widget class
void SetupLayout()
{
QHBoxLayout * main_layout = new QHBoxLayout();
main_layout->addWidget(Some_Widget);
//Create a control widget
control_widget = new QWidget(); // IMPORTANT control_widget is a member
QVBoxLayout * layout = new QVBoxLayout(); //IMPORTANT!!!! - Here it was QFormLayout
layout->setAlignment(Qt::AlignTop); //IMPORTANT - Needed this after switching to QVBoxLayout
layout->addWidget(new QComboBox("stuff")); //Some combo box
control_widget->setLayout(layout);
main_layout->addWidget(control_widget);
}
//Later on, we have a "Put a new widget in the control area" task
void NewControlArea()
{
if(current_control)
control_widget->removeWidget(current_control); //current_control is a member variable
current_control = new MyWidget(); //FROM ABOVE
control_widget->addWidget(current_control);
}
If MyWidget uses a QFormLayout, things are not expanded unless I add spacers with size hints. However, if MyWidget uses a QVBoxLayout, any QWidgets inside it are expanded correctly.
After lot of time with manual as well as lots of tries i guess it's impossible to do what you want using QFormLayout.
This layout is desinged for windows with lot of fields to fill, but only for that.
If you want to add bigger spacing between sections of your form you can use QVBoxLayout with a couple of QFormLayout's inside it separated by spacings.
Notice that in this case each section will have own width of first and second column so maybe that is not the best solution.
The other solution (if we are talking about grouping some options) is to use a couple of QGroupBoxes with QFormLayouts in it. The groups will not be separated by growing spacing, but it will be very readable and you can name your groups. If grouping options is what you want to do - this is probably the most common and user friendly way to do this.
If you only want visual effect you pointed - columns with same width in every section and growing spacing between sections, you can use QGridLayout with 2 columns and add spacers in rows between sections. In this case you have to create QLabel to put into first column by yourself.
Just for posterity, I was having a similar problem. I found that having an organization like the following would cause anything in the inner layout (QHBoxLayout) to not expand vertically as they would if I had dropped them into the QFormLayout by themselves:
QFormLayout
-QHBoxLayout
--QListWidget
However, if I added a layer of indirection by putting the HBoxLayout into a QWidget, then sizing worked correctly:
QFormLayout
-QWidget
--QHBoxLayout
---QListWidget
So you might try adding a QWidget in there and putting your spacer inside of it.
What I did is probably a bit of hacking and is not very elegant, yet it did the trick in just one row:
addRow(" ", (QWidget*)nullptr);
This doubles the space between rows added before and after this call.
I suspect that MyWidget has the wrong size. The layout will use the widget's size to work out how best to layout its own items. Just because one of those items is set to an expanding policy does not mean that the layout will force the size of MyWidget to expand. If the size of MyWidget is fixed there is nothing the internal layout can do.
If you make MyWidget bigger you should see the layout working as you hope. Perhaps you need to put MyWidget in a vertical layout with its policy set to expanding? Without knowing how MyWidget gets its size it's hard to be sure.