0

我在 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() 因为在没有 show() 的情况下调用 document().size(),值将全部为 0,我不知道为什么会这样。

4

1 回答 1

0

问题来自您试图过早设置高度的事实。实际上,如果您在print(self.show())之后添加一个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 约定);
于 2021-07-06T13:54:17.803 回答