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

實現代碼

實現效果

代碼鏈接

github 鏈接(RToolTip 類):https://github.com/gongjianbo/RectangleComponent.git

主要代碼

#ifndef RTOOLTIP_H
#define RTOOLTIP_H

#include <QLabel>
#include <QBasicTimer>

/**
 * @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 <QApplication>
#include <QDesktopWidget>
#include <QEvent>
#include <QTimerEvent>
#include <QResizeEvent>
//#include <QStyle>
#include <QHBoxLayout>
//#include <QDebug>

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"<<objectName;
}

RToolTip::~RToolTip()
{
    //qDebug()<<"delete tooltip"<<objectName();
}

int RToolTip::getRightOffset()
{
    return rightOffset;
}

void RToolTip::setRightOffset(int offset)
{
    if(rightOffset!=offset){
        rightOffset=offset;
        //沒有動態樣式的處理,可自行添加
        //style()->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 <QDebug>

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

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章