How to properly use QLayouts (hbox, vbox, etc)? - c++

I am building a custom widget based on QGraphicsWidget
Inside the widget, I need various other widgets in line with the size of the window. For this I am using
Layout = new QGraphicsLinearLayout();
Layout->setOrientation(Qt::Horizontal);
setLayout(Layout);
So I have a layout, and on each side of the layout I have a widget. So a widget on the left side, and a widget on the right side.
QGraphicsLinearLayout() below
500 px wide x
|--------------------------------|
| |
|left widget right widget| 100 px tall y
|--------------------------------|
I want the behaviour that when the window gets bigger, the widgets stay within x pixels of the edge. So if the window were to be resized to 10,000 pixels wide, left widget would always be 1 pixel from the left edge and right widget would always be 1 pixel from the right edge.
10000 px wide x
|---------------------------------------------------|
| |
|left widget right widget| 100 px tall y
|---------------------------------------------------|
The current behaviour is that left widget will stay in place and never move, and right widget will move away from the right edge.
To achieve this behaviour, I have tried the following:
Layout->setAlignment(leftWidget, Qt::AlignLeft);
That does absolutely nothing.
I also tried
Layout->addItem(leftWidget);
Layout->setStretchFactor(leftWidget, 0);
Layout->addItem(rightWidget);
Which gives the desired effect but overlaps the rightWidget with the border like so
500 px wide x
|--------------------------------|
| |
|left widget right widget 100 px tall y
|--------------------------------|
So how can I get my desired behaviour? QLayouts seem very confusing and the API's misleading thus far.

Solution
Use QGraphicsLinearLayout::addStretch between the left and right widgets in order to keep them to the sides and QGraphicsLayout::setContentsMargins to add a space between the widgets and the edges:
auto *l = new QGraphicsLinearLayout();
l->addItem(leftProxy);
l->addStretch();
l->addItem(rightProxy);
l->setContentsMargins(25, 1, 1, 1);
l->setSpacing(1);
Example
I have prepared a minimal example for you of how to use the proposed solution:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
auto *view = new QGraphicsView(this);
auto *widget = new QWidget();
auto *leftWidget = new QPushButton(tr("Left"));
auto *rightWidget = new QPushButton(tr("Right"));
auto *leftProxy = new QGraphicsProxyWidget();
auto *rightProxy = new QGraphicsProxyWidget();
auto *l = new QGraphicsLinearLayout();
auto *sizeGrip = new QSizeGrip(widget);
leftWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
rightWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
widget->setMinimumSize(sizeGrip->sizeHint().width()
+ leftWidget->sizeHint().width()
+ rightWidget->sizeHint().width(),
leftWidget->sizeHint().height());
leftProxy->setWidget(leftWidget);
rightProxy->setWidget(rightWidget);
l->addItem(leftProxy);
l->addStretch();
l->addItem(rightProxy);
l->setContentsMargins(25, 1, 1, 1);
l->setSpacing(1);
view->setScene(new QGraphicsScene(this));
view->scene()->addWidget(widget)->setLayout(l);
widget->resize(400, 200);
setCentralWidget(view);
resize(640, 480);
}

Related

How to limit a sizer within a certain space? (wxWidgets)

I'm using a sizer to place the buttons in a grid:
However, I don't want the sizer to use all the space in the window, but just a small area, like the following (in the white area):
Is this possible to achieve? And if yes, how?
Please know that I'm VERY new to wxWidgets.
By the way, here's the code behind the frame:
Main::Main() : wxFrame(nullptr, wxID_ANY, "IKE Calculator", wxDefaultPosition, wxSize(500, 650), wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE_BOX & ~wxRESIZE_BORDER)
{
int w = 3, h = 3;
NumBtn = new wxButton*[w*h];
NumSizer = new wxGridSizer(w, h, 0, 0);
//Populate sizer
int cycle = 1;
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
int current = (y * w + x);
NumBtn[current] = new wxButton(this, BUTTON_NUM + current, std::to_string(cycle));
NumSizer->Add(NumBtn[current], 1, wxEXPAND | wxALL);
cycle++;
}
}
this->SetSizer(NumSizer);
NumSizer->Layout();
}
Main::~Main()
{
delete[] NumBtn;
}
Sizers are used to position children of a window and they use the entire window client area (i.e. everything inside it) for this. This doesn't mean that you can't position your buttons in the way you want to do it, of course, it just means that you have to do it in the right way:
Either you can create a child window which takes just the area you want it to occupy and then you create your buttons as its children and associate the sizer with this window. This, of course, assumes you can position this window correctly, but windows have SetSize() method, so in principle this could be done. But setting sizers manually is not the right thing to do and you risk running into a common issue with positioning the only child of a window, so it's better to do it in another way.
This other way is to use "spacers", which are sizer elements that take "space", without doing anything else, to occupy the unused parts of the window. For example, in this case you could create a vertical box sizer (wxBoxSizer(wxVERTICAL)) containing a spacer of the given height (the top area) and a horizontal box sizer containing a spacer corresponding to the left gap and your current sizer.
Note that a layout such as what you want to create is rather unusual, typically you don't need to just leave arbitrarily-sized parts of the window empty. So you wouldn't do something like this often, but if you really want to, you can do it, of course.

Qt C++ - QListWidget layout doesn't scale to fit the content

I have a QWidget rzadKontener that represents a row in a QListWidget.
QWidget* rzadKontener = new QWidget;
QHBoxLayout* rzadKontenerLayout = new QHBoxLayout();
rzadKontenerLayout->setAlignment(Qt::AlignLeft);
rzadKontenerLayout->setAlignment(Qt::AlignTop);
rzadKontener->setObjectName("rzadKontener_" + poziom);
rzadKontener->setFixedHeight(200);
rzadKontener->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
rzadKontener->setLayout(rzadKontenerLayout);
(....)
QListWidgetItem* newItemRzad = new QListWidgetItem;
newItemRzad->setSizeHint(QSize(listSize*225, 200));
QString poziomText = poziom;
newItemRzad->setText(poziomText);
newItemRzad->setTextColor(QColor(Qt::white));
ui->listWidgetZdjeciaModelu->addItem(newItemRzad);
ui->listWidgetZdjeciaModelu->setItemWidget(newItemRzad, rzadKontener);
It contains a number of items, that are QWidgets with pictures, buttons and text. It is then placed in a QListWidget as a row full of these items. When I add 5 items at once, while creating a new rzadKontener, scroll bars in the QListWidget will appear. But if I add 3 and then 2 items later on, they'll go out of bounds without a scrollbar. How can I force the layout to scale to the new rzadKontener's size?
I found an answer. Instead of modifying the QWidget inside the list, I should be modifying the QListWidgetItem it's inside of (the parent...).
QWidget* newPicture = new QWidget;
yadda yadda yadda (....)
int currentRowWidth = ui->listWidgetZdjeciaModelu->findChild<QWidget*>(objectName)->width(); //gets max width of rzadKontener, which fills the entirety of the row - it equals the QListWidgetItem's width
int newWidth = currentRowWidth + 225; //225 is a fixed width of newPicture
ui->listWidgetZdjeciaModelu->item(0)->setSizeHint(QSize(newWidth, 200));
ui->listWidgetZdjeciaModelu->findChild<QWidget*>(objectName)->layout()->addWidget(newPicture );
We can do ui->listWidgetZdjeciaModelu->item(0)->setSizeHint(QSize(newWidth, 200)); instead of a specific row (->item(row)), because the list is a rectangle. Doesn't matter which row we enlarge, the entire thing will stretch anyway.
But, if you want to get the row number, you can do it this way:
int row=0;
//we make a list. Each of my QListWidgetItem has a unique string poziom in it, so I can filter by that.
QList<QListWidgetItem *> items = ui->listWidgetZdjeciaModelu->findItems(poziom, Qt::MatchContains);
if (items.size() > 0) {
//we use the first (and only, in my case) item on this list to get row number
row = ui->listWidgetZdjeciaModelu->row(items[0]);
}
Weird thing is, I already tried this. Must've kept making a typo.

QScrollBar + QScrollAera in QTabWidget

My question is simple : how can I set a QScrollBar in my QScrollArea. I have tested a lot of things but nothing work ... Maybe it's a problem to set a QScrollArea in a QTabWidget ? Here is the code :
void GamesWindow::createTabSucces()
{
std::string nameImg;
_succesPage = new QWidget(_tab);
_tab->addTab(_succesPage, " Succes ");
scrollArea = new QScrollArea(_succesPage);
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setFixedSize(500,500);
/* Integration of QScrollBar */
for (int i = 0; i < 45; i++)
{
nameImg = "img/allAchiv/";
nameImg += intToString(i + 1);
nameImg += ".jpg";
_imgSucc[i] = new QLabel(scrollArea);
_imgSucc[i]->setPixmap(QPixmap(QString(nameImg.c_str())));
_imgSucc[i]->setGeometry((14 + (85 * (i % 5))), 46 + ((i / 5) * 85), 60, 60);
}
}
In fact, I add pictures in a tab where is created a QScrollArea (like 8-9 lines of pictures) but only 5 are visible, the others are hide, because they are at the bottom, out of the defined zone.
Any idea ? Thank's.
You must:
add a layout to your page (QVBoxLayout)
add the scroll area to that page layout
add a layout to the viewport() widget in the scroll area (QVBoxLayout)
add your QLabels to that viewport layout
This way you won't need to call setGeometry on each label
You need to set a widget to your scroll area and add your pictures to the widget's layout. Check QScrollArea::setWidget(QWidget *widget)
I'm almost sure that you can't add scroll to a tab widget but my idea is just to try make more tabs that can be shown and see if the slider comes up by default.

Qt Scroll Area does not add in scroll bars

Hi guys I have to dynamically create push buttons depending on user inputs, therefore if user gives a large input number the widget containing the push buttons has to have the ability to scroll up and down. For this reason I am using QScrollArea. I generate the template in Qt designer and the UIC generates the code for me after which I add in my part which should handle dynamic creation of push buttons. However, I can not seem to get the vertical scroll bars to appear. Here is the relevant part of the code.
verticalWidget = new QWidget(FWHMWorkflowDialog);
verticalWidget->setObjectName(QString::fromUtf8("verticalWidget"));
verticalWidget->setMinimumSize(QSize(150, 0));
verticalWidget->setMaximumSize(QSize(150, 16777215));
verticalLayout_5 = new QVBoxLayout(verticalWidget);
verticalLayout_5->setObjectName(QString::fromUtf8("verticalLayout_5"));
scrollArea = new QScrollArea(verticalWidget);
scrollArea->setObjectName(QString::fromUtf8("scrollArea"));
scrollArea->setMaximumSize(QSize(150, 16777215));
scrollArea->setWidgetResizable(true);
scrollAreaWidgetContents = new QWidget();
scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents"));
scrollAreaWidgetContents->setGeometry(QRect(0, 0, 130, 432));
numberOfSlices = numberSlices;
for (int i = 0; i < numberOfSlices; i++)
{
QWidget *horizontalWidget = new QWidget(scrollAreaWidgetContents);
horizontalWidget->setMaximumSize(150,40);
horizontalWidget->setGeometry(QRect(0, i*40, 150, 40));
hWidgetList.push_back(horizontalWidget);
QHBoxLayout *hLayout = new QHBoxLayout(horizontalWidget);
hLayoutList.push_back(hLayout);
hLayout->setSizeConstraint(QLayout::SetMinimumSize);
hLayout->setContentsMargins(-1, 1, -1, 1);
QPushButton *pushButton = new QPushButton(horizontalWidget);
pushButtonList.push_back(pushButton);
QString temp = QString("m_sliceButton").arg(i);
pushButtonList[i]->setObjectName(temp);
pushButtonList[i]->setGeometry(QRect(10, 20+i*40, 98, 27));
hLayout->addWidget(pushButton);
QCheckBox *checkBox = new QCheckBox(horizontalWidget);
checkBoxList.push_back(checkBox);
temp = QString("m_checkBox").arg(i);
checkBoxList[i]->setObjectName(temp);
checkBoxList[i]->setEnabled(true);
checkBoxList[i]->setGeometry(QRect(110, 20+i*40, 21, 22));
hLayout->addWidget(checkBox);
}
scrollArea->setWidget(scrollAreaWidgetContents);
//scrollArea->setWidgetResizable(true);
verticalLayout_5->addWidget(scrollArea);
The output window always looks like the following.
In this example the input by the user is 25 however you can see that the 21st button is cut off and 4 other buttons are not visible.
The size window problem occurring after scroll functionality started working.
You need to add your horizontalWidget to a vertical widget like so:
QVBoxLayout* vLayout = new QVBoxLayout();
for (int i = 0; i < numberOfSlices; i++)
{
QWidget *horizontalWidget = new QWidget();
vLayout->addWidget(horizontalWidget);
....
}
scrollAreaWidgetContents->setLayout(vLayout);
You second problem looks like it comes from this line:
scrollArea = new QScrollArea(verticalWidget);
You're adding scrollArea directly to verticalWidget, but to get it to lay out the way you want you need to put it in a layout. Try the following instead:
QVBoxLayout* l = new QVBoxLayout();
l->addWidget(sliceLabel); // or whatever you call it
l->addWidget(scrollArea);
l->addWidget(clearButton); // again, your name here
verticalWidget->setLayout(l);
Try playing around with the QScrollBarPolicy.
http://doc.qt.digia.com/qt/qabstractscrollarea.html#horizontalScrollBarPolicy-prop
I'm guessing that the default behavior isn't working because there is something strange going on with layouts.

How to get the QLayouts to expand properly?

My structure is as follows:
QWidget
-QHBoxLayout
-QLabel
-QVBoxLayout
-QLabel
-QWebView
I want the HBoxLayout to fill the width however large the container may be but go no more or less. However, I want the QVBoxLayout to expand to accommodate the size of its contents in the vertical direction.
+-------------+------------------------------+
| FixedTitle: | Expanding to Width Title +
| |------------------------------+
| | +
| | this is a test which wraps to+
| | the next line +
| | +
| | +
| | +
| | bla bla bla +
| | +
| | +
| | +
| | there are no vertical scroll +
| | bars here +
+-------------+------------------------------+
In this example, FixedTitle's width is however big it needs to be, but does not resize ever. Expanding to Width Title fills up the remaining horizontal space.
So far, I have:
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QHBoxLayout *layout = new QHBoxLayout;
this->setLayout(layout);
layout->addWidget(new QLabel(QString("FixedTitle")), 0, Qt::AlignTop);
QVBoxLayout *v_layout = new QVBoxLayout;
v_layout->setSizeConstraint(QLayout::SetNoConstraint);
layout->addLayout(v_layout);
v_layout ->addWidget(new QLabel(QString("Expanding to Width Title")), 1, Qt::AlignTop | Qt::AlignLeft);
QWebView *view = new QWebView();
QTextEdit text;
text.setPlainText(QSString("\nthis is a test which wraps to the next line\n\n\nbla bla bla\n\n\nthere are no vertical scroll bars here"));
view->setHtml(text.toHtml());
int width = view->page()->mainFrame()->contentsSize().width();
int height = view->page()->mainFrame()->contentsSize().height();
view->page()->setViewportSize(QSize(width, height));
view->resize(width, height);
view->setFixedSize(width, height);
v_layout->addWidget(view);
There are two problems with this: 1. It ignores the width of the container and 2. It still doesnt get the height of the QWebView correct.
How do I fix this?
This is my take on an answer... and forgive me but its written in PyQt.
I feel that your shouldn't be thinking so much about resizing the containing widget to the contents of the QWebView, but rather just have the size policy set to expanding, turn off the scrollbars, and defer sizing to whatever layout this container is added to. It makes no sense to try and manually resize it.
from PyQt4 import QtCore, QtGui, QtWebKit
class WebWidget(QtGui.QWidget):
def __init__(self):
super(WebWidget, self).__init__()
layout = QtGui.QHBoxLayout(self)
title = QtGui.QLabel("FixedTitle:")
# title.setText("A much larger fixed title")
title.setSizePolicy(
QtGui.QSizePolicy.Preferred,
QtGui.QSizePolicy.Fixed)
layout.addWidget(title, 0, QtCore.Qt.AlignTop)
v_layout = QtGui.QVBoxLayout()
layout.addLayout(v_layout)
expandingTitle = QtGui.QLabel("Expanding to Width Title")
expandingTitle.setSizePolicy(
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Fixed)
v_layout.addWidget(expandingTitle)
text = QtGui.QTextEdit()
view = QtWebKit.QWebView()
view.setSizePolicy(
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
view.page().mainFrame().setScrollBarPolicy(
QtCore.Qt.Vertical,
QtCore.Qt.ScrollBarAlwaysOff )
view.page().mainFrame().setScrollBarPolicy(
QtCore.Qt.Horizontal,
QtCore.Qt.ScrollBarAlwaysOff )
v_layout.addWidget(view, 1)
text.setPlainText("""
this is a test which wraps to the next line\n\n\n
bla bla bla\n\n\nthere are no vertical scroll bars here
""")
view.setHtml(text.toHtml())
v_layout.addStretch()
self.view = view
view.page().mainFrame().contentsSizeChanged.connect(self.updateWebSize)
def updateWebSize(self, size=None):
if size is None:
size = self.view.page().mainFrame().contentsSize()
self.view.setFixedSize(size)
def resizeEvent(self, event):
super(WebWidget, self).resizeEvent(event)
self.updateWebSize()
if __name__ == "__main__":
app = QtGui.QApplication([])
w = QtGui.QScrollArea()
w.resize(800,600)
web = WebWidget()
w.setWidget(web)
w.setWidgetResizable(True)
w.show()
app.exec_()
The left title is set to preferred width and fixed height, so that it gets the width it needs but doesn't grow vertically.
The expanding title gets an expanding width policy and a fixed height.
The QWebView gets expanding policies in both directions, and its scrollbars turned off.
And for an example. I just created a QScrollArea and set the WebWidget into it, so that you can see the parent layout will allow the Web view to grow as big as it wants to, but will handle overflow with scrollbars.