python - QLabel setMinimumHeight 在自定义 WordWrap Qt.TextWrapAnywhere PyQt5 之后(完全响应有/没有表情符号)

标签 python qt pyqt5 word-wrap qlabel

我想在布局中为我的 QLabel 使用 Qt.TextWrapAnywhere

我关注了This指令。我的代码也一样,给出一个最小的代码

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QStyleOption, QVBoxLayout, QWidget, QStyle

class SuperQLabel(QLabel):
    def __init__(self, *args, **kwargs):
        super(SuperQLabel, self).__init__(*args, **kwargs)

        self.textalignment = Qt.AlignLeft | Qt.TextWrapAnywhere
        self.isTextLabel = True
        self.align = None

    def paintEvent(self, event):

        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)

        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)

        self.style().drawItemText(painter, self.rect(),
                                  self.textalignment, self.palette(), True, self.text())


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.resize(100, 200)

        self.label = QLabel()
        self.label.setWordWrap(True)
        self.label.setText("11111111111111111111\n2222222211111111")

        self.slabel = SuperQLabel()
        self.slabel.setMinimumWidth(10)
        self.slabel.setText("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")

        self.centralwidget = QWidget()
        self.setCentralWidget(self.centralwidget)

        self.mainlayout = QVBoxLayout()
        self.mainlayout.addWidget(self.label)
        self.mainlayout.addWidget(self.slabel)

        self.centralwidget.setLayout(self.mainlayout)


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

我对代码 self.slabel.setMinimumWidth(10) 做了一点改动,否则根据宽度调整 Label 大小将不起作用。

它根据宽度完美地包裹文本。但问题是当考虑高度时 self.label = QLabel() 正常 QLabel 会根据布局内容自动调整高度。

例如,如果我添加一个 \n,其中包含意味着 Qlabel 必须显示 2 行的文本。

但是有了这个新的自定义标签,例如self.slabel = SuperQLabel(),只要布局中有高度空间,包装就很好。 我想我必须使用 setminimumHeight() 但不知道如何在自定义包装后获得正确的高度。

最佳答案

只要标签显示在滚动区域(不会对顶层布局造成问题),更好的解决方案是使用 QTextEdit 子类,配置如下:

  • readOnly 必须为 True;
  • 禁用滚动条;
  • 垂直尺寸政策必须是Preferred(而不是Expanding);
  • minimumSizeHint()sizeHint() 都应使用内部 QTextDocument 返回适当的高度,并具有最小默认宽度;
  • 任何大小或内容的变化都必须触发updateGeometry()这样父布局就会知道提示已更改并且可以再次计算几何图形;
  • 提示必须包括滚动区域的可能装饰(这是一个 QFrame);

这允许避免 paintEvent() 重写,并且由于 QTextDocument 提供的特性而提供更好和更容易的大小机制实现,同时将递归的可能性最小化到可接受的水平。

class WrapLabel(QtWidgets.QTextEdit):
    def __init__(self, text=''):
        super().__init__(text)
        self.setStyleSheet('''
            WrapLabel {
                border: 1px outset palette(dark);
                border-radius: 8px;
                background: palette(light);
            }
        ''')
        self.setReadOnly(True)
        self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, 
            QtWidgets.QSizePolicy.Maximum)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.textChanged.connect(self.updateGeometry)

    def minimumSizeHint(self):
        doc = self.document().clone()
        doc.setTextWidth(self.viewport().width())
        height = doc.size().height()
        height += self.frameWidth() * 2
        return QtCore.QSize(50, height)

    def sizeHint(self):
        return self.minimumSizeHint()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.updateGeometry()


class ChatTest(QtWidgets.QScrollArea):
    def __init__(self):
        super().__init__()
        self.messages = []

        container = QtWidgets.QWidget()
        self.setWidget(container)
        self.setWidgetResizable(True)

        layout = QtWidgets.QVBoxLayout(container)
        layout.addStretch()
        self.resize(480, 360)

        for i in range(1, 11):
            QtCore.QTimer.singleShot(1000 * i, lambda:
                self.addMessage('1' * randrange(100, 250)))

    def addMessage(self, text):
        self.widget().layout().addWidget(WrapLabel(text))
        QtCore.QTimer.singleShot(0, self.scrollToBottom)

    def scrollToBottom(self):
        QtWidgets.QApplication.processEvents()
        self.verticalScrollBar().setValue(
            self.verticalScrollBar().maximum())

更新:HTML 和 QTextDocument

当使用 setHtml()setDocument() 时,源代码可能有不允许换行的 pre 格式文本。为避免这种情况,有必要遍历所有 QTextBlocks的文件,得到他们的QTextBlockFormat , 检查 nonBreakableLines()属性并最终将其设置为 False 并将格式设置回 QTextCursor .

class WrapLabel(QtWidgets.QTextEdit):
    def __init__(self, text=None):
        super().__init__()
        if isinstance(text, str):
            if Qt.mightBeRichText(text):
                self.setHtml(text)
            else:
                self.setPlainText(text)
        elif isinstance(text, QtGui.QTextDocument):
            self.setDocument(text)
        # ...

    def setHtml(self, html):
        doc = QtGui.QTextDocument()
        doc.setHtml(html)
        self.setDocument(doc)

    def setDocument(self, doc):
        doc = doc.clone()
        tb = doc.begin() # start a QTextBlock iterator
        while tb.isValid():
            fmt = tb.blockFormat()
            if fmt.nonBreakableLines():
                fmt.setNonBreakableLines(False)
                # create a QTextCursor for the current text block,
                # then set the updated format to override the wrap
                tc = QtGui.QTextCursor(tb)
                tc.setBlockFormat(fmt)
            tb = tb.next()
        super().setDocument(doc)

但是请注意,当使用具有预定义或最小宽度的对象时,这还不够:图像和表格。结果将是,如果对象大于可用空间,它将在右侧裁剪(或从 RightToLeft 文本布局的左侧裁剪)。

关于python - QLabel setMinimumHeight 在自定义 WordWrap Qt.TextWrapAnywhere PyQt5 之后(完全响应有/没有表情符号),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70707523/

相关文章:

c++ - 如何在qt creator中将QString转换成int

python - Qt - 不要在 QVBoxLayout 中拉伸(stretch)小部件

python - 如何在 python 中将 qwidgettable 中的列设置为会计格式?

python - 将 Window BAT(批处理)文件翻译成 Python 脚本

python - 如何从 pandas 数据框中选择特定列项目作为列表?

python - 堆叠色图

css - 更改 QComboBox 的悬停样式

python - 在使用 PyQt 捕获网站图像之前等待延迟

c++ - 如何使用相同样式的 "Re-Polish"QApplication?

python - PyQt5:居中对齐标签