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