How does stretch factor work in Qt?
Asked Answered
S

2

7

I'm working on a GUI application with pyqt5. At a certain dialog I need many components, being one of those a QWebEngineView as a canvas for plotting data, which should take most of the space available even if the chart is not ready when creating the dialog.

I expect it to look something like this: enter image description here

I investigated and found about the stretch factor. I saw that QSizePolicy is directly applicable only to widgets and not to layouts, as shown in this SO answer. But then I saw that the methods addWidget and addLayout allow me to set the stretch factor in the direction of the QBoxLayout, and that seemed ideal for my intentions.

So I tried with:

from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout
from strategy_table import StrategyTable

layout = QVBoxLayout()
layout.addWidget(QLabel("Strategy components"))

# Upper layout for a table. Using a 30% of vertical space
upper_layout = QHBoxLayout() # I'm using a HBox because I also want a small button at the right side of the table
self.table = StrategyTable(self) # Own class derived from QTableWidget
upper_layout.addWidget(self.table)

add_button = QPushButton('Add')
add_button.clicked.connect(self._show_add_dialog)
upper_layout.addWidget(add_button)
layout.addLayout(upper_layout, 3) # Setting 20% of size with the stretch factor

# Then the plot area, using 60% of vertical space
layout.addWidget(QLabel("Plot area"))
canvas = QWebEngineView()
layout.addWidget(self.canvas, 6)


# Finally, a small are of 10% of vertical size to show numerical results
layout.addWidget(QLabel("Results"))
params_layout = self._create_results_labels() # A column with several QLabel-QTextArea pairs, returned as a QHBoxLayout
layout.addLayout(params_layout, 1)

self.setLayout(layout)

But it looked exactly the same as before: enter image description here

It looked quite ok before adding the results Layout at the bottom, I guess because the upper table is empty at the beginning, and therefore took very little space and left the rest to the canvas.

Anyway, it seems that the stretch factor is being ignored, so I don't know if I am missing something here, or that I didn't fully understand the stretch factor.

BTW, I know I would use the QtEditor for designing the GUI, but I kind of prefer doing these things manually.

Senescent answered 29/8, 2018 at 6:52 Comment(2)
What happens if you put an empty Results pane? I think it is not like you want since the text boxes 'need' more spaceKilar
@Kilar If a set an empty QHBoxLayout as the results pane, then it looks exactly as expected. Why is that?Senescent
R
9

The problem is simple, the layouts handle the position and size of the widgets, but it has limits, among them the minimum size of the widgets, and in your case the last element has a height higher than 10%, so physically it is impossible. We can see that by removing the content or using a QScrollArea:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets

class StrategyTable(QtWidgets.QTableWidget):
    pass

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)

        table = StrategyTable()
        button = QtWidgets.QPushButton("Add")
        hlay = QtWidgets.QHBoxLayout()
        hlay.addWidget(table)
        hlay.addWidget(button)

        canvas = QtWebEngineWidgets.QWebEngineView()
        canvas.setUrl(QtCore.QUrl("http://www.google.com/"))

        scroll = QtWidgets.QScrollArea()
        content_widget = QtWidgets.QWidget()
        scroll.setWidgetResizable(True)
        scroll.setWidget(content_widget)
        vlay = QtWidgets.QVBoxLayout()
        vlay.addWidget(QtWidgets.QLabel("Results:"))
        params_layout = self._create_results_labels()
        content_widget.setLayout(params_layout)
        vlay.addWidget(scroll)

        lay.addLayout(hlay, 3)
        lay.addWidget(canvas, 6)
        lay.addLayout(vlay, 1)


    def _create_results_labels(self):

        flay = QtWidgets.QFormLayout()
        for text in ("Delta", "Gamma", "Rho", "Theta", "Vega"):
            flay.addRow(text, QtWidgets.QTextEdit())
        return flay


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

enter image description here

Rustic answered 29/8, 2018 at 8:15 Comment(0)
T
8

Hope this will be useful to understand Layouts along with stretch factor

QVBoxLayout *topMostVerticalLayout = new QVBoxLayout(this);
QHBoxLayout *upperHorznLayout = new QHBoxLayout();
QHBoxLayout *bottomHorznLayout = new QHBoxLayout();

 QVBoxLayout *InnerVerticalLayout1 = new QVBoxLayout(this);
 QVBoxLayout *InnerVerticalLayout2 = new QVBoxLayout(this);
 QVBoxLayout *InnerVerticalLayout3 = new QVBoxLayout(this);
 QVBoxLayout *InnerVerticalLayout4 = new QVBoxLayout(this);
 QVBoxLayout *InnerVerticalLayout5 = new QVBoxLayout(this);

bottomHorznLayout->addLayout(InnerVerticalLayout1,15); //(15% stretch)
bottomHorznLayout->addLayout(InnerVerticalLayout2,15); //(15% stretch)
bottomHorznLayout->addLayout(InnerVerticalLayout3,15); //(15% stretch)
bottomHorznLayout->addLayout(InnerVerticalLayout4,15); //(15% stretch)
bottomHorznLayout->addLayout(InnerVerticalLayout5,40); //(40% stretch)

topMostVerticalLayout->addLayout(upperHorznLayout,3); //(30% stretch)
topMostVerticalLayout->addLayout(bottomHorznLayout,7); //(70% stretch)

this->setLayout(topMostVerticalLayout);

The result is:

Sample Layout based on percentages (CPP)

Tinishatinker answered 16/7, 2020 at 6:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.