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.

neydroydrec
  • 6,973
  • 9
  • 57
  • 89

2 Answers2

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)
Community
  • 1
  • 1
jdi
  • 90,542
  • 19
  • 167
  • 203
  • This is exactly what I needed. Had not come across this with my search terms though. Thanks! – neydroydrec Aug 08 '12 at 19:30
  • 2
    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)` . – neydroydrec Aug 09 '12 at 04:37
  • 1
    If you see in my example i am using a different signal higher up. I didnt need any other connections – jdi Aug 09 '12 at 04:44
  • 1
    @jdi The signal that you connect to is protected in c++ – chacham15 Dec 27 '12 at 22:00
  • @chacham15: Thanks for pointing that out for any c++ users. Fortunately this question was surrounding the python bindings :-) – jdi Dec 28 '12 at 00:00
  • One might want to also react to width changes by overriding `resizeEvent`. – Андрей Беньковский Jul 04 '16 at 19:24
  • 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`? – Jason Sep 18 '18 at 08:00
  • @Jason if you don't want a reusable widget with this behavior, sure. – jdi Sep 18 '18 at 08:54
  • 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? – Jason Sep 28 '18 at 11:40
  • answer myself: `self.document().documentLayout().documentSizeChanged` might be the signal to connect. – Jason Sep 28 '18 at 12:27
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.

laurasia
  • 666
  • 7
  • 8
  • 1
    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. – eric Dec 31 '15 at 21:15
  • 1
    Technically `contentsMargins().top() + contentsMargins().bottom()` is more correct than `contentsMargins().top()*2` although this values are usually the same. – Андрей Беньковский Jul 04 '16 at 19:26
  • 10+ years after this answer was posted, it certainly helped me. Thanks! – Escovado Dec 22 '22 at 00:28