当前位置:   article > 正文

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

qt tooltip

实现细节

因为 QToolTip 自定义样式不大方便,而且半透明也没法设置,所以需要自定义。

Qt 中的顶层 QWidget 好像默认是不支持透明样式的,可以设置:

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

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

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

  1. switch (event->type()) {
  2. case QEvent::Enter:
  3. //showTip(QCursor::pos());
  4. showTip(targetWidget);
  5. break;
  6. case QEvent::Leave:
  7. hideTip();
  8. break;
  9. default:
  10. break;
  11. }

目前的实现是相对一个 widget 固定位置 show 的,如锚定一个按钮的位置,但没有处理鼠标移动事件,只处理 enter 和 leave。

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

最初我是外层用 QWidget,但是设置 parent 后不能独立窗口显示,不设置 parent 设置样式表和 delete 又麻烦,所以改成了 QDialog,然后内部放一个 QLabel 显示文字。

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

实现代码

实现效果

代码链接

github 链接(CuteToolTip 类):https://github.com/gongjianbo/QtWidgetsComponent

主要代码

  1. #pragma once
  2. #include <QDialog>
  3. #include <QLabel>
  4. #include <QBasicTimer>
  5. #include "CuteComponentExport.h"
  6. /**
  7. * @brief 最简易的 ToolTip 提示框
  8. * @author 龚建波
  9. * @date 2020-6-7
  10. * @details
  11. * 外部为 QDialog,设置 parent 后也能独立窗口显示
  12. * 内部套一个 QLabel 显示文字
  13. */
  14. class Cute_API CuteToolTip : public QDialog
  15. {
  16. Q_OBJECT
  17. // 默认显示为 point 的左上角,通过属性设置偏移,左-右+,上-下+,同屏幕坐标系规则
  18. // qss 右移 1px: qproperty-xOffset:"1";
  19. Q_PROPERTY(int xOffset READ getXOffset WRITE setXOffset NOTIFY xOffsetChanged)
  20. // qss 上移 1px: qproperty-yOffset:"-1";
  21. Q_PROPERTY(int yOffset READ getYOffset WRITE setYOffset NOTIFY yOffsetChanged)
  22. Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
  23. Q_PROPERTY(Qt::Alignment alignment READ getAlignment WRITE setAlignment NOTIFY alignmentChanged)
  24. public:
  25. explicit CuteToolTip(const QString text = QString(), QWidget *parent = nullptr);
  26. ~CuteToolTip();
  27. // x 轴偏移
  28. int getXOffset() const;
  29. void setXOffset(int offset);
  30. // y 轴偏移
  31. int getYOffset() const;
  32. void setYOffset(int offset);
  33. // 文本
  34. QString getText() const;
  35. void setText(const QString &text);
  36. // 对齐方式
  37. Qt::Alignment getAlignment() const;
  38. void setAlignment(Qt::Alignment alignment);
  39. // 设置锚定窗口,鼠标放上去时显示 tooltip
  40. void anchorTarget(QWidget *target);
  41. // 获取内层 label 对象
  42. const QLabel *label() const;
  43. // 显示 tip 在 widget 的左上角
  44. void showTip(const QWidget *obj);
  45. // 显示 tip 在点的左上角
  46. void showTip(const QPoint &rightBottom);
  47. // 隐藏 tip
  48. void hideTip();
  49. protected:
  50. // 过滤锚定窗口的 enter 和 leave 事件
  51. bool eventFilter(QObject *target, QEvent *event) override;
  52. void timerEvent(QTimerEvent *event) override;
  53. void resizeEvent(QResizeEvent *event) override;
  54. signals:
  55. void xOffsetChanged();
  56. void yOffsetChanged();
  57. void textChanged();
  58. void alignmentChanged();
  59. private:
  60. // 内部套一个 QLabel 显示文字
  61. QLabel *contentLabel{ nullptr };
  62. // 默认显示为 point 的左上角,通过属性设置偏移
  63. // 左-右+,上-下+,同屏幕坐标系规则
  64. int xOffset{ 0 };
  65. int yOffset{ 0 };
  66. // 锚定的窗口
  67. QWidget *targetWidget{ nullptr };
  68. QPoint targetPoint;
  69. // show 和 hide 延迟
  70. QBasicTimer showTimer;
  71. QBasicTimer hideTimer;
  72. };
  1. #include "CuteToolTip.h"
  2. #include <QGuiApplication>
  3. #include <QScreen>
  4. #include <QEvent>
  5. #include <QTimerEvent>
  6. #include <QResizeEvent>
  7. #include <QHBoxLayout>
  8. //#include <QDebug>
  9. CuteToolTip::CuteToolTip(const QString text, QWidget *parent)
  10. : QDialog{parent}
  11. , contentLabel{new QLabel(this)}
  12. {
  13. // 把内层 label 添加到透明 dialog
  14. QHBoxLayout *layout = new QHBoxLayout(this);
  15. layout->setContentsMargins(0, 0, 0, 0);
  16. layout->setSpacing(0);
  17. layout->addWidget(contentLabel);
  18. contentLabel->setAlignment(Qt::AlignCenter);
  19. contentLabel->setText(text);
  20. // 把 dialog 设置为透明,样式表设置给 label
  21. setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip);
  22. setAttribute(Qt::WA_TranslucentBackground, true);
  23. // 默认 hide
  24. setVisible(false);
  25. // qDebug() << __FUNCTION__;
  26. }
  27. CuteToolTip::~CuteToolTip()
  28. {
  29. // qDebug() << __FUNCTION__;
  30. }
  31. int CuteToolTip::getXOffset() const
  32. {
  33. return xOffset;
  34. }
  35. void CuteToolTip::setXOffset(int offset)
  36. {
  37. if (xOffset != offset){
  38. xOffset = offset;
  39. // 没有动态样式的处理,可自行添加
  40. // style()->unpolish(this);
  41. // style()->polish(this);
  42. emit xOffsetChanged();
  43. }
  44. }
  45. int CuteToolTip::getYOffset() const
  46. {
  47. return yOffset;
  48. }
  49. void CuteToolTip::setYOffset(int offset)
  50. {
  51. if (yOffset != offset){
  52. yOffset = offset;
  53. emit yOffsetChanged();
  54. }
  55. }
  56. QString CuteToolTip::getText() const
  57. {
  58. return contentLabel->text();
  59. }
  60. void CuteToolTip::setText(const QString &text)
  61. {
  62. if (getText() != text) {
  63. contentLabel->setText(text);
  64. emit textChanged();
  65. }
  66. }
  67. Qt::Alignment CuteToolTip::getAlignment() const
  68. {
  69. return contentLabel->alignment();
  70. }
  71. void CuteToolTip::setAlignment(Qt::Alignment alignment)
  72. {
  73. if (getAlignment() != alignment) {
  74. contentLabel->setAlignment(alignment);
  75. emit alignmentChanged();
  76. }
  77. }
  78. void CuteToolTip::anchorTarget(QWidget *target)
  79. {
  80. if (!target) return;
  81. if (target != targetWidget) {
  82. if (targetWidget) {
  83. targetWidget->removeEventFilter(this);
  84. }
  85. targetWidget = target;
  86. targetWidget->installEventFilter(this);
  87. targetWidget->setMouseTracking(true);
  88. }
  89. }
  90. const QLabel *CuteToolTip::label() const
  91. {
  92. return contentLabel;
  93. }
  94. void CuteToolTip::showTip(const QWidget *obj)
  95. {
  96. if (!obj) return;
  97. showTip(obj->mapToGlobal(QPoint(0, 0)));
  98. }
  99. void CuteToolTip::showTip(const QPoint &rightBottom)
  100. {
  101. targetPoint = rightBottom;
  102. // move(rightBottom.x() - width() + xOffset,
  103. // rightBottom.y() - height() + yOffset);
  104. // 直接用size + point得到的位置可能显示不全,这里计算下
  105. int rect_left = rightBottom.x() - width() + xOffset;
  106. int rect_top = rightBottom.y() - height() + yOffset;
  107. // 根据当前所在屏幕尺寸计算,左上角不超过范围
  108. // 因为锚定的左上角显示,右下角暂不考虑
  109. // 老版本用 QDesktopWidget
  110. // QDesktopWidget *desktop = QApplication::desktop();
  111. // QRect desk_rect=desktop->screenGeometry(targetWidget);
  112. // Qt5.11 QDesktopWidget 部分接口被废弃,Qt6 该类被移除
  113. // 新版本用 QScreen
  114. // 直接用 widget 的 screen() 获取的是整个 window 所在 screen,而不是组件的
  115. // 跨屏时在哪个屏幕占的区域最大,就以该屏幕作为组件的所属 screen
  116. QScreen *screen = nullptr;
  117. if (targetWidget) {
  118. screen = QGuiApplication::screenAt(targetWidget->mapToGlobal(targetWidget->rect().center()));
  119. }
  120. if (screen) {
  121. QRect desk_rect = screen->geometry();
  122. if(rect_left < desk_rect.x())
  123. rect_left = desk_rect.x();
  124. if(rect_top < desk_rect.y())
  125. rect_top = desk_rect.y();
  126. } else {
  127. if (rect_left < 0)
  128. rect_left = 0;
  129. if (rect_top < 0)
  130. rect_top = 0;
  131. }
  132. move(rect_left, rect_top);
  133. if (!showTimer.isActive())
  134. showTimer.start(200, this);
  135. }
  136. void CuteToolTip::hideTip()
  137. {
  138. if (!hideTimer.isActive())
  139. hideTimer.start(300, this);
  140. }
  141. bool CuteToolTip::eventFilter(QObject *target, QEvent *event)
  142. {
  143. if (target == targetWidget) {
  144. switch (event->type()) {
  145. case QEvent::Enter:
  146. // showTip(QCursor::pos());
  147. showTip(targetWidget);
  148. break;
  149. case QEvent::Leave:
  150. hideTip();
  151. break;
  152. default:
  153. break;
  154. }
  155. }
  156. return QWidget::eventFilter(target, event);
  157. }
  158. void CuteToolTip::timerEvent(QTimerEvent *event)
  159. {
  160. if (event->timerId() == showTimer.timerId()) {
  161. showTimer.stop();
  162. // hideTimer.stop();
  163. if (!hideTimer.isActive() && isHidden()) {
  164. show();
  165. }
  166. } else if (event->timerId() == hideTimer.timerId()) {
  167. showTimer.stop();
  168. hideTimer.stop();
  169. if (!isHidden()) {
  170. hide();
  171. }
  172. } else {
  173. QWidget::timerEvent(event);
  174. }
  175. }
  176. void CuteToolTip::resizeEvent(QResizeEvent *event)
  177. {
  178. // 初次 show 的时候可能 size 可能还没计算好
  179. showTip(targetPoint);
  180. QWidget::resizeEvent(event);
  181. }

使用

  1. void ToolTipDemo::initToolTip()
  2. {
  3. CuteToolTip *tip_a = new CuteToolTip("Tip A", ui->btnToolTipA);
  4. // 通过 objectName 区分样式表设置
  5. tip_a->setObjectName("tipA");
  6. tip_a->anchorTarget(ui->btnToolTipA);
  7. // 样式表设置为固定尺寸 tip
  8. tip_a->setStyleSheet(R"(
  9. .CuteToolTip#tipA{
  10. min-width:100px;
  11. max-width:100px;
  12. min-height:30px;
  13. max-height:30px;
  14. }
  15. .CuteToolTip#tipA QLabel{
  16. color:white;
  17. border:1px solid white;
  18. padding:0;
  19. border-radius:5px;
  20. background-color:rgba(250,170,0,150);
  21. }
  22. )");
  23. CuteToolTip *tip_b = new CuteToolTip("Tip B", ui->btnToolTipB);
  24. tip_b->setObjectName("tipB");
  25. tip_b->anchorTarget(ui->btnToolTipB);
  26. // 自适应的尺寸
  27. tip_b->setStyleSheet(R"(
  28. .CuteToolTip#tipB{
  29. qproperty-xOffset:"20";
  30. qproperty-yOffset:"3";
  31. }
  32. .CuteToolTip#tipB QLabel{
  33. padding:10px 30px;
  34. color:white;
  35. border:1px solid white;
  36. background-color:rgb(20,50,90);
  37. }
  38. )");
  39. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/806012
推荐阅读
相关标签
  

闽ICP备14008679号