A QWidget like QTextEdit that wraps its height automatically to its contents?
Asked Answered
G

2

12

I am creating a form with some QTextEdit widgets.

The default height of the QTextEdit exceeds a single line of text and as the contents' height exceeds the QTextEdit's height, it creates a scroll-bar to scroll the content.

I would like to override this behaviour to create a QTextEdit that would rather wrap its height to its contents. This means that the default height would be one line and that on wrapping or entering a new line, the QTextEdit would increase its height automatically. Whenever the contents height exceeds the QTextEdit's height, the latter should not create a scroll bar but simply increase in height.

How can I go about doing this? Thanks.

Gouveia answered 7/8, 2012 at 17:24 Comment(0)
C
7

This is almost exactly like a question I answer the other day about making a QTextEdit adjust its height in reponse to content changes: PySide Qt: Auto vertical growth for TextEdit Widget

I am answering instead of marking a duplicate as I suspect its possible you want a variation on this. Let me know if you want me to expand this answer:

The other question had multiple parts. Here is the excerpt of the growing height widget:

class Window(QtGui.QDialog):

    def __init__(self):
        super(Window, self).__init__()
        self.resize(600,400)

        self.mainLayout = QtGui.QVBoxLayout(self)
        self.mainLayout.setMargin(10)

        self.scroll = QtGui.QScrollArea()
        self.scroll.setWidgetResizable(True)
        self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.mainLayout.addWidget(self.scroll)

        scrollContents = QtGui.QWidget()
        self.scroll.setWidget(scrollContents)

        self.textLayout = QtGui.QVBoxLayout(scrollContents)
        self.textLayout.setMargin(10)

        for _ in xrange(5):
            text = GrowingTextEdit()
            text.setMinimumHeight(50)
            self.textLayout.addWidget(text)


class GrowingTextEdit(QtGui.QTextEdit):

    def __init__(self, *args, **kwargs):
        super(GrowingTextEdit, self).__init__(*args, **kwargs)  
        self.document().contentsChanged.connect(self.sizeChange)

        self.heightMin = 0
        self.heightMax = 65000

    def sizeChange(self):
        docHeight = self.document().size().height()
        if self.heightMin <= docHeight <= self.heightMax:
            self.setMinimumHeight(docHeight)
Christos answered 8/8, 2012 at 6:25 Comment(10)
This is exactly what I needed. Had not come across this with my search terms though. Thanks!Gouveia
Also (I couldn't see that in your other post) I had to connect the following in order to wrap the QTextEdit's height to its document when the former is resized: self.document().documentLayout().documentSizeChanged.connect(self.wrapHeightToContents) .Gouveia
If you see in my example i am using a different signal higher up. I didnt need any other connectionsChristos
@Christos The signal that you connect to is protected in c++Impiety
@chacham15: Thanks for pointing that out for any c++ users. Fortunately this question was surrounding the python bindings :-)Christos
One might want to also react to width changes by overriding resizeEvent.Softboiled
Maybe one can connect the text_edit's textChanged signal, and call sender().document().size().height() to get the height, so you don't need to subclass QTextEdit?Doctrinaire
@Doctrinaire if you don't want a reusable widget with this behavior, sure.Christos
Does this work well with resizing? I tried very similar method as yours (I also setMaximumHeight(docHeight+1)), but when resizing the main window I still got scroll bars. I guess I also need to connect sizeChange() somehow to a resize signal?Doctrinaire
answer myself: self.document().documentLayout().documentSizeChanged might be the signal to connect.Doctrinaire
I
7

the following code sets a QTextEdit widget to the height of the content:

# using QVBoxLayout in this example
grid = QVBoxLayout()
text_edit = QTextEdit('Some content. I make this a little bit longer as I want to see the effect on a widget with more than one line.')

# read-only
text_edit.setReadOnly(True)

# no scroll bars in this example
text_edit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 
text_edit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 
text_edit.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

# you can set the width to a specific value
# text_edit.setFixedWidth(400)

# this is the trick, we nee to show the widget without making it visible.
# only then the document is created and the size calculated.

# Qt.WA_DontShowOnScreen = 103, PyQt does not have this mapping?!
text_edit.setAttribute(103)
text_edit.show()

# now that we have a document we can use it's size to set the QTextEdit's size
# also we add the margins
text_edit.setFixedHeight(text_edit.document().size().height() + text_edit.contentsMargins().top()*2)

# finally we add the QTextEdit to our layout
grid.addWidget(text_edit)

I hope this helps.

Instrumentality answered 28/11, 2012 at 6:33 Comment(3)
This is a very interesting trick, but part of me thinks there must be some other way. But the other part of me realizes that this may be the best way in practice, because you don't have to calculate frame widths and such and figure all that out.Fridafriday
Technically contentsMargins().top() + contentsMargins().bottom() is more correct than contentsMargins().top()*2 although this values are usually the same.Softboiled
10+ years after this answer was posted, it certainly helped me. Thanks!Disturb

© 2022 - 2024 — McMap. All rights reserved.