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

Posted

技术标签:

【中文标题】如何检测 qt 标签(或任何小部件)是不是具有所需的所有空间?【英文标题】:How to detect if a qt label (or any widget) has all the space it would need?如何检测 qt 标签(或任何小部件)是否具有所需的所有空间? 【发布时间】:2021-01-05 10:58:12 【问题描述】:

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

这是一个例子:

    起点:label1.setText("This is a long long long long long long long long label") 显示“This is a long long long long long long long long long long label”并禁止缩小窗口。

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

    我希望显示“这是……长标签”或“这是一个长……长标签”,以适应标签的实际可用尺寸。

我们能否获取布局管理器希望为标签提供多大尺寸的信息?计算对应的字符数,将文本缩写,并设置新文本?

【问题讨论】:

看看Elided Label Example。 好的,我通读了,但对我来说似乎很复杂......也许我会尝试使用省略的 Qlabel(因为我的只有一行),(即使我更喜欢中间的点)。谢谢你的帮助。我什至不知道英文单词'to elide'...... 关于我什至不知道英文单词'to elide',现在只需要知道正确的关键字来查找信息。 :) 所以,不客气!至于复杂性,它是复杂的,但是你可能会学习这个类,把它放在你的项目中,然后照原样使用它。 我也会尝试让它按你想要的方式工作,敬请期待。 【参考方案1】:

您可以使用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()

【讨论】:

谢谢,这看起来比 Elided Label 示例要简单得多。我会尝试将其翻译成 Pyhton 并进行测试。 是的,它工作正常。非常感谢。我会将pyhton版本放在下面的答案中【参考方案2】:

使用 Elide 类。

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);

只要您想省略文本,就可以通过事件调用此函数。

【讨论】:

也感谢您的回答。由于它更短,对我来说比@Chaitanya 更难理解,但它给了我信息,你可以选择 eldide 模式为ElideMiddle。我认为最终这将是一个接近理想的快速解决方案,而无需处理缩短文本的细节。【参考方案3】:

有不同的方法来解决这个问题。创建自定义小部件就是其中之一。

所以,我根据您的要求修改了Elided Label Example

    仅支持单行文字 它可以让你通过ElidedLabel::setElideMode设置省略模式

这里是文件:

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();

【讨论】:

【参考方案4】:

这是@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)

【讨论】:

裁剪直到文本适合while text_rect.width() &gt; self.width(): # crop one by one until it fits self.setText(self.text()[:-1]) text_rect = QRectF(font_m.boundingRect(self.text()))

以上是关于如何检测 qt 标签(或任何小部件)是不是具有所需的所有空间?的主要内容,如果未能解决你的问题,请参考以下文章

qt3d窗口内的Qt小部件

如何在 Qt 中制作可展开/可折叠的部分小部件

QT:如何检查 qt 表格小部件中是不是有空单元格

具有重叠子小部件的 Qt 自定义小部件

Qt - 重新加载小部件内容

如何使 Qt 小部件淡入或淡出?