How to create a scrollable QVBoxLayout? - qt

I'm trying to put a QVBoxLayout inside a QScrollArea in order for it to be scrollable vertically. However items don't seem to be added to it.
I saw a suggestion that I ought to create an inner widget that the ScrollArea uses and to place the layout inside that, although it doesn't seem to have worked. My structure is supposed to look like this:
+-------------------------------
| QScrollArea(realmScroll)
| +----------------------------
| | QWidget(realmScrollInner)
| | +-------------------------
| | | QVBoxLayout(realmLayout)
And the code to do this:
# Irrelevant, added for context (this works)
centralWidget = QWidget(self)
self.container = QVBoxLayout(centralWidget)
centralWidget.setLayout(self.container)
self.setCentralWidget(centralWidget)
# Where trouble starts
self.realmScroll = QScrollArea(self.container.widget())
self.realmScroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.realmLayout = QVBoxLayout(self.container.widget())
self.realmScrollInner = QWidget(self.realmScroll)
self.realmScrollInner.setLayout(self.realmLayout)
self.realmScroll.setWidget(self.realmScrollInner)
self.container.addWidget(self.realmScroll)
# Doesn't add to realmLayout
self.realmLayout.addWidget(QLabel("test"))
I'm still learning Qt (2 days in), so in-depth answers to where I'm going wrong would be appreciated.
Update:
It seems that the addWidget(QLabel()) works right up until the realmScrollInner has been set as realmScroll's widget. Since I'd like to add elements after the UI has been displayed I have to do this, which I'm not sure is really correct:
self.realmLayout.addWidget(QLabel("test"))
# realmScrollInner bound to realmScroll
realmScroll.setWidget(realmScrollInner)
self.container.addWidget(realmScroll)
# Access realmScroll's widget and then layout to add
realmScroll.widget().layout().addWidget(QLabel("test"))
But if you remove that first call to addWidget before the widget has been bound (so the layout has no widgets), then bind to the ScrollArea widgets added afterwards are not displayed. Perhaps the ScrollArea needs repainting (although I don't see a method for that)?
Update 2: Calling repaint() on realmScroll or its contained widget does nothing, as does calling activate/update() on the layout.

It turned out that I was lead down a wrong path by putting the layout as the layout of a widget. The actual way to do this is as simple as:
scrollarea = QScrollArea(parent.widget())
layout = QVBoxLayout(scrollarea)
realmScroll.setWidget(layout.widget())
layout.addWidget(QLabel("Test"))
Which I'm pretty sure I tried originally, but hey it's working.
However this adds an issue that the layout's items are shrunk vertically instead of causing the scrollarea to add a scrollbar.

OK, I just got done fighting with this. Here's a widget that can go into a scroll area (scrollarea->setWidget) and work correctly. It contains a QVBoxLayout and a list of label/listwidget pairs, each in their own little horizontal layout, and it does pretty much what you'd want.
The important thing was reading the QScrollArea docs section on Size Hints and Layouts, and finding the bit where having the sizeContraint QLayout::SetMinAndMaxSize on the layout would be necessary.
class MappingDisplayWidget : public QWidget
{
Q_OBJECT
public:
explicit MappingDisplayWidget(QWidget *parent = 0);
void addFile(QString name);
private:
QVBoxLayout *m_layout;
QMap<QString, QListWidget *> m_mappings;
};
MappingDisplayWidget::MappingDisplayWidget(QWidget *parent) :
QWidget(parent)
{
m_layout = new QVBoxLayout;
m_layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
setLayout(m_layout);
}
void MappingDisplayWidget::addFile(QString name) {
if (m_mappings.find(name) == m_mappings.end()) {
QWidget *widg = new QWidget;
QHBoxLayout *lay = new QHBoxLayout;
widg->setLayout(lay);
QLabel *nlab = new QLabel(name);
lay->addWidget(nlab);
QListWidget *list = new QListWidget;
lay->addWidget(list);
m_layout->addWidget(widg);
m_mappings[name] = list;
}
}
I keep pointers to the list widgets so that I can add stuff to them later, and that works fine.

Try calling
self.realmScroll.setWidgetResizable(True)

Related

Shared layout by widgets in QStackedWidget

I have a QStackedWidget with QWidgets. My QWidgets differ a bit, one has an additional button, other lacks a combo box and so on but all of them can be arranged in the same QGridLayout.
And that is exactly what I would like to achieve. I would like to have a QGridLayout in my QStackedWidget that is shared by all my QWidgets. Additionally my main window (QDockWidget) can be resized and I would like to set different column and row stretch for the grid layout.
Is there a clean solution for this?
I came up with two ideas.
One is to have a QGridLayout in each QWidget and connect them all together so that when one is resized, others do the same. However, the more QWidgets I had, the more complicated it would have been.
My second idea is to have one QGridLayout with QStackedWidget in each cell. The bigger the QGridLayout was, the harder it would be to maintain it.
None of my ideas seem to be good.
I am using PyQt4 but examples in C++ are welcome as well.
This doesn't seem to warrant anything more than maybe a shared addWidgetsToGridLayout function that can be used for each widget in the stack, which is a different object because it shows different things. Sharing widgets is bad in this setup, so make sure each widget in the stack has its own distinct widgets.
I don't understand why you would want to resize invisible widgets on resize. When you switch to another widget on the stack, Qt will make sure the proper events are called to properly resize the layout.
A layout cannot be shared. The layout system was not designed for it.
You could have a setup function that creates widgets in a layout, and returns the layout. The layout carries the widgets. You can then apply that layout to a widget, establishing the contents that way. The setup function can take parameters that customize its behavior. For example:
enum WidgetType {
Normal, WithButton, WithCombo
};
QGridLayout* setup(WidgetType type) {
auto l = new QGridLayout;
l.addWidget(0, 0, new QLabel("Hello"));
l.addWidget(0, 1, new QLabel("World"));
switch (type) {
case WithButton:
l.addWidget(1, 0, new QPushButton("Click Me!"));
break;
case WithCombo:
if (auto combo = new QComboBox) {
...
l.addWidget(1, 1, combo);
}
break;
case Normal:
break;
}
return l;
};
void test() {
QWidget w1, w2;
w1.setLayout(setup(Normal));
w2.setLayout(setup(WithButton));
}
Of course, presumably you'll want to easily refer to the widgets. Thus it's best that you create a custom widget that can take multple forms, and holds the widget by value or by pointer as needed:
class Widget : public QWidget {
QGridLayout m_layout{this};
QLabel m_label1("Hello");
QLabel m_label2("World");
QPointer<QPushButton> m_button;
QPointer<QComboBox> m_combo;
public:
Widget(WidgetType type, QWidget *parent = nullptr) : QWidget(parent) {
m_layout.addWidget(0, 0, &m_label1);
m_layout.addWidget(0, 1, &m_label2);
switch (type) {
case WithButton:
m_button = new QPushButton("Click Me!");
m_layout.addWidget(1, 0, m_button.data());
break;
case WithCombo:
m_combo = new QComboBox;
...
m_layout.addWidget(1, 1, m_combo.data());
break;
case Normal:
break;
}
}
};

Qt QLabel fails to resize

I implemented QLabel much like Qt's ImageViewer example, except I use QGridLayout for positioning my widgets. I also implemented similar lines for scaling my QLabel using QScrollBar, but QLabel just doesn't scale like it should inside the QScrollArea. I am not sure if it is related to some kind of GridLayout management issue or something else. I have been reading everywhere and trying different things for 3 days now. Below I list the relevant portion of my code.
In my Viewer class constructor:
{
imageLabel1 = new QLabel;
imageLabel1->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
imageLabel1->setScaledContents(true);
scrollArea1 = new QScrollArea;
scrollArea1->setWidget(imageLabel1);
scrollArea1->setWidgetResizable(true);
....
QGridLayout *centralLayout = new QGridLayout;
centralLayout->addWidget(scrollArea1, 0, 0);
...}
and my scaleImage method:
void Viewer::scaleImage1(int factor)
{
Q_ASSERT(imageLabel1->pixmap());
scaleFactor *= (1 + factor);
//imageLabel1->resize(scaleFactor* imageLabel1->pixmap()->size());
imageLabel1->pixmap()->toImage().scaled(scaleFactor* imageLabel1->pixmap()->size(), Qt::KeepAspectRatio, Qt::FastTransformation);
imageLabel1->adjustSize();
adjustScrollBar(scrollArea1->horizontalScrollBar(), factor);
adjustScrollBar(scrollArea1->verticalScrollBar(), factor);
imageLabel1->update();
}
My scaleImage1 function is a public slot, and it receives signal from a scrollbar that goes between 0 and 2 so that, into the scaleFactor, the imageLabel1 is designed to be capable of being zoomed in up to 3 times its original size. But when I run the code, I don’t observe the imageLabel becoming enlarged inside the QScrollArea, which I saw in the imageViewer demo. The imageLabel1 simply retains the original size as it is loaded and does not respond to the valueChange() of scrollbar.
I'd appreciate your advice/tips very much.
I think is because you set QSizePolicy::Minimum to the imageLabel, try with MinimumExpanding or other that better fit your needs.

Reversing the layout of a QToolButton

I would like to make a QToolButton where the text is the left of its icon. I've tried to find related information, and tried things with:
button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
but this sets the text to the right side of the button. I've looked into trying stylesheets, but text-alignment is only for push buttons. Is there any way to do this?
My highest-level goal is to make an interface bar which looks like this:
with a text label and image right beside it. Currently I'm using a toolbar with checkable toolbuttons because of the style (no border unless moused-over, still has the indent with checkable, contains text and an icon...). It's entirely possible that I'm using the wrong type widgets, so if I can't change the layout of this, is there any way to emulate this style?
This is what I currently have:
.
You can use QWidget::setLayoutDirection:
toolbar->setLayoutDirection(Qt::RightToLeft);
Your best bet is probably to create this functionality yourself in a QWidget. It should have a QLabel and a QToolButton (or QPushButton) arranged in a QHBoxLayout. Then you can add this custom widget to the toolbar using QToolBar::addWidget() or QToolBar::insertWidget().
Here's an example of a widget you could use:
#include <QtGui>
class CoolButton : public QWidget
{
public:
CoolButton(QWidget *parent = 0) : QWidget(parent)
{
QLabel *label = new QLabel("Hi!", this);
QToolButton *button = new QToolButton(this);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(label);
layout->addWidget(button);
setLayout(layout);
}
};

How to Add QListView/QListWidget to QGraphicsScene in Qt?

How to Add QListView/QListWidget to QGraphicsScene and add Widgets to ListView
When i Try to add QLisView to QGraphicsScene mouse scroll affects goes from Scene.
I want to add QPushButtons as ListView Items in QgraphicsScene with mouse scroll affect.
Thanks.
What about QGraphicsProxyWidget?
QListView *listView = new QListView;
QGraphicsProxyWidget *proxy = scene.addWidget(listView);
Then (or before that) you can populate the list with anything you want. QPushButton can be added to the list using setIndexWidget(). Also you might rethink the whole idea of having a QListView, and give it a try with QScrollArea and a linear layout containing buttons. That would require a bit more logic to organize items within the scroll area, but it should be more lightweight that QListView with custom widgets.
I second the answer above: ProxyWidget is the answer.
Here is my working code,
Header:
class CenterScreen{
private:
QListWidget* nameListWidget;
QGraphicsProxyWidget* nameProxyWidget;
...
C++ source:
void CenterScreen::addListView()
{
QGraphicsScene* scene = ui.centerGraphicsView->scene();
nameListWidget = new QListWidget();
nameProxyWidget = scene->addWidget(nameListWidget);
...
nameProxyWidget->hide(); // you can control your widget as you like

how to add an widget into the Form In QtDesigner

in qdesigner_workbench.cpp, how can I add a widget (say QLabel) into a FormWindow by code?
Since methods like createWidget()...etc are all abstract, how do I properly use the internal mechanics to add QLabel into the active FormWindow?
EDIT:
In qdesigner_workbench.cpp, this is currently what I have:
QDesignerFormWindowManagerInterface* fwm = core()->formWindowManager();
QDesignerFormWindowInterface* fw = fwm->activeFormWindow();
QWidget* mw = fw->mainContainer();
QLabel* label = new QLabel(mw); //can be added correctly but not in the right hierarchy
label->setText("I am a good girl.");
The mw (obtained from fw->mainContainer()) is actually a MainWindow, however the real data I need is in:
mw -> children[2] (which is a QDesignerWidget) -> children
There are 9 widgets in the designer, and you can see there's 9 arrays in children mentioned above; see this link (an image) for illustration.
http://img24.imagevenue.com/img.php?image=98871_a_122_476lo.jpg
So... how can I correctly add the QLabel widget?
Tried both
QLabel* label = new QLabel(fw); // will be a sibling of MainContainer, which is the QMainWindow (mw) in this case
QLabel* label = new QLabel(mw); // will be a sibling of QDesignerWidget
and apprarently either of the works.
If you want just to display a widget on a form, you can set your QMainWindow or QDialog to be the widget parent:
QLabel *l = new QLabel(this);
l->setText("My long string");
Where this is a pointer pointing to your current QDialog or QMainWindow.
Otherwise as ufukgun pointed out, you can use setCentralWidget if you need your widget to occupy the center of the QMainWindow.
You should add any QWidget to the QLayout of the form.This will put it into the display strategy of the form when resizing it.
form->ui->layout->add(yourQWidget);
Depending of the QLayout you are using, parameters of the add function will not be the same.
create a widget and add it to your main window as it is your central widget
mainWindow->setCentralWidget(centralWidget);
if you want to add a label, you can add it to this central widget

Resources