实现过程
因为 QToolTip 自定义样式不大方便,而且半透明也没法设置,所以需要自定义。而且,Qt 中的顶层 widget 好像默认是不支持透明样式的,可以设置:
1 2 | setWindowFlags(Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground, true); //无边框才有效 |
这样顶层窗口是透明了,但是样式表又没效果了。虽然可以用 QStyleOption 获取到样式表设置的颜色等信息,然后在 paintEvent 中绘制,但是图片我不知道怎么获取 。索性就嵌套了两个 widget ,给里层的 widget 设置样式。
显示和隐藏我是过滤的 QEvent::Enter 和 QEvent::Leave 来进行操作:
1 2 3 4 5 6 7 8 9 10 11 | 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
主要代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | #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); } |
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #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); } )"); } |