I have a widget containing two QTableViews in a horizontal layout. I would like them to resize to their preferred size (so that they don't have to show autoscrolls). I also don't want them to grow more than their preferred size and leave empty space to the right if there is any left. It should look like this:
I've tried to achieve it with a spacer on the right, but the views don't grow because of autoscrolls.
Can you propose any solution?
If I understood your question, you want the two QTableViews to resize to their PreferredSize if there is enough space (but no more than thier PreferredSize) and to shrink if there is not enough space. If there is too much space, it should be left empty. Here's an example that does it:
#include <QtGui>
int main(int argc, char **argv) {
QApplication app(argc, argv);
QMainWindow w;
QHBoxLayout *hbox = new QHBoxLayout;
QWidget *centralw = new QWidget;
centralw->setLayout(hbox);
w.setCentralWidget(centralw);
QTableView *t1 = new QTableView;
QTableView *t2 = new QTableView;
// Version one: the preferred size is the maximum size
// t1->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
// t2->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
// Version two: the preferred size is the only accepted size
// if you want the widgets not to change their size, change the two previous lines with
// t1->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
// t2->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
// hbox->addWidget(t1);
// hbox->addWidget(t2);
// add a stretch that will fill empty space
// hbox->addStretch(1);
// Version three: tables have a minimum and maximum width. They can be shrunk
// but they try to expand to take the maximum available space up to their maximum
// width.
t1->setMinimumWidth(150);
t1->setMaximumWidth(400);
t2->setMinimumWidth(100);
t2->setMaximumWidth(200);
t1->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
t2->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
hbox->addWidget(t1);
hbox->addWidget(t2);
hbox->setStretchFactor(t1, 1);
hbox->setStretchFactor(t2, 1); // tableviews have the same stretch factor
hbox->addStretch(0); // lowest stretch factor
w.show();
return app.exec();
}
You can reimplement parent's widget resizeEvent function and not to use a layout at all. So when the parent widget is resized you can manually set the sizes of your tables.
The other way you can try is to reimplement sizeHint or/and minimumSizeHint functions of the QTableView so they return a good for you size.
Also take a look at QSizePolicy - it may be usefull
Related
when i add a QLabel and QCheckBoxs to either a QVBoxLayout or a QHBoxLayout i would expect them to be evenly distributed but the checkboxes will allign tight at the very bottom (in the above example) and the label will be centered on the resulting free space in the widget. How can i change this behaviour to distribute all 3 widgets evenly?
Many thanks.
This is the example code:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
QLabel* l = new QLabel("Hi");
QCheckBox* c = new QCheckBox("Label");
QCheckBox* c2 = new QCheckBox("Label");
l->setText("Hi");
QVBoxLayout* v = new QVBoxLayout;
v->addWidget(l);
v->addWidget(c);
v->addWidget(c2);
setLayout(v);
ui->setupUi(this);
}
And this is the result:
Take a look at QSizePolicy. You need to setSizePolicy for your QLabel and QCheckBoxes to be QSizePolicy::Preferred, from the docs:
The default policy is Preferred/Preferred, which means that the widget
can be freely resized, but prefers to be the size sizeHint() returns.
Button-like widgets set the size policy to specify that they may
stretch horizontally, but are fixed vertically. The same applies to
lineedit controls (such as QLineEdit, QSpinBox or an editable
QComboBox) and other horizontally orientated widgets (such as
QProgressBar).
Currently, your QLabel has preferred height, while both of the QCheckBoxes has fixed height. That means that the QLabel will be expanded automatically to take up any additional vertical space (that can't be taken by the QCheckBoxes.
So, In order to set all your widgets to Preferred height, you need to add the following to your constructor:
l->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
c->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
c2->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
Another option, would be to add spacers around each one of the widgets, like this:
v->addStretch();
v->addWidget(l);
v->addStretch();
v->addWidget(c);
v->addStretch();
v->addWidget(c2);
v->addStretch();
setLayout(v);
This way QSpacerItems will take up all the additional space.
How do the options size policy and stretch factors influence the size of a widget?
The image below shows previews of three differently arranged windows. For all three windows (W1-W3) the widget on the right is a QFrame widget with the horizontal and vertical size policies set to Preferred (this is the default setting). The horizontal stretch factor is set to 2. The widget on the left is a QListView widget which also has the size policies set to Preferred (by default this would be Expanding) and the horizontal stretch factor set to 1.
The three windows differ in the way the two widget are layout against each other.
(W1) The window on the left in the image above has a central widget set to a horizontal layout resulting in a size ratio of 2/3 of the widgets that I would expect because of the stretch factors set to 2 and 1.
(W2) The two widget are "connected" with each other through a QSplitter widget. The central widget is set to t horizontal layout. The results of the size of the widgets differ from W1 and are not in the ratio of 2/3.
(W3) The right window "connects" the two widget also with a QSplitter like in window W2. Howevert, The QListView widget is child to a QVBoxLayout. So the QSplitter has the QFrame and a QVBoxLayout as its children.
In detail the setup for the three different windows is shown in the image below:
I have the following questions:
Why do the ratios of the two widgets differ between W1, W2 and W3?
The ratio in W2 seems to be affected by the stretch factors, however not with the results expected. But W3 does not seem to be influenced by the stretch factors at all. Why is that the case?
The behavior of the splitter with relation to stretch factors is documented to be different than the behavior of a layout: you shouldn't expect them to look the same.
The W2 & W3 should look identical if implemented as you claim. Your ui file has a mistake in it.
Here's a test case that doesn't use a .ui file:
// https://github.com/KubaO/stackoverflown/tree/master/questions/layout-stretch-triad-37680657
#include <QtWidgets>
struct Window : public QMainWindow {
QWidget central;
QHBoxLayout layout{¢ral};
QListView view;
QFrame frame;
Window(const QString & title) {
setWindowTitle(title);
setCentralWidget(¢ral);
view.resize(300, 300);
view.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
frame.resize(300, 300);
frame.setLineWidth(3);
frame.setFrameStyle(QFrame::Box);
resize(500, 200);
show();
}
};
struct Window1 : Window {
Window1() : Window("W1") {
layout.addWidget(&view, 1);
layout.addWidget(&frame, 2);
}
};
struct Window2 : Window {
QSplitter splitter;
Window2() : Window("W2") {
layout.addWidget(&splitter);
splitter.addWidget(&view);
splitter.addWidget(&frame);
splitter.setStretchFactor(0, 1);
splitter.setStretchFactor(1, 2);
}
~Window2() { frame.setParent(0); view.setParent(0); }
};
struct Window3 : Window {
QSplitter splitter;
QWidget leftWidget;
QVBoxLayout leftLayout{&leftWidget};
Window3() : Window("W3") {
layout.addWidget(&splitter);
splitter.addWidget(&leftWidget);
splitter.addWidget(&frame);
splitter.setStretchFactor(0, 1);
splitter.setStretchFactor(1, 2);
leftLayout.setMargin(0);
leftLayout.addWidget(&view);
}
~Window3() { frame.setParent(0); view.setParent(0); }
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Window1 w1;
Window2 w2;
Window3 w3;
w2.move(w1.pos() + QPoint(0, 75));
w3.move(w2.pos() + QPoint(0, 75));
return app.exec();
}
The following code (Qt 5, same behaviour with Qt 4.8) uses a QGridLayout to add widgets above and to the left of a QScrollArea (to serve as a kind of header later):
#include <QApplication>
#include <QMainWindow>
#include <QGridLayout>
#include <QScrollArea>
class ColoredWidget : public QWidget {
public:
ColoredWidget(const QColor& color, QWidget* parent) : QWidget(parent) {
QPalette pal;
QBrush brush(color);
brush.setStyle(Qt::SolidPattern);
pal.setBrush(QPalette::Active, QPalette::Window, brush);
setPalette(pal);
setAutoFillBackground(true);
}
};
class MainWindow : public QMainWindow {
public:
MainWindow(QWidget* parent) : QMainWindow(parent) {
resize(300, 400);
QWidget* centralwidget = new ColoredWidget(QColor(0xff, 0xf0, 0xb5), this);
QGridLayout* layout = new QGridLayout();
centralwidget->setLayout(layout);
setCentralWidget(centralwidget);
// create widget with fixed height of 20 px and maximum width of 200
QWidget* topHeader = new ColoredWidget(Qt::green, centralwidget);
topHeader->setMaximumWidth(200);
topHeader->setFixedHeight(20);
// create widget with fixed width of 20 px and maximum height of 200
QWidget* leftHeader = new ColoredWidget(Qt::blue, centralwidget);
leftHeader->setFixedWidth(20);
leftHeader->setMaximumHeight(200);
// create scroll area as main widget
QWidget* view = new QScrollArea();
layout->addWidget(topHeader, 0, 1);
layout->addWidget(leftHeader, 1, 0);
// layout->addWidget(leftHeader, 1, 0, Qt::AlignTop); // widget not displayed at all!
layout->addWidget(view, 1, 1);
}
};
int main(int argc, char ** argv) {
QApplication app( argc, argv );
MainWindow win(0);
win.show();
return app.exec();
}
The QGridLayout documentation says that "Columns and rows behave identically", thus I would expect that the two widgets are layouted the same way.
However, the left one is automatically vertically centered, while the top widget is aligned to the left within the cell (and not centered horizontally). I would like to have the left widget also at a fixed position, means aligned to the top:
Which property causes this different behaviour between the two widgets, one beeing centered, the other being at a fixed position?
How can I influence the alignment (I can probably add a flexible spacer to fix it, but ideally I would like to avoid that)? I tried with Qt::AlignTop, but that made the blue widget disappear.
Althought this question is pretty old, I will add my answer for all those that come here afterwards, like me.
Setting the alignment either with
layout->setAlignment(leftHeader, Qt::AlignHCenter | Qt::AlignTop);
or with
layout->addWidget(leftHeader, 1, 0, Qt::AlignTop);
is intuitiv and seems to be the right way. If it is setup completly it is also working as expected.
But why does the Widget vanish then?
Long story short: because leftHeader has a size of 0.
In my pretty similar situation I've setfixedHeight() and the widget reappeared, this should also work by setting a value for minimalHeight() for example.
Long Story - Details
I've put myself in position of gridLayout and tried to determine the size of leftHeader:
Outside of the layout leftHeader got a fixed width of 20 - ok, but no given height. With the defaults of QWidget this leads to a default-height of 0.
Documentation of void QGridLayout::addWidget() and void QGridLayout::addLayout() states:
... The alignment is specified by alignment. The default alignment is
0, which means that the widget fills the entire cell. ...
source
Hence for default alignment of 0 the height of leftHeader is obviously set as height of its row.
But in case a different Qt::Alignment is used, as in the question (Qt::AlignTop ), it is not that simple.
From my point of view the layout is unable to determine the correct height without further settings from the designer.
Having a quick look at the implementation of void QGridLayout::addWidget(), we can detect that a QLayoutItem(as part of QGridBox) is created inside the QGridLayout for each added widget resp. Layout.
So it seems that the QGridLayout relies on default layout mechanisms of Qt based on the settings from the added widget/Layout (like minSize, maxSize, BaseSize, Size Increment, etc ).
In combination with 1.) this implies a height of 0 for leftHeader and therefore nothing to draw resp. it seems that the widget has vanished.
Although not a minimal solution you could probably fix the alignments by adding a QHBoxLayout in the cell for the green widget and a QVBoxLayout in the cell for the blue widget. Add the green widget to the QHBoxLayout and the blue widget to the QVBoxLayout. Then apply addStretch(n) to both the QHBoxLayout and QVBoxLayout. Maybe this is what you already mentioned you could do and then we are at the same level of knowledge concerning this. I believe this is the way I have solved these kind of issues and I haven't spent more time on it.
Try this:
setAlignment(Qt::AlignHCenter | Qt::AlignTop );
Ok, so basically I have a simple table with a QWidget and two buttons as shown below:
QGridLayout *layout = new QGridLayout;
layout->addWidget(viewcontainer,0,0,1,2);
layout->addWidget(reset,1,0);
layout->addWidget(done,1,1);
This is basically what I want, where "reset" and "done" are buttons. Essentially it's a QWidget, viewcontainer, which resizes as the window size is changed by the user while the buttons' heights remains the same. But, the default for the gridlayout is to align the contents to the left. If I change this with:
layout->addWidget(viewcontainer,0,0,1,2, Qt::AlignCenter);
It does sort of what I want, but the graphicsscene no longer resizes (remains a small constant size). I'd like to retain the resizing while just aligning the widget to the center. Thanks.
I think the easiest solution which provides a clean solution is to nest 2 layouts.
Your 'outer' (parent) layout should be a QHBoxLayout and you can add your QGridLayout into it as an 'inner' (child) layout with addLayout().
Based on my experience you should avoid to set Qt::Alignment every time you can. It can really mess up your layout. For simple layouts it can work but for more complex ones you should avoid it. And you never know that you should extend your layout in the future or not so my suggestion is to use nested layouts.
Of course you can create a QWidget for the 'outer' layout and for the 'innser' layout as well but most of the times it should be fine to just nest 2 layouts.
Also you can use QSpacerItem to fine-tune your layout.
Have a look at this example code, I think it does what you want:
#include <QApplication>
#include <QPushButton>
#include <QGraphicsView>
#include <QGridLayout>
#include <QPalette>
class MyWidget : public QWidget
{
public:
MyWidget()
{
QGridLayout * layout = new QGridLayout(this);
QGraphicsView * gv = new QGraphicsView;
layout->addWidget(gv, 0,0, 1,2);
layout->setRowStretch(0, 1); // make the top row get more space than the second row
layout->addWidget(new QPushButton("reset"), 1,0);
layout->addWidget(new QPushButton("done"), 1,1);
}
};
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MyWidget w;
w.show();
return app.exec();
}
In Qt Designer , you can drag a "Line" widget , which will create a line in your layout.
But I checked the document and headers , I didn't find the "Line" header / widget , what was it ?
In Qt 5.7 the code generated by Qt Designer for a Horizontal Line (which can be checked in the menu using "Form/View Code...") is:
QFrame *line;
line = new QFrame(Form);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
This will create the lines you see in Qt Designer.
The current answers do not seem to give working solutions, here is a comparison of all answers (this solution is the first line):
Full code:
#include <QtWidgets>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QWidget widget;
auto layout = new QVBoxLayout;
widget.setLayout(layout);
widget.resize(200, 200);
auto lineA = new QFrame;
lineA->setFrameShape(QFrame::HLine);
lineA->setFrameShadow(QFrame::Sunken);
layout->addWidget(lineA);
QWidget *lineB = new QWidget;
lineB->setFixedHeight(2);
lineB->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
lineB->setStyleSheet(QString("background-color: #c0c0c0;"));
layout->addWidget(lineB);
auto lineC = new QFrame;
lineC->setFixedHeight(3);
lineC->setFrameShadow(QFrame::Sunken);
lineC->setLineWidth(1);
layout->addWidget(lineC);
QFrame* lineD = new QFrame;
lineD->setFrameShape(QFrame::HLine);
layout->addWidget(lineD);
widget.show();
return app.exec();
}
I guess you mean a horizontal / vertical line widget: it's just a simple QWidget with a gray background color and the horizontal is a fix height (1-3 pixel) and expanding width widget, the vertical is a fix width expanding height widget.
Horizontal example code:
QWidget *horizontalLineWidget = new QWidget;
horizontalLineWidget->setFixedHeight(2);
horizontalLineWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
horizontalLineWidget->setStyleSheet(QString("background-color: #c0c0c0;"));
Check out QFrame::setFrameShape(). To get a line, use either QFrame::HLine or QFrame::VLine as the function's argument.
// Create a horizontal line by creating a frame and setting its shape to QFrame::HLine:
QFrame* hFrame = new QFrame;
hFrame->setFrameShape(QFrame::HLine);
// Create a vertical line by creating a frame and setting its shape to QFrame::VLine:
QFrame* vFrame = new QFrame;
vFrame->setFrameShape(QFrame::VLine);
It is a QFrame with height 3, sunken shadow and line width equal to 1.
You can see it if examine header generated by uic tool.