【问题标题】:One-line Elideable QLabel单行可省略 QLabel
【发布时间】:2021-06-23 11:31:37
【问题描述】:

我正在尝试制作一个仅填充 Qt 中可用空间的 Qlabel(实际上在 PyQt 中,但同样的原则适用)。它旨在显示一个(有时很长)文件路径,并且正如我目前所拥有的那样,标签的长度使得该窗口部分的最小尺寸太大。

我想让标签的文本减少到最大可能的长度由于面板中的其他小部件而不会超过最小宽度。我找到了 QFontMetrics::elideText() 方法,它可以有效地按照我想要的方式剪切文本,但我仍然无法弄清楚如何在不影响面板大小的标签的情况下获得像素宽度。

我的 hackjob 思考过程是通过覆盖 size/minimumsize/maximumsize 将 Qlabel 的大小设置为零,测量分配的剩余空间,然后将文本设置为该值。但是,我不知道如何获得剩余的空间,我觉得应该有更好的方法。

我的布局供参考:

【问题讨论】:

    标签: python pyqt


    【解决方案1】:

    QLabel 是一个非常简洁的小部件:它看起来非常简单,但实际上并非如此。

    大小和显示方面非常重要:因为它能够显示格式化文本,它甚至可以有一些layout issues

    由于您的要求是使标签尽可能小(但尽可能保持其内容显示),因此最重要的要求是实现sizeHint(和minimumSizeHint())函数,因为父小部件在调整其内容大小时会考虑这一点。

    可能的解决方案基于两个方面:

    • 提供不考虑全部内容的基本 [最小] 大小提示
    • 当可用空间不足时,通过删除文本来覆盖绘画

    以下代码显然考虑富文本格式,包括不同的段落对齐方式、自动换行等。

    这是一个显示子类 QLabel 尝试显示以下路径的示例: '/tmp/test_dir/some_long_path/some_subdir/imagepath/'

    考虑一下,您甚至可以实际使用基本的 QWidget。在下面的代码中,我正在考虑 QFrame 子类化功能,其中还包括添加适当的边距和边框,具体取决于样式和 frameShapeframeShadow 属性。

    class ElideLabel(QtWidgets.QLabel):
        _elideMode = QtCore.Qt.ElideMiddle
    
        def elideMode(self):
            return self._elideMode
    
        def setElideMode(self, mode):
            if self._elideMode != mode and mode != QtCore.Qt.ElideNone:
                self._elideMode = mode
                self.updateGeometry()
    
        def minimumSizeHint(self):
            return self.sizeHint()
    
        def sizeHint(self):
            hint = self.fontMetrics().boundingRect(self.text()).size()
            l, t, r, b = self.getContentsMargins()
            margin = self.margin() * 2
            return QtCore.QSize(
                min(100, hint.width()) + l + r + margin, 
                min(self.fontMetrics().height(), hint.height()) + t + b + margin
            )
    
        def paintEvent(self, event):
            qp = QtGui.QPainter(self)
            opt = QtWidgets.QStyleOptionFrame()
            self.initStyleOption(opt)
            self.style().drawControl(
                QtWidgets.QStyle.CE_ShapedFrame, opt, qp, self)
            l, t, r, b = self.getContentsMargins()
            margin = self.margin()
            try:
                # since Qt >= 5.11
                m = self.fontMetrics().horizontalAdvance('x') / 2 - margin
            except:
                m = self.fontMetrics().width('x') / 2 - margin
            r = self.contentsRect().adjusted(
                margin + m,  margin, -(margin + m), -margin)
            qp.drawText(r, self.alignment(), 
                self.fontMetrics().elidedText(
                    self.text(), self.elideMode(), r.width()))
    

    【讨论】:

    • 这很好用!因为我在 qt6 中,所以我不得不改变一些东西,但这很有效。最重要的秘密也是覆盖 minimumsizehint() (不知道那是一件事),但渲染看起来也很棒。
    【解决方案2】:

    您可以覆盖paintEvent()并通过以下方式实现:

    class QElidedLabel(QLabel):
        def paintEvent(self, event):
            painter = QPainter(self)
            textDoc = QTextDocument()
            metrics = QFontMetrics(self.font())
            elided = metrics.elidedText(self.text(), Qt.ElideRight, self.width() - 10)
            textDoc.setPlainText(elided)
            textDoc.drawContents(painter)
    

    这将自动绘制 Elided Label,您无需在其他任何地方更改代码。您还可以将QLabel 的大小策略设置为MinimumExpanding,以确保您的QLabel 占用最大可用空间。这样self.width() 返回当前最大宽度。您可以查看QTextDocument()QFontMetrics() 的工作文档。此外,self.width() - 10 只是确保省略标签中的'...' 未被隐藏,您可以删除- 10,如果.. 在删除后对您可见,则只需使用self.width()

    【讨论】:

    • 注意:1. 10 的值是任意的,因为它取决于当前的 fontMetrics。 2. text() 将始终返回“原始”文本,因此尝试将 QTextDocument 与 setHtml 一起使用是没有意义的,因为它与实际解析的 html 文本不一致(如果省略“也可能无效”剪掉”标签)。
    • 这几乎很好用,但我无法将大小调整为小于文本宽度 [我在更大的窗口上使用 qsplitter] 并且文本的底部被切断,即使我设置最小高度
    • @musicamante 这是从我的原始代码派生的文本格式 QLabel 需要,所以这就是 setHtml 的原因。这里可以使用setPlainText()。如有必要,我也可以分享格式化省略的 QLabel 的代码。
    • @minerharry 你可以使用textDoc.setPlainText(),这可能会解决问题。我现在也编辑了代码。如果它仍然是一个问题,那么您还可以将对齐设置为 Top 可能能够修复它。
    • @YogeshBhandari 我理解,但是 OP 有不同的要求需要考虑。虽然开始使用 QTextDocument 并不是一个坏主意(QLabel 确实这样做,但这是一个假设,因为 text 可以是任何格式),并且 OP 可能不需要格式化文本,使用 @987654340 @ 在概念上仍然是错误的,因为它基于 text 属性,它不提供有关格式的任何提示,因为它返回 QTextDocument 原始文本(除非 Qt.mightBeRichText() 将用于检查),所以省略可能仍然是错误的。
    猜你喜欢
    • 1970-01-01
    • 2012-07-24
    • 1970-01-01
    • 1970-01-01
    • 2012-07-04
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多