如何检测 qt 标签(或任何小部件)是否具有所需的所有 space?

How to detect if a qt label (or any widget) has all the space it would need?

我的小部件在网格布局中有一个长标签,没有任何额外的代码行,禁止缩小 window。在 this post 中,我看到设置 label1.setMiniumSize(1, 1) 可以再次缩小 window。但是,如果缩小 windows 会减少文本的显示,我想在标签中显示文本的缩写形式。

这是一个例子:

  1. 起点:label1.setText("This is a long long long long long long long long label")显示“这是一个长长的长长长长标签”,并抑制window的收缩。

  2. 添加label1.setMinimumSize(1, 1)后window可以缩小显示“This is a long lo”

  3. 我想要显示“This is ... ong label”或“This is a long ... long label”,以适应标签的实际可用尺寸。

我们能否捕捉到布局管理器想要赋予标签的大小的信息?计算对应的字符数,将文本缩写,并设置新的文本?

您可以使用 QFontMetricsF 获取绘制文本所需的 QLabel 的大小。相反,您可以覆盖 QLabelresizeEvent() 以在每次更改标签大小时获取标签的当前大小并使您的文本适应它。

QFontMetricsF FM(ui->label->font());
QRectF rect = FM.boundingRect("A Long Long Long Long Text");
double rectWidth = rect.width();
if (rectWidth > ui->label->width())
{
    // Change text and recalculate in a loop if it fits
}

最好的位置是在标签或父窗口小部件的 resizeEvent() 中。

使用省略号Class。

static void SetTextToLabel(QLabel *label, QString text)
{
    QFontMetrics metrix(label->font());
    int width = label->width() - 2;
    QString clippedText = metrix.elidedText(text, Qt::ElideRight, width);
    label->setText(clippedText);
}

每当您想删除文本时,调用此函数和事件。

有多种方法可以解决这个问题。创建自定义小部件就是其中之一。

所以,我已经根据您的规格修改了 Elided Label Example

  1. 仅支持one-line文本
  2. 它允许您通过 ElidedLabel::setElideMode
  3. 设置省略号模式

文件如下:

ElidedLabel.h

#ifndef ELIDEDLABEL_H
#define ELIDEDLABEL_H

#include <QFrame>

class ElidedLabelPrivate;

class ElidedLabel : public QFrame
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
    Q_PROPERTY(QFlags<Qt::AlignmentFlag> alignment READ alignment
               WRITE setAlignment NOTIFY alignmentChanged)
    Q_PROPERTY(Qt::TextElideMode elideMode READ elideMode WRITE setElideMode
               NOTIFY elideModeChanged)
public:
    explicit ElidedLabel(QWidget *parent = nullptr);
    explicit ElidedLabel(const QString &text, QWidget *parent = nullptr);
    ~ElidedLabel();

    QString text() const;
    void setText(const QString &str);
    QFlags<Qt::AlignmentFlag> alignment() const;
    void setAlignment(QFlags<Qt::AlignmentFlag> flags);
    Qt::TextElideMode elideMode() const;
    void setElideMode(Qt::TextElideMode mode);

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    ElidedLabelPrivate *m_ptr;

signals:
    void textChanged();
    void alignmentChanged();
    void elideModeChanged();
};

#endif // ELIDEDLABEL_H

ElidedLabel_p.h

#ifndef ELIDEDLABEL_P_H
#define ELIDEDLABEL_P_H

#include <Qt>
#include <QString>

class ElidedLabel;

class ElidedLabelPrivate {

    Q_DISABLE_COPY(ElidedLabelPrivate)

    explicit ElidedLabelPrivate();

    QString text;
    QFlags<Qt::AlignmentFlag> alignment;
    Qt::TextElideMode elideMode;

    friend class ElidedLabel;
};

#endif // ELIDEDLABEL_P_H

ElidedLabel.cpp

#include "ElidedLabel.h"
#include "ElidedLabel_p.h"
#include <QPaintEvent>
#include <QPainter>

ElidedLabel::ElidedLabel(QWidget *parent) :
    QFrame(parent),
    m_ptr(new ElidedLabelPrivate)
{

}

ElidedLabel::ElidedLabel(const QString &text, QWidget *parent) :
    ElidedLabel(parent)
{
    m_ptr->text = text;
}

ElidedLabel::~ElidedLabel()
{
    delete m_ptr;
}

QString ElidedLabel::text() const
{
    return m_ptr->text;
}

void ElidedLabel::setText(const QString &str)
{
    m_ptr->text = str;

    update();

    emit textChanged();
}

QFlags<Qt::AlignmentFlag> ElidedLabel::alignment() const
{
    return m_ptr->alignment;
}

void ElidedLabel::setAlignment(QFlags<Qt::AlignmentFlag> flags)
{
    m_ptr->alignment = flags;

    update();

    emit alignmentChanged();
}

Qt::TextElideMode ElidedLabel::elideMode() const
{
    return m_ptr->elideMode;
}

void ElidedLabel::setElideMode(Qt::TextElideMode mode)
{
    m_ptr->elideMode = mode;

    update();

    emit elideModeChanged();
}

void ElidedLabel::paintEvent(QPaintEvent *event)
{
    QFrame::paintEvent(event);
    QPainter painter(this);

    painter.setPen(QPalette().windowText().color());
    painter.setClipRect(event->rect());
    painter.setFont(font());
    painter.drawText(contentsRect(), m_ptr->alignment | Qt::TextSingleLine,
                     painter.fontMetrics().elidedText(m_ptr->text,
                                                      m_ptr->elideMode,
                                                      contentsRect().width(),
                                                      1));
}

ElidedLabelPrivate::ElidedLabelPrivate() :
    alignment(Qt::AlignLeft | Qt::AlignVCenter),
    elideMode(Qt::ElideRight)
{

}

我知道,代码太多了,看起来可能很吓人。然而它的使用并不那么困难。只需将给定的文件添加到您的项目中,然后像使用任何其他小部件一样使用它。

这是一个例子main.cpp:

#include "ElidedLabel.h"
#include <QApplication>

int main(int argc, char *argv[])
{

    QApplication a(argc, argv);
    QFont f = QGuiApplication::font();

    f.setPointSize(11);

    QGuiApplication::setFont(f);

    ElidedLabel label;

    label.setText(QObject::tr("Hello Elided World! We have a very very very"
                              " long one-line text here."));
    label.setContentsMargins(10, 10, 10, 10);
    label.setElideMode(Qt::ElideMiddle);
    label.show();
    label.resize(200, 100);

    return a.exec();
}

这是@Chaitanyas 回答的 Python3 版本。

from PyQt5.QtCore import QRectF
from PyQt5.QtGui import QFontMetricsF

class PathLabel(QLabel):
def resizeEvent(self, *args, **kwargs):
    font_m = QFontMetricsF(self.font())
    text_rect = QRectF(font_m.boundingRect(self.text()))
    if text_rect.width() > self.width():  # use shorter text
        # just for test: remove ten characters
        self.setText(self.text()[:-10])
    else:  # possibly use longer text
        # check if a longer version would fit
        pass

稍后在主窗口中:

self.LbFolderName = PathLabel()

这是我现在终于使用的:

class PathLabel(QLabel):
    """Use setLongText instead of setText for a usual label"""

    def __init__(self, parent=None):
        super(PathLabel, self).__init__(parent)
        self.long_text = ""

    def make_short_text(self):
        # print("make_short_text called")
        """works fine but is not perfect for fnames as the middle is hidden.
        -> better hide the middle of the path but not long filenames"""
        font_m = QFontMetricsF(self.font())  # F or not ?
        avail_width = self.width() - 3  # - 3 px for a little space at the end
        short_text = font_m.elidedText(self.long_text, Qt.ElideMiddle, avail_width)
        return short_text

    def setLongText(self, text_in):
        # print("setLongText called")
        """Use this instead of setText for a usual label"""
        self.long_text = text_in
        self.setToolTip(text_in)  # tooltip shows the full text
        short_text = self.make_short_text()
        self.setText(short_text)

    def resizeEvent(self, *args, **kwargs):
        #print("resizeEvent called")
        short_text = self.make_short_text()
        self.setText(short_text)

创建标签后,

lb_folder = PathLabel("File not yet defined")

我打电话

lb_folder.setLongText(full_path_to_file)