Qt自定义一个简单的ToolTip提示框

实现过程

因为 QToolTip 自定义样式不大方便,而且半透明也没法设置,所以需要自定义。而且,Qt 中的顶层 widget 好像默认是不支持透明样式的,可以设置:

    setWindowFlags(Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground, true); //无边框才有效

这样顶层窗口是透明了,但是样式表又没效果了。虽然可以用 QStyleOption 获取到样式表设置的颜色等信息,然后在 paintEvent 中绘制,但是图片我不知道怎么获取 。索性就嵌套了两个 widget ,给里层的 widget 设置样式。

显示和隐藏我是过滤的 QEvent::Enter 和  QEvent::Leave 来进行操作:

        switch (event->type()) {
        case QEvent::Enter:
            //showTip(QCursor::pos());
            showTip(targetWidget);
            break;
        case QEvent::Leave:
            hideTip();
            break;
        default:
            break;
        }

目前的实现是相对 widget 固定位置 show 的,没有处理鼠标移动事件。

弹出的时候因为我是先计算的位置再 show ,可能大小还没计算出来,所以在 resizeEvent 中重新调用了计算位置的函数。

(目前没有条件测试多屏幕时弹出的位置,先不写了)

对于设置样式表,目前只能通过 qApp 或者直接给实例对象设置。

参考 Qt 源码:E:\Qt\qt-everywhere-src-5.15.0\qtbase\src\widgets\kernel\qtooltip.h

实现代码

实现效果

Qt自定义一个简单的ToolTip提示框_第1张图片

代码链接

github 链接(RToolTip 类):https://github.com/gongjianbo/RectangleComponent.git

主要代码

#ifndef RTOOLTIP_H
#define RTOOLTIP_H

#include 
#include 

/**
 * @brief 最简易的ToolTip
 * @note 这是顶层窗口不要设置parent
 * @details 顶层设置透明后,样式表失效了,所以我在里面套了一层label
 * 本来想外层也用QLabel,show时内层label把属性设置为外层的,感觉没必要
 */
class RToolTip : public QWidget
{
    Q_OBJECT
    //READ WRITE可以替换成MEMBER
    //默认显示为point的左上角,通过属性设置偏移,以右下角为起点,左减右加,上减下加
    //qss右移1px:qproperty-rightOffset:1;
    Q_PROPERTY(int rightOffset READ getRightOffset WRITE setRightOffset)
    //qss上移1px:qproperty-bottomOffset:"-1";
    Q_PROPERTY(int bottomOffset READ getBottomOffset WRITE setBottomOffset)
    Q_PROPERTY(QString text READ getText WRITE setText)
    Q_PROPERTY(Qt::Alignment alignment READ getAlignment WRITE setAlignment)
public:
    //独立的窗口不设置parent,样式表可用qApp设置
    explicit RToolTip(const QString &objectName=QString(),const QString text=QString());
    ~RToolTip();

    //右侧偏移
    int getRightOffset();
    void setRightOffset(int offset);
    //底部偏移
    int getBottomOffset();
    void setBottomOffset(int offset);

    //文本
    QString getText() const;
    void setText(const QString &text);
    //对齐方式
    Qt::Alignment getAlignment() const;
    void setAlignment(Qt::Alignment alignment);

    //设置锚定窗口,鼠标放上去时显示tooltip
    void anchorTarget(QWidget *target);
    //获取内层label对象
    const QLabel *label() const;
    //显示tip在widget的左上角
    void showTip(const QWidget *obj);
    //显示tip在点的左上角
    void showTip(const QPoint &rightBottom);
    //隐藏tip
    void hideTip();

protected:
    //过滤锚定窗口的enter和leave事件
    bool eventFilter(QObject *target, QEvent *event) override;
    void timerEvent(QTimerEvent *event) override;
    void resizeEvent(QResizeEvent *event) override;

private:
    //外层设置背景透明后样式不生效,所以嵌套了一层
    QLabel *contentLabel;
    //默认显示为point的左上角,通过属性设置偏移
    //以右下角为起点,左减右加,上减下加
    int rightOffset = 0;
    int bottomOffset = 0;

    //锚定的窗口
    QWidget *targetWidget=nullptr;
    QPoint targetPoint;
    //show和hide延迟
    QBasicTimer showTimer;
    QBasicTimer hideTimer;
};

#endif // RTOOLTIP_H
#include "RToolTip.h"

#include 
#include 
#include 
#include 
#include 
//#include 
#include 
//#include 

RToolTip::RToolTip(const QString &objectName, const QString text)
    : QWidget(nullptr)
    ,contentLabel(new QLabel(this))
{
    //把内层label添加到透明widget
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(contentLabel);

    contentLabel->setAlignment(Qt::AlignCenter);
    contentLabel->setText(text);

    setObjectName(objectName);

    //把widget设置为透明,样式表设置给label
    setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip);
    setAttribute(Qt::WA_TranslucentBackground, true);
    //默认hide
    setVisible(false);

    //qDebug()<<"new tooltip"<unpolish(this);
        //style()->polish(this);
    }
}

int RToolTip::getBottomOffset()
{
    return bottomOffset;
}

void RToolTip::setBottomOffset(int offset)
{
    if(bottomOffset!=offset){
        bottomOffset=offset;
    }
}

QString RToolTip::getText() const
{
    return contentLabel->text();
}

void RToolTip::setText(const QString &text)
{
    contentLabel->setText(text);
}

Qt::Alignment RToolTip::getAlignment() const
{
    return contentLabel->alignment();
}

void RToolTip::setAlignment(Qt::Alignment alignment)
{
    contentLabel->setAlignment(alignment);
}

void RToolTip::anchorTarget(QWidget *target)
{
    if(target&&target!=targetWidget){
        if(targetWidget){
            targetWidget->removeEventFilter(this);
        }
        targetWidget=target;
        targetWidget->installEventFilter(this);
        targetWidget->setMouseTracking(true);
        //如果是随窗口关闭的,看不到析构的打印,难道此时事件循环已停止?
        connect(targetWidget,&QObject::destroyed,this,&QObject::deleteLater);
    }
}

const QLabel *RToolTip::label() const
{
    return contentLabel;
}

void RToolTip::showTip(const QWidget *obj)
{
    if(!obj)
        return;
    showTip(obj->mapToGlobal(QPoint(0, 0)));
}

void RToolTip::showTip(const QPoint &rightBottom)
{
    targetPoint=rightBottom;
    //move(rightBottom.x() - width() + rightOffset,
    //     rightBottom.y() - height() + bottomOffset);
    //直接用size+point得到的位置可能显示不全,这里计算下

    int rect_left=rightBottom.x()-width()+rightOffset;
    int rect_top=rightBottom.y()-height()+bottomOffset;
    if(rect_left<0)
        rect_left=0;
    if(rect_top<0)
        rect_top=0;
    //要考虑多个显示器情况,【待测试】
    //根据当前所在屏幕尺寸计算
    QDesktopWidget * desktop=QApplication::desktop();
    if(desktop){
        QRect desk_rect=desktop->screenGeometry(targetWidget?targetWidget:this);
        if(rect_left+width()>desk_rect.width())
            rect_left=desk_rect.width()-width();
        if(rect_top+height()>desk_rect.height())
            rect_top=desk_rect.height()-height();
    }

    move(rect_left,rect_top);
    if(!showTimer.isActive())
        showTimer.start(200,this);
}

void RToolTip::hideTip()
{
    if(!hideTimer.isActive())
        hideTimer.start(300,this);
}

bool RToolTip::eventFilter(QObject *target, QEvent *event)
{
    if(target==targetWidget){
        switch (event->type()) {
        case QEvent::Enter:
            //showTip(QCursor::pos());
            showTip(targetWidget);
            break;
        case QEvent::Leave:
            hideTip();
            break;
        default:
            break;
        }
    }
    return QWidget::eventFilter(target,event);
}

void RToolTip::timerEvent(QTimerEvent *event)
{
    if(event->timerId()==showTimer.timerId()) {
        showTimer.stop();
        //hideTimer.stop();
        if(!hideTimer.isActive()&&isHidden()){
            show();
        }
    }else if(event->timerId()==hideTimer.timerId()){
        showTimer.stop();
        hideTimer.stop();
        if(!isHidden()){
            hide();
        }
    }else{
        QWidget::timerEvent(event);
    }
}

void RToolTip::resizeEvent(QResizeEvent *event)
{
    //初次show的时候可能size可能还没计算好
    showTip(targetPoint);
    QWidget::resizeEvent(event);
}

使用

#include "RToolTip.h"

#include 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    initAppStyleSheet();
    /*配合样式表设置tab背景*/
    ui->tabWidget->setAttribute(Qt::WA_StyledBackground);

    RToolTip *tipA=new RToolTip("tipA","第1个tip");
    tipA->anchorTarget(ui->btnToolTipA);
    connect(ui->btnToolTipA,&QPushButton::clicked,[=]{
        if(ui->btnToolTipB){
            ui->btnToolTipB->deleteLater();
            ui->btnToolTipB=nullptr;
        }
    });

    RToolTip *tipB=new RToolTip("tipB","第2个tip");
    tipB->anchorTarget(ui->btnToolTipB);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::initAppStyleSheet()
{
    qApp->setStyleSheet(R"(
                        .RToolTip#tipA{
                        min-width:100px;
                        max-width:100px;
                        min-height:30px;
                        max-height:30px;
                        }
                        .RToolTip#tipB{
                        qproperty-rightOffset:"20";
                        qproperty-bottomOffset:"3";
                        }
                        .RToolTip QLabel{
                        padding:10px 50px;
                        color:white;
                        border: 1px solid white;
                        background-color:rgb(20,50,90);
                        }
                        .RToolTip#tipA QLabel{
                        padding:0;
                        border-radius:5px;
                        background-color:rgba(250,170,0,150);
                        }
                        )");
}

 

 

你可能感兴趣的:(Qt,略知一二)