QScrollArea messing up with QGridLayout : QGridLayout hidden and no scroll - c++

I am trying to make a QGridLayout scrollable. It may contain several custom widgets, the number of widgets is not fixed. QGridLayout must be scrollable when there are more than x widgets, x being an arbitrary number.
The problem is, when I use QScrollArea, QScrollArea seems to hide the whole layout (only the background color of scroll area is shown). When I use QGridLayout alone, my view is - of course - not scrollable but everything works as it should do.
I am probably missing something, my guesses are:
I must fix somehow the size of the scrollable area, but I am not sure it's necessary or if I already have done it unconsciously
Parent Widgets or Child Widgets of the existing QScrollArea prevent QScrollArea by an unusual implementation to act normally.
Here's a piece of the involved code :
QScrollArea *scrollArea = new QScrollArea;
QWidget *resultsPage = new QWidget;
booksGrid = new QGridLayout;
booksGrid->setSizeConstraint(QLayout::SetMinAndMaxSize);
resultsPage->setLayout(booksGrid);
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setWidget(resultsPage);
mainWidget->addWidget(scrollArea);
Also, booksGrid is declared as a class attribute, mainWidget is a QStackedWidget.
Any help is welcomed, let me know if you need more information !

As I couldn't see anything suspicious in your code fragment, I made an MCVE to reproduce your issue:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
// setup GUI
QMainWindow qWin;
QScrollArea qScrArea;
QWidget qScrView;
QGridLayout qGrid;
enum { nCols = 4 };
#define MAKE_LABEL(I) \
QLabel qLbl##I(QString::fromUtf8("Label "#I)); \
qGrid.addWidget(&qLbl##I, I / nCols, I % nCols)
MAKE_LABEL(0); MAKE_LABEL(1); MAKE_LABEL(2); MAKE_LABEL(3); MAKE_LABEL(4);
MAKE_LABEL(5); MAKE_LABEL(6); MAKE_LABEL(7); MAKE_LABEL(8); MAKE_LABEL(9);
MAKE_LABEL(10); MAKE_LABEL(11); MAKE_LABEL(12); MAKE_LABEL(13); MAKE_LABEL(14);
MAKE_LABEL(15); MAKE_LABEL(16); MAKE_LABEL(17); MAKE_LABEL(18); MAKE_LABEL(19);
MAKE_LABEL(20); MAKE_LABEL(21); MAKE_LABEL(22); MAKE_LABEL(23); MAKE_LABEL(24);
MAKE_LABEL(25); MAKE_LABEL(26); MAKE_LABEL(27); MAKE_LABEL(28); MAKE_LABEL(29);
MAKE_LABEL(30); MAKE_LABEL(31); MAKE_LABEL(32); MAKE_LABEL(33); MAKE_LABEL(34);
MAKE_LABEL(35); MAKE_LABEL(36); MAKE_LABEL(37); MAKE_LABEL(38); MAKE_LABEL(39);
#undef MAKE_LABEL
qScrView.setLayout(&qGrid);
qScrArea.setWidget(&qScrView);
qWin.setCentralWidget(&qScrArea);
qWin.show();
// run-time loop
return app.exec();
}
Compiled and tested in VS2013, Qt 5.9.2 on Windows 10 (64 bit):
a)
b)
c)
The snapshots are taken after start (a), after resizing (b), and after scrolling (c).
For me, everything looks and works like expected.
You may compile and test the sample on your side as well. If it shows the same broken behavior like your application then something is wrong in your Qt version (otherwise something in your application).

The solution - resultsPage was a personalized widget containing several other widgets, with unspecified sizes, arranged in a QGridLayout.
Widgets were encapsulated like this :
QMainWidget -> QScrollArea -> personalized QWidget resultsPage -> QGridLayout -> personalized QWidgets result(s) with unspecified size
In the end the only thing I had to do was to set a fixed size in the constructor of the QWidget result with
setFixedSize(int w, int h);

Related

Why aren't my QBoxLayouts working?

So, I took it down a few notches and am trying simple programs to get the hang of Qt's tools.
First I tried a simple label inside main() function, then a button inside it. All good.
Next, I tried the same, but inside a main window (using the Qt's created documents). After the one-button program worked, I did a two-button program, that simple. Worked.
Then, I tried Qt's Box Layouts.
In none of these "main window" tries, I changed the main.cpp file created by Qt.
Here's the mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QPushButton *button1;
QPushButton *button2;
};
#endif // MAINWINDOW_H
Next, the mainwindow.cpp file:
#include "mainwindow.h"
#include <QPushButton>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setWindowTitle(tr("Two Button Example"));
resize(400,300);
button1 = new QPushButton(tr("Bye!"));
button1->setGeometry(0,0,200,30);
//button1->setParent(this);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
button1->show();
button2 = new QPushButton(tr("Hide B1!"));
button2->setGeometry(0,0,200,30);
//button2->setParent(this);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
button2->show();
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
setLayout(layout);
}
MainWindow::~MainWindow()
{
}
I'm studying this book (for the layouts specifically, Chapter 2, the Find Dialog example (page 16 of the book, or 34 from the pdf file): C++ GUI Programming with Qt 4 1st ed.pdf
For this specific problem, I also used this: QHBoxLayout - Qt Examples and Tutorials
What I noticed:
Commenting the QHBoxLayout part, all the way to the "setLayout" function, makes no difference in the program. No test I've done is affected by this QBoxLayout thing;
The "setGeometry" function sets the position (first two parameters) and size (width and heightm, last two parameters) of a widget. This position is related to the parent widget, which may be the screen itself when no parent is assigned;
When the "setParent" functions for the buttons are commented and the "show" function is uncommented, the buttons are shown each in a separate window from the MainWindow. When I uncomment the "setParent" functions, the buttons are shown inside the MainWindow, and there's no difference if there are or no "show" functions for the buttons. The detail is, for the book I referenced, no example so far have had the need to declare the parent widgets, nor did the example in Qt's Examples and Tutorials site;
If I don't use the "setGeometry" function, the buttons are big, like 600x600, whatever. They follow the rules above regarding the other functions, but they are huge and always one on top of the other, not side by side. The function "sizeHint()" used extensively in the book, also has no effect;
Appearently, it's all following the same syntaxes and rules of the examples. So, what am I doing wrong, or not doing here? If it's not a problem to enlight me with the "sizeHint()" function, too, that would be great.
Thanks in advance!
I don't even remember the origin of the problem, but I have never been able to work with the layouts right in the window. In company I worked with Qt we used a central widget for managing layouts, so the diagram is: window -> central widget -> layouts -> subwidgets. If I modified your code so, it would look like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setWindowTitle(tr("Two Button Example"));
resize(400,300);
auto *parentWidget = new QWidget;
auto button1 = new QPushButton(tr("Bye!"), parentWidget);
button1->setGeometry(0,0,200,30);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
auto button2 = new QPushButton(tr("Hide B1!"), parentWidget);
button2->setGeometry(0,0,200,30);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
parentWidget->setLayout(layout);
setCentralWidget(parentWidget);
}
Also, I don't quite understand why do you try to set the button size if the layout resets it later. To change the size inside a layout you must change the sizeHint of a widget:
setWindowTitle(tr("Two Button Example"));
resize(400,300);
auto *parentWidget = new QWidget;
auto button1 = new QPushButton(tr("Bye!"), parentWidget);
button1->setMaximumSize(20, 20);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
auto button2 = new QPushButton(tr("Hide B1!"), parentWidget);
button2->setMaximumSize(20, 20);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
parentWidget->setLayout(layout);
setCentralWidget(parentWidget);
After some observation, I believe I know why it wasn't working.
In the examples provided, the person was using QDialog or applying the layouts straight to the main() code, while I was using the QMainWindow class.
I suppose that when you use a QMainWindow class, you always have to set a Central Widget in order to make layouts work (setting this central widget layout).
So a solution would be:
QWidget centerWidget = new QWidget();
setCentralWidget(centerWidget);
//Add buttons, lists, and whatever
/*Add a QHBoxLayout or a QVBoxLayout and use
layout->addWidget() to add the widgets created,
like the examples. Then:*/
centerWidget->setLayout(layout);
It works, like Victor Polevoy suggested. Just answering as I believe I understood why weren't my QLayouts working.

Central Widget occupies to much space and Dock Widgets to few after resize

I'm right now using QDockWidget to create a dynamic component for arranging some user defined plots. The plots should be changed in their sizes and can be arranged on top of each other.
The following code snippet illustrates what I'm trying to achieve:
#include <QApplication>
#include <QMainWindow>
#include <QLabel>
#include <QDockWidget>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
auto window = new QMainWindow;
window->setCentralWidget(new QLabel("Central Widget"));
for (int i = 1; i < 9; i++) {
auto dock = new QDockWidget(QString("Plot %1").arg(i));
dock->setWidget(new QLabel(QString("Plot %1").arg(i)));
dock->setAllowedAreas(Qt::AllDockWidgetAreas);
window->addDockWidget(Qt::BottomDockWidgetArea, dock);
}
window->show();
return app.exec();
}
The central widget merely serves as a simple placeholder and is just necessary to allow for dragging and rearranging the QDockWidget.
I'm already very satisfied with the achieved behavior besides a single drawback. After resizing window (making it larger), the central widget consumes all of the newly gained space, whereas the DockWidgets still occupy the same space like before.
The behavior is depicted below:
This is kind of annoying for user, as the central widget is just a placeholder. Actually, I just wanted to have the behavior the other way around, i.e. the central widget should keep its size, whereas the DockWidgets should be enlarged.
How can I achieve this?
Just resize your central widget to the desired size. Or even better, hide it (it seems that you only use QDockWidgets besides this one).
QLabel* label = new QLabel("Central Widget");
label->hide();
window->setCentralWidget(label);
window->setDockNestingEnabled(true);
centralWidget()->hide(); // enable full dock space
works for me w/ qt 5.8

Qt C++ GUI QSpinBox Storing Input?

How would I take the user input from the a spinbox and use that as a value? In other words if I wanted to store the input from the QSpinBox into a variable how would I go about doing this. Im really new at Qt GUI so any input would be greatly appreciated.
To react to GUI elements in Qt, you connect to the signals that those elements give off. Also if you have a pointer to the instance of it, you can query and change its states and properties.
Here is a quick example of what you are looking for
#include <QApplication>
#include <QVBoxLayout>
#include <QLabel>
#include <QSpinBox>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// The widget, contains a layout
QWidget * w;
w = new QWidget;
// The layout arranges and holds
// all the children of the widget
QVBoxLayout * vbox;
vbox = new QVBoxLayout;
// The user input element, the spinbox!
QSpinBox * spinbox;
spinbox = new QSpinBox();
spinbox->setValue(5);// example of using a pointer to edit its states
// now add it to the layout
vbox->addWidget(spinbox);
// add in an element to connect to,
// the infamous QLabel
QLabel * label;
label = new QLabel("spinbox output");
// add it also to the layout
vbox->addWidget(label);
// connection can happen anytime as long as the two elements
// are not null!
// This connection takes advantage of signals and slots
// and making their connection at runtime.
// if a connect call fails you should be able to see why in the
// application output.
QObject::connect(spinbox, SIGNAL(valueChanged(QString)),
label, SLOT(setText(QString)));
// associate the layout to the widget
w->setLayout(vbox);
// make the widget appear!
w->show();
return a.exec();
}
I usually put most of the initializing and connecting of GUI elements into the constructor or a method of the main QWidget or the QMainWindow. I often take the signal from a GUI input element, like a spinbox and I connect it to a custom slot defined on my subclassed QWidget. Then if I want to display it with the input value differently or increase the output by 2, I can do so easily.
// in the constructor of my Widget class
// after spinbox has been initialized
QObject(m_spinbox, SIGNAL(valueChanged(int)),
this, SLOT(on_spinboxValueChanged(int)));
void Widget::on_spinboxValueChanged(int i)
{
// here m_label is a member variable of the Widget class
m_label->setText(QString::number(i + 2));
// if accessing the value in this function is inconvenient, you can always
// use a member variable pointer to it to get its stored value.
// for example:
int j = m_spinbox->value();
qDebug() << "Spinbox value" << j;
}
Identical things can be done in QML and Qt Quick, and for many people it is easier and more intuitive because of how close it is to javascript and css.
Also Qt Creator has a tool for generating forms, and it provides another way to create your widgets with layouts and then when you access your elements you do it through a ui variable.
Also the Qt docs and examples are awesome. Taking time to learn them and read up on them is well worth the effort.
Hope that helps.

QGraphicsView size in a GridLayout

I have found this: Getting the size of a QGraphicsView
But I can't figure out what does it mean to "move my initialization code to showEvent" and I can't comment on that answer.
I am want to resize a QPixmap so it could fit my QGraphicsView. I've placed my graphicsview in Designer and set GridLayout for my main window. In a MainWindow constructor I have written the following code:
ui->setupUi(this);
// Get GView size
g_sizeX = ui->mapView->width();
g_sizeY = ui->mapView->height();
// Init scene
scene = new QGraphicsScene(this);
// Init MAP pixmap and add it to scene
mapImage = new QPixmap(":/Map/europe.jpg");
QPixmap newmapImage = mapImage->scaled(g_sizeX, g_sizeY);
scene->addPixmap(newmapImage);
// Display scene in gview.
ui->mapView->setScene(scene);
But I always get size of 100x30. If I break the gridLayout, I get the correct size.
So, how should I deal with this?
Thank you.
The QGraphicsView will be resized by the QGridLayout after the widget is shown, and can be also resized later when the window is itself resized.
So you should change the size of the pixmap as a result of a QResizeEvent, either by subclassing QGraphicsView to redefine resizeEvent(), and then promoting your view object to your new class in the designer to use it instead of QGraphicsView, or by installing your MainWindow object as an event filter for the view to handle to the resize event from the MainWindow::eventFilter function.
You probably don't want to change the pixmap size in the scene, but rather adjust the view matrix so that your QGraphicsPixmapItem fits perfectly inside the view, with QGraphicsView::fitInView.
For example:
/* QGraphicsPixmapItem *pixmapItem; as a MainWindow member */
pixmapItem = scene->addPixmap(newmapImage);
/* Either always disable or enable the scrollbars (see fitInView doc) */
ui->mapView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->mapView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->mapView->installEventFilter(this);
...
bool MainWindow::eventFilter(QObject *obj, QEvent *evt) {
if(obj == ui->mapView && evt->type() == QEvent::Resize) {
ui->mapView->fitInView(pixmapItem, Qt::KeepAspectRatioByExpanding);
}
// Call the base class implementation
return QMainWindow::eventFilter(obj, evt);
}
I believe that what is happening is that Qt only applies layouts and sets widget sizes when the widget is first displayed.
One way to work that is to override QWidget::showEvent(), and put your sizing code in there.
However, one simpler way, that often works in constructors, is to ask the widget for its sizeHint(), rather than for its not-yet-layed-out size.
In your case, that would mean changing two lines of code to:
g_sizeX = ui->mapView->sizeHint().width();
g_sizeY = ui->mapView->sizeHint().height();
If your layout isn't too complicated, and if you haven't overridden the default size policies, this may well fix things for you.

Qt : how to implement QDialog StatusBar

I have QDialog that is heavily designed with QDesigner , I saw on the web that I could add QStatusBar with code like this :
#include <QDialog>
#include <QStatusBar>
#include <QLayout>
#include <QApplication>
#include <QTextEdit>
#include <QStatusTipEvent>
class Dialog : public QDialog {
public:
Dialog() : QDialog(){
QLayout *l = new QVBoxLayout(this);
QTextEdit *te = new QTextEdit;
te->setStatusTip("XXX");
l->addWidget(te);
bar = new QStatusBar;
l->addWidget(bar);
l->setMargin(0);
l->setSpacing(0);
}
private:
QStatusBar *bar;
protected:
bool event(QEvent *e){
if(e->type()==QEvent::StatusTip){
QStatusTipEvent *ev = (QStatusTipEvent*)e;
bar->showMessage(ev->tip());
return true;
}
return QDialog::event(e);
}
};
int main(int argc, char **argv){
QApplication app(argc, argv);
Dialog dlg;
return dlg.exec();
}
Its not even working in my case .. maybe the QDialog is already have few layets that holds widget.
My question is can I some how use palceholder in the QDesigner or somehow promote widget that place hold the QStatusbar class? I don’t know …
What can I do in such case? can I implement new QStatusbar?
Thanks
I presume when you say it doesn't work, that you are not seeing the status bar when you run.
I don't see any way to do this wholly in the designer. The designer certainly resists the idea of promoting something to a QStatusBar. I suppose you could fool the designer by subclassing QStatusBar, and then promoting a QWidget to your subclass.
But I don't think we need to go that route just yet. I think with a few tweaks to the code you have above should help.
In the designer, add a layout, it doesn't matter what kind, at the bottom of your dialog. I called mine 'StatusBarLayout'. You can see the layout (the red box that's squished at the bottom). I removed the bottom margin in the dialog so that the status bar is flush at the bottom.
Now remove everything in the above code about layout l, and just do this:
bar = new QStatusBar(this);
pUI->StatusBarLayout->addWidget(bar);
pUI->textEdit->setStatusTip("XXX");
The textEdit was something added in the designer. Now when you run it you should see this:
I hope that helps
Edit:
You can also set the Status Tips for various widgets in the designer too, so there is no need to do that in the code unless you want to.
Try adding a QStatusBar like this:
QDialog dialog;
QLayout* layoutWidget = new QVBoxLayout(&dialog);
layoutWidget ->addWidget(new QTextEdit);
QStatusBar* statusBar = new QStatusBar;
layoutWidget ->addWidget(statusBar );