C++ – How to get the QLayouts to expand properly

clayoutqtqwebviewwidget

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?

Best Answer

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.