Search code examples
pythonpyqtpyqt5qgridlayoutqtextbrowser

How to Pack with PyQt - how to make QFrame/Layout adapt to content


I am building a grid of QTextEdit with HTML Subset to show some data within an interface in PyQt.

At the moment I get the GridLayout and the QFrame that contains it doing whatever they want and there is no way for me to make them fit the QTextEdit size. I plaid with SizePolicy but does not work. see from the image how extra space appears on the side of each QText and how the GridLayout cuts the QTextEdit in height.

The only way to make it work is if I set the correct QFrame size to make everything fit, but being that the size of the QTextEdit is customizable, I would like the QFrame to simply fit whatever it has to contain instead of getting free will +_+

Notice that the GridLayout is usually bigger than the mainwindow so it will not need to fit the window hence why need a scrollArea.

What I do not want: What I do not want to Happen

As it should be:

What I want to happen notice that the image I want has been obtained adding this line (in MainFrame()):

container.resize(1100,1360)

As I said I want to obtain the same behaviour but without specifying the dimension for QFrame, this should fit the content (the subset HTML tables)

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from random import *

class Box(QTextBrowser):

    def __init__(self):
        super().__init__()

        self.setText('''
            <table border="0" cellspacing="0" cellpadding="5" style="background-color: rgba(119, 212, 212, 0.7);">
                <tr>
                    <td width="100">
                    bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla
                    </td>
                <tr>
                <tr>
                    <td>
                    bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla
                    </td>
                <tr>
            </table>

        ''')

        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.setContentsMargins(0,0,0,0)

        cstring ="""
        QTextBrowser {
            border: 0;
            background-color: #<---->;
            margin: 0px;
            padding-left:0;
            padding-top:0;
            padding-bottom:0;
            padding-right:0;
        }
        """

        ncol = randint(300000, 999999)

        cstring = cstring.replace('<---->', str(ncol))

        self.setStyleSheet(cstring)


class MainFrame(QScrollArea):
    def __init__(self):
        super().__init__()

        container = QFrame(self)


        layout = QGridLayout(container)

        for row in range(0, 5):
            for col in range(0, 10):
                QGridLayout.addWidget(layout, Box(), row, col)

        self.setWidget(container)

        container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        layout.setContentsMargins(0,0,0,0)
        layout.setSpacing(0)

        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = MainFrame()
    sys.exit(app.exec_())

Solution

  • To get the right size, you must use the idealWidth() method of document(), but this will only be correct when the item is visible so you should apply it when the widget is visible, so you should call the show() method that this change is applied, in addition to calling it every time the text changes.

    To get the correct height, use the size() method of QTextDocument()

    class Box(QTextBrowser):
        def __init__(self, parent=None):
            QTextBrowser.__init__(self, parent)
            self.setContentsMargins(0, 0, 0, 0)
            self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    
            cstring = """
            QTextBrowser {
                border: 0;
                background-color: #<---->;
                margin: 0px;
                padding-left:0;
                padding-top:0;
                padding-bottom:0;
                padding-right:0;
            }
            """
    
            ncol = randint(300000, 999999)
            cstring = cstring.replace('<---->', str(ncol))
            self.setStyleSheet(cstring)
    
            self.document().contentsChange.connect(lambda: self.customGeometry())
            self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
            self.setContentsMargins(0, 0, 0, 0)
    
        def customGeometry(self):
            if self.isVisible():
                self.setFixedWidth(self.document().idealWidth())
                self.setFixedHeight(self.document().size().height())
    
        def showEvent(self, event):
            self.customGeometry()
            QTextBrowser.showEvent(self, event)
    
    
    class MainFrame(QScrollArea):
        def __init__(self, parent=None):
            QScrollArea.__init__(self, parent)
            container = QFrame(self)
    
            layout = QGridLayout(container)
            layout.setSpacing(0)
            layout.setContentsMargins(0, 0, 0, 0)
            self.show()
    
            text = '''
                    <table border="0" cellspacing="0" cellpadding="5" style="background-color: rgba(119, 212, 212, 0.7);">
                    <tr>
                        <td width="100">
                            bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla
                        </td>
                    <tr>
                    <tr>
                       <td>
                            bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla
                        </td>
                    <tr>
                    </table>
                    '''
    
            for row in range(5):
                for col in range(10):
                    box = Box(container)
                    box.setText(text)
                    box.show()
                    layout.addWidget(box, row, col)
    
            self.setWidget(container)
    
    
    if __name__ == '__main__':
        import sys
    
        app = QApplication(sys.argv)
        ex = MainFrame()
        ex.show()
        sys.exit(app.exec_())
    

    Output:

    enter image description here