一行Elideable QLabel

One-line Elideable QLabel

我正在尝试制作一个仅在 Qt 中可用 space 的 Qlabel(实际上是在 PyQt 中,但同样的原则适用)。它旨在显示一个(有时非常长的)文件路径,正如我目前所拥有的那样,标签的长度使得 window 的那部分的最小尺寸太大了。

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

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

我的布局供参考:

您可以覆盖 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 占用最大可用 space。这样 self.width() returns 当前最大宽度。您可以查看有关 QTextDocument()QFontMetrics() 工作的文档。此外,self.width() - 10 只是确保省略标签中的 '...' 未隐藏,您可以删除 - 10 并在 .. 之后对您可见时使用 self.width()也将其删除。

QLabel 是一个非常简洁的小部件:它似乎非常简单,但事实并非如此。

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

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

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

  • 提供不考虑全部内容的基本[最小]尺寸提示
  • 当可用的 space 不够时,通过省略文本来覆盖绘画

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

这是一个显示子类 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()))