功能描述
很多软件中会有类似下面这种的蓝色提示气泡,目的是为了引导或提示用户如何操作。
气泡上附有提示信息和一个可以点击的按钮。
下面绘制一个提示气泡,该气泡是提示用户第一步需要先点击导航栏上的“浏览器”图标。
跟随导航栏移动
实现“躲避屏幕边缘”机制,当导航栏位置到屏幕左侧的距离大于气泡长度,气泡显示在导航栏左侧,反之显示在右侧。
气泡显示时机
- 启动软件,不点击气泡上的按钮及提示用户操作的按钮
- 重新安装软件
气泡关闭时机
- 点击气泡上“不再提示”的按钮
- 点击提示用户操作的按钮
代码实现
.h文件
//三角形的高度
#define DEF_TRIANGLE_HEIGHT 8
//透明宽度
#define TRANSPARENT_LENGTH 10
enum class ENDIRECT {
DIRECT_LEFT = 1,
DIRECT_TOP,
DIRECT_RIGHT,
DIRECT_BOTTOM
};
class BubbleTipsWidget : public QWidget
{
Q_OBJECT
public:
explicit BubbleTipsWidget(QWidget *parent = nullptr);
void setBackColor(int r, int g, int b, int a = 255);
void setDirect(ENDIRECT direct);
void setContentFont(QFont font = {});
void setContent(const QString &content, QColor color = {});
bool isClickTips();
private slots:
void slotClickTips();
protected:
void paintEvent(QPaintEvent *event);
private:
QColor m_backColor;
ENDIRECT m_direct;
QLabel *m_pLabel;
QWidget *m_pParent;
QPushButton *m_pTipsButton;
QLockFile *m_pLockFile;
};
.cpp文件(绘制气泡)
#pragma region 提示气泡
/**
* @brief 气泡按钮样式
* @return void
*/
static void setBubbleButtonStyle(QPushButton *pTipsButton, QHBoxLayout *layout, QSize oButtonSize)
{
pTipsButton->setFixedSize(oButtonSize);
pTipsButton->setText(QStringLiteral("不再提示"));
//设置按钮样式、按下时的状态
pTipsButton->setStyleSheet("QPushButton{background-color:rgb(91, 181, 250);border-radius:2px;}"
"QPushButton{color:rgb(255, 255, 255);}"
"QPushButton:pressed{background-color:rgb(80, 170, 240);}");
QAction *oAct = new QAction();
QFont oButtonFont = oAct->font();
oButtonFont.setFamily("Microsoft YaHei");//设置字体为微软雅黑
oButtonFont.setPixelSize(14);
pTipsButton->setFont(oButtonFont);
layout->addWidget(pTipsButton);
}
BubbleTipsWidget::BubbleTipsWidget(QWidget *parent)
: QWidget(parent)
{
m_pParent = parent;
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);//窗口无边框效果
setAttribute(Qt::WA_TranslucentBackground);//窗口透明显示
//窗体尺寸
int nWidth = 190;
int nHeight = 40;
resize(nWidth, nHeight);
setMinimumSize(nWidth, nHeight);
QHBoxLayout *pMainLayout = new QHBoxLayout;
m_pLabel = new QLabel();
pMainLayout->addWidget(m_pLabel);
//“不在提示”按钮
m_pTipsButton = new QPushButton();
QSize oButtonSize(65, 20);//按钮尺寸
setBubbleButtonStyle(m_pTipsButton, pMainLayout, oButtonSize);
setLayout(pMainLayout);
//获得导航栏位置,定位气泡
QPoint oParentPoint = m_pParent->pos();
int oParentPointX = oParentPoint.x() - this->width();
int oParentPointY = oParentPoint.y() + TRANSPARENT_LENGTH;
this->move(oParentPointX, oParentPointY);
connect(m_pTipsButton, SIGNAL(clicked()), this, SLOT(slotClickTips()));
}
/**
* @brief 气泡背景色
* @return void
*/
void BubbleTipsWidget::setBackColor(int r, int g, int b, int a)
{
m_backColor = QColor(r, g, b, a);
}
/**
* @brief 气泡三角方向
* @return void
*/
void BubbleTipsWidget::setDirect(ENDIRECT direct)
{
m_direct = direct;
}
/**
* @brief 气泡提示信息字体
* @return void
*/
void BubbleTipsWidget::setContentFont(QFont font)
{
this->setFont(font);
}
/**
* @brief 气泡提示信息颜色
* @return void
*/
void BubbleTipsWidget::setContent(const QString &content, QColor color)
{
m_pLabel->setText(content);
m_pLabel->setStyleSheet(QString("color: rgb(%1, %2, %3)")
.arg(color.red())
.arg(color.green())
.arg(color.blue()));
}
/**
* @brief 通过是否生成lock.txt文件,判断用户是否点击过气泡上“不再提示”按钮或提示用户操作的按钮,用于显隐
* @brief lock.txt文件会跟随软件的拆卸而删除
* @return bool
*/
bool BubbleTipsWidget::isClickTips()
{
//获取程序当前运行目录
QString strFilePath = QCoreApplication::applicationDirPath();
QFileInfo oFile(strFilePath + "/lock.txt");
if (oFile.isFile())
{
return true;
}
return false;
}
/**
* @brief 点击过气泡上“不再提示”按钮,会生成一个lock.txt文件
* @return void
*/
void BubbleTipsWidget::slotClickTips()
{
//添加文件锁
QString strFilePath = QCoreApplication::applicationDirPath() + "/lock.txt";
QFile oFile(strFilePath);
if (!oFile.open(QFile::WriteOnly | QFile::Text))
{
return;
}
this->close();
}
/**
* @brief 重绘气泡
* @return void
*/
void BubbleTipsWidget::paintEvent(QPaintEvent *event)
{
int nBubbleWidth = width();
int nBubbleHeight = height();
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(m_backColor);
//相对于当前布局的起点坐标
painter.drawRoundedRect(DEF_TRIANGLE_HEIGHT, 0,
nBubbleWidth - DEF_TRIANGLE_HEIGHT * 2,
nBubbleHeight, 2, 2);
QPointF points[3];
switch (m_direct)
{
case ENDIRECT::DIRECT_LEFT:
{
points[0] = QPointF(0, nBubbleHeight * 0.5);
points[1] = QPointF(DEF_TRIANGLE_HEIGHT, nBubbleHeight * 0.3);
points[2] = QPointF(DEF_TRIANGLE_HEIGHT, nBubbleHeight * 0.7);
break;
}
case ENDIRECT::DIRECT_RIGHT:
{
points[0] = QPointF(nBubbleWidth, nBubbleHeight * 0.5);
points[1] = QPointF(nBubbleWidth - DEF_TRIANGLE_HEIGHT, nBubbleHeight * 0.3);
points[2] = QPointF(nBubbleWidth - DEF_TRIANGLE_HEIGHT, nBubbleHeight * 0.7);
break;
}
default:
break;
}
painter.drawPolygon(points, 3);
}
#pragma endregion
.cpp文件(功能逻辑实现)
ToolbarViewForm为导航栏窗体类
ToolbarViewForm::ToolbarViewForm(QWidget *parent) :
QDialog(parent)
{
bubbleTips();
connect(m_pBrowserPushButton, SIGNAL(clicked()), this, SLOT(slotClickBrowserPushButton()));
}
/**
* @brief 气泡提示框
* @return void
*/
void ToolbarViewForm::bubbleTips()
{
m_pBubbleWgt = new BubbleTipsWidget(this); //this为将导航栏窗体作为父窗体传入,主要用于获取导航栏的实时位置
if (m_pBubbleWgt->isClickTips())
{
return;
}
QFont oFont;
oFont.setFamily("Microsoft YaHei");
oFont.setPixelSize(14);
m_pBubbleWgt->setContentFont(oFont);
m_pBubbleWgt->setBackColor(0, 145, 255, 240);
m_pBubbleWgt->setDirect(ENDIRECT::DIRECT_RIGHT);
m_pBubbleWgt->setContent(QStringLiteral("请先打开浏览器"), QColor(Qt::white));
m_pBubbleWgt->show();
}
/**
* @brief 点击浏览器按钮响应的槽函数,点击按钮生成lock.txt文件,并关闭气泡
* @return void
*/
void ToolbarViewForm::slotClickBrowserPushButton()
{
//添加文件锁
QString strFilePath = QCoreApplication::applicationDirPath() + "/lock.txt";
QFile oFile(strFilePath);
if (!oFile.open(QFile::WriteOnly | QFile::Text))
{
return;
}
m_pBubbleWgt->close();
}
mousePressEvent,mouseMoveEvent为两个鼠标事件,实现导航栏随意拖动。
void ToolbarViewForm::mousePressEvent(QMouseEvent *event)
{
this->m_windowPos = this->pos(); // 获得部件当前位置
this->m_mousePos = event->globalPos(); // 获得鼠标位置
this->m_dPos = m_mousePos - m_windowPos; // 移动后部件所在的位置
}
void ToolbarViewForm::mouseMoveEvent(QMouseEvent *event)
{
this->move(event->globalPos() - this->m_dPos);
//气泡移动
moveBubbleWgt();
}
/**
* @brief 气泡跟随导航栏移动,并在满足条件时改变气泡方向
* @return void
*/
void ToolbarViewForm::moveBubbleWgt()
{
int nToolbarWgtWidth = this->width();
if (m_pBubbleWgt)
{
if (this->x() - m_pBubbleWgt->width() > 0)
{
m_pBubbleWgt->setDirect(ENDIRECT::DIRECT_RIGHT);
m_pBubbleWgt->move(this->x() - m_pBubbleWgt->width(), this->y() + TRANSPARENT_LENGTH);
}
else
{
m_pBubbleWgt->setDirect(ENDIRECT::DIRECT_LEFT);
m_pBubbleWgt->move(this->x() + nToolbarWgtWidth, this->y() + TRANSPARENT_LENGTH);
}
m_pBubbleWgt->update();
}
}