【问题标题】:PyQt6 auto-resize QTextEdit to fit content problemPyQt6 自动调整 QTextEdit 大小以适应内容问题
【发布时间】:2021-09-16 23:09:38
【问题描述】:

我在 QScrollArea 的 QVBoxLayout 中有一堆 QTextEdits。

文本通常会变得很长,水平空间受设计限制,QTextEdit 会自动将文本换成多行。

我想自动调整 QTextEdit 的大小以适合换行的文本,文本本身将始终在一行中,并且换行的文本可以有多行,我希望 QTextEdits 适合换行的高度。

通过数小时的谷歌搜索,我找到了一个解决方案,但它没有按预期工作,有时底部可能会多出一行,我将在下面发布示例代码:

from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *

font = QFont('Noto Serif', 9)

class Editor(QTextEdit):
    doubleClicked = pyqtSignal(QTextEdit)
    def __init__(self):
        super().__init__()
        self.setReadOnly(True)
        self.setFont(font)
        self.textChanged.connect(self.autoResize)
        self.margins = self.contentsMargins()
        self.setAttribute(Qt.WidgetAttribute.WA_DontShowOnScreen)
    @pyqtSlot(QMouseEvent)
    def mouseDoubleClickEvent(self, e: QMouseEvent) -> None:
        self.doubleClicked.emit(self)
    
    def autoResize(self):
        self.show()
        height = int(self.document().size().height() + self.margins.top() + self.margins.bottom())
        self.setFixedHeight(height)

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(405, 720)
        frame = self.frameGeometry()
        center = self.screen().availableGeometry().center()
        frame.moveCenter(center)
        self.move(frame.topLeft())
        self.centralwidget = QWidget(self)
        self.vbox = QVBoxLayout(self.centralwidget)
        self.scrollArea = QScrollArea(self.centralwidget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollAreaWidgetContents = QWidget()
        self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
        self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents)
        self.verticalLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.scrollArea.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
        self.vbox.addWidget(self.scrollArea)
        self.setCentralWidget(self.centralwidget)

def addItems():
    items = [
        "L'Estro Armonico No. 9 in D Major\u2014III. Allegro", 
        "L'Estro Armonico No. 6 in A Minor\u2014III. Presto",
        "L'Estro Armonico No. 6 in A Minor\u2014I. Allegro",
        "L'Estro Armonico No. 1 in D major\u2014I. Allegro",
        "12 Concertos Op.3 \u2014 L'estro Armonico \u2014 Concerto No. 6 In A Minor For Solo Violin RV 356\u2014Presto",
        "Ultimate Mozart\u2014 The Essential Masterpieces",
        "Serenade in G K.525 Eine kleine Nachtmusik\u20141. Allegro",
        "Vivaldi\u2014 L'estro Armonico",
        "Academy of St. Martin in the Fields",
        "Are You With Me \u2014 Reality"
        ]
    for i in items:
        textbox = Editor()
        textbox.setText(i)
        window.verticalLayout.addWidget(textbox)


app = QApplication([])
window = Window()
window.show()
addItems()
app.exec()

请注意,您需要 Noto Serif 才能正确运行(当然您可以直接替换它),当然您还需要 PyQt6。

在示例中,前七个文本框的底部都有一个额外的空行,而后三个则没有。

是什么导致了多余的行以及如何删除它?

更新:

我必须设置Qt.WidgetAttribute.WA_DontShowOnScreen,因为如果我不设置,调用QTextEdit的.show()会导致QTextEdit出现在屏幕中间并很快消失,很烦人。

我必须调用.show(),因为调用document().size()没有show(),值都会是0,我不知道为什么会这样。

【问题讨论】:

  • 尝试设置Qt.WidgetAttribute.WA_DontShowOnScreen 属性是否有特定原因?

标签: python python-3.x pyqt pyqt6


【解决方案1】:

问题在于您尝试过早设置高度。事实上,如果您在 show() 之后添加 print(self.show()),您会看到所有内容都将显示默认大小(可能是 256x192)。

这取决于两个方面:

  1. 第一次显示小部件时,它还没有完全“映射”到操作系统窗口管理中,因此它将根据许多方面使用默认大小;
  2. 您在将文本添加到布局之前设置文本,因此 QTextEdit 将不知道所需的父级大小;

那么另一个问题出现了:如果窗口被调整大小,内容将不会适应,直到文本被改变。

为了根据内容正确设置垂直高度,您应该设置文档的textWidth,并在每次调整小部件大小时调用autoResize

class Editor(QTextEdit):
    def __init__(self):
        super().__init__()
        self.setReadOnly(True)
        self.setFont(font)
        self.textChanged.connect(self.autoResize)

    def autoResize(self):
        self.document().setTextWidth(self.viewport().width())
        margins = self.contentsMargins()
        height = int(self.document().size().height() + margins.top() + margins.bottom())
        self.setFixedHeight(height)

    def resizeEvent(self, event):
        self.autoResize()

注意:

  • 边距应该是动态访问的,而不是存储在__init__中;
  • mouseDoubleClickEvent 是一个在鼠标事件上调用的函数,它不是(也不应该)是一个插槽,所以使用 pyqtSlot 装饰器是没有意义的;
  • 虽然在概念上适用于“主”布局,例如滚动区域内容的布局,但设置布局的对齐方式不会设置其项目的对齐方式,而只会设置布局的对齐方式;虽然结果经常相同,但在实践中却大不相同(因此结果并不总是相同,尤其是当更多布局添加到同一个父布局时);
  • 在文本字段中双击非常常用来进行高级选择(通常是选择光标下的单词),应谨慎考虑选择阻止此类操作(从而更改已知的 UI 约定);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-02
    • 2018-05-22
    • 2013-06-05
    • 2011-11-03
    • 2011-08-20
    • 1970-01-01
    • 2021-12-17
    • 2015-12-27
    相关资源
    最近更新 更多