等待样式控件是我们在做UI时出场率还挺高的控件之一,通常情况下有如下的几种实现方式:1、自定义绘图,然后重写paintEvent函数,在paintEvent中绘制等待图标,通过QTimer更新绘制达到转圈圈的效果。2、 获取一张gif的资源图,然后使用QMovie 在一个QLabel 控件上加载显示gif的waiting等待动态。
本示例采用自定义绘图,然后使用Qt动画,达到转圈圈的效果,给大家一个好看的样式示例。你可以根据需要进行修改和扩展,实现你想要的程序启动等待提示栏效果。
想看利用QMovie实现的请移步:https://blog.csdn.net/u012959478/article/details/140441021
一、简述
Qt纯代码绘制一个等待提示Ui控件。自定义绘图,然后重写paintEvent函数,在paintEvent中绘制等待图标,通过QTimer更新绘制达到转圈圈的效果。
二、 设计思路
- 创建一个QWidget派生的自定义控件,用于显示等待提示。
- 在自定义控件中,绘制一个圆形的等待图标。
- 通过重写paintEvent函数,在paintEvent中绘制等待图标。
- 利用定时器QTimer并实现一个槽函数来更新图标。
- 在槽函数中调用update函数,触发重绘事件,更新控件的显示。
三、效果
四、核心代码
1、头文件
#pragma once
#include <QWidget>
#include <QTimer>
#include <QColor>
class WaitingSpinnerWidget : public QWidget {
Q_OBJECT
public:
WaitingSpinnerWidget(QWidget *parent = 0,
bool centerOnParent = true,
bool disableParentWhenSpinning = true);
WaitingSpinnerWidget(Qt::WindowModality modality,
QWidget *parent = 0,
bool centerOnParent = true,
bool disableParentWhenSpinning = true);
public slots:
void start();
void stop();
public:
void setColor(QColor color);
void setRoundness(qreal roundness);//圆角
void setMinimumTrailOpacity(qreal minimumTrailOpacity);//最小轨迹不透明度
void setTrailFadePercentage(qreal trail);//轨迹褪色百分比
void setRevolutionsPerSecond(qreal revolutionsPerSecond);//每秒钟转数
void setNumberOfLines(int lines);//线条数量
void setLineLength(int length);//线条长度
void setLineWidth(int width);//线条宽度
void setInnerRadius(int radius);//内圆半径
QColor color();
qreal roundness();
qreal minimumTrailOpacity();
qreal trailFadePercentage();
qreal revolutionsPersSecond();
int numberOfLines();
int lineLength();
int lineWidth();
int innerRadius();
bool isSpinning() const;
private slots:
void rotate();
protected:
void paintEvent(QPaintEvent *paintEvent);
private:
static int lineCountDistanceFromPrimary(int current, int primary,
int totalNrOfLines);
static QColor currentLineColor(int distance, int totalNrOfLines,
qreal trailFadePerc, qreal minOpacity,
QColor color);
void initialize();
void updateSize();
void updateTimer();
void updatePosition();
private:
QColor _color;
qreal _roundness; // 0..100
qreal _minimumTrailOpacity;
qreal _trailFadePercentage;
qreal _revolutionsPerSecond;
int _numberOfLines;
int _lineLength;
int _lineWidth;
int _innerRadius;
private:
WaitingSpinnerWidget(const WaitingSpinnerWidget&);
WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&);
QTimer *_timer;
bool _centerOnParent;
bool _disableParentWhenSpinning;
int _currentCounter;
bool _isSpinning;
};
2、实现代码
#include "waitingspinnerwidget.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QTimer>
WaitingSpinnerWidget::WaitingSpinnerWidget(QWidget *parent,
bool centerOnParent,
bool disableParentWhenSpinning)
: QWidget(parent),
_centerOnParent(centerOnParent),
_disableParentWhenSpinning(disableParentWhenSpinning) {
initialize();
}
WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality,
QWidget *parent,
bool centerOnParent,
bool disableParentWhenSpinning)
: QWidget(parent, Qt::Dialog | Qt::FramelessWindowHint),
_centerOnParent(centerOnParent),
_disableParentWhenSpinning(disableParentWhenSpinning){
initialize();
// We need to set the window modality AFTER we've hidden the
// widget for the first time since changing this property while
// the widget is visible has no effect.
setWindowModality(modality);
setAttribute(Qt::WA_TranslucentBackground);
}
void WaitingSpinnerWidget::initialize() {
_color = Qt::black;
_roundness = 100.0;
_minimumTrailOpacity = 3.14159265358979323846;
_trailFadePercentage = 80.0;
_revolutionsPerSecond = 1.57079632679489661923;
_numberOfLines = 20;
_lineLength = 10;
_lineWidth = 2;
_innerRadius = 10;
_currentCounter = 0;
_isSpinning = false;
_timer = new QTimer(this);
connect(_timer, SIGNAL(timeout()), this, SLOT(rotate()));
updateSize();
updateTimer();
hide();
}
void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
updatePosition();
QPainter painter(this);
painter.fillRect(this->rect(), Qt::transparent);
painter.setRenderHint(QPainter::Antialiasing, true);
if (_currentCounter >= _numberOfLines) {
_currentCounter = 0;
}
painter.setPen(Qt::NoPen);
for (int i = 0; i < _numberOfLines; ++i) {
painter.save();
painter.translate(_innerRadius + _lineLength,
_innerRadius + _lineLength);
qreal rotateAngle =
static_cast<qreal>(360 * i) / static_cast<qreal>(_numberOfLines);
painter.rotate(rotateAngle);
painter.translate(_innerRadius, 0);
int distance =
lineCountDistanceFromPrimary(i, _currentCounter, _numberOfLines);
QColor color =
currentLineColor(distance, _numberOfLines, _trailFadePercentage,
_minimumTrailOpacity, _color);
painter.setBrush(color);
// TODO improve the way rounded rect is painted
painter.drawRoundedRect(
QRect(0, -_lineWidth / 2, _lineLength, _lineWidth), _roundness,
_roundness, Qt::RelativeSize);
painter.restore();
}
}
void WaitingSpinnerWidget::start() {
updatePosition();
_isSpinning = true;
show();
if(parentWidget() && _disableParentWhenSpinning) {
parentWidget()->setEnabled(false);
}
if (!_timer->isActive()) {
_timer->start();
_currentCounter = 0;
}
}
void WaitingSpinnerWidget::stop() {
_isSpinning = false;
hide();
if(parentWidget() && _disableParentWhenSpinning) {
parentWidget()->setEnabled(true);
}
if (_timer->isActive()) {
_timer->stop();
_currentCounter = 0;
}
}
void WaitingSpinnerWidget::setNumberOfLines(int lines) {
_numberOfLines = lines;
_currentCounter = 0;
updateTimer();
}
void WaitingSpinnerWidget::setLineLength(int length) {
_lineLength = length;
updateSize();
}
void WaitingSpinnerWidget::setLineWidth(int width) {
_lineWidth = width;
updateSize();
}
void WaitingSpinnerWidget::setInnerRadius(int radius) {
_innerRadius = radius;
updateSize();
}
QColor WaitingSpinnerWidget::color() {
return _color;
}
qreal WaitingSpinnerWidget::roundness() {
return _roundness;
}
qreal WaitingSpinnerWidget::minimumTrailOpacity() {
return _minimumTrailOpacity;
}
qreal WaitingSpinnerWidget::trailFadePercentage() {
return _trailFadePercentage;
}
qreal WaitingSpinnerWidget::revolutionsPersSecond() {
return _revolutionsPerSecond;
}
int WaitingSpinnerWidget::numberOfLines() {
return _numberOfLines;
}
int WaitingSpinnerWidget::lineLength() {
return _lineLength;
}
int WaitingSpinnerWidget::lineWidth() {
return _lineWidth;
}
int WaitingSpinnerWidget::innerRadius() {
return _innerRadius;
}
bool WaitingSpinnerWidget::isSpinning() const {
return _isSpinning;
}
void WaitingSpinnerWidget::setRoundness(qreal roundness) {
_roundness = std::max(0.0, std::min(100.0, roundness));
}
void WaitingSpinnerWidget::setColor(QColor color) {
_color = color;
}
void WaitingSpinnerWidget::setRevolutionsPerSecond(qreal revolutionsPerSecond) {
_revolutionsPerSecond = revolutionsPerSecond;
updateTimer();
}
void WaitingSpinnerWidget::setTrailFadePercentage(qreal trail) {
_trailFadePercentage = trail;
}
void WaitingSpinnerWidget::setMinimumTrailOpacity(qreal minimumTrailOpacity) {
_minimumTrailOpacity = minimumTrailOpacity;
}
void WaitingSpinnerWidget::rotate() {
++_currentCounter;
if (_currentCounter >= _numberOfLines) {
_currentCounter = 0;
}
update();
}
void WaitingSpinnerWidget::updateSize() {
int size = (_innerRadius + _lineLength) * 2;
setFixedSize(size, size);
}
void WaitingSpinnerWidget::updateTimer() {
_timer->setInterval(1000 / (_numberOfLines * _revolutionsPerSecond));
}
void WaitingSpinnerWidget::updatePosition() {
if (parentWidget() && _centerOnParent) {
move(parentWidget()->width() / 2 - width() / 2,
parentWidget()->height() / 2 - height() / 2);
}
}
int WaitingSpinnerWidget::lineCountDistanceFromPrimary(int current, int primary,
int totalNrOfLines) {
int distance = primary - current;
if (distance < 0) {
distance += totalNrOfLines;
}
return distance;
}
QColor WaitingSpinnerWidget::currentLineColor(int countDistance, int totalNrOfLines,
qreal trailFadePerc, qreal minOpacity,
QColor color) {
if (countDistance == 0) {
return color;
}
const qreal minAlphaF = minOpacity / 100.0;
int distanceThreshold =
static_cast<int>(ceil((totalNrOfLines - 1) * trailFadePerc / 100.0));
if (countDistance > distanceThreshold) {
color.setAlphaF(minAlphaF);
} else {
qreal alphaDiff = color.alphaF() - minAlphaF;
qreal gradient = alphaDiff / static_cast<qreal>(distanceThreshold + 1);
qreal resultAlpha = color.alphaF() - gradient * countDistance;
// If alpha is out of bounds, clip it.
resultAlpha = std::min(1.0, std::max(0.0, resultAlpha));
color.setAlphaF(resultAlpha);
}
return color;
}
以上是等待提示UI控件的实现代码,大家可以根据不同的应用场景和用户需求进行扩展。
需要注意的是,等待提示UI控件应该在适当的时机显示,并且要避免过长时间的等待,以免影响用户体验。同时,等待提示UI控件的样式应该简洁明了,清晰展示当前操作的状态,以便用户能够理解和接受。
五、使用示例
以下是一个简单的示例代码,演示了如何在Qt中使用此控件:
#include <QCoreApplication>
#include <QApplication>
#include <waitingspinnerwidget.h>
#include <QFrame>
#include <QHBoxLayout>
int main(int argc,char* argv[])
{
QApplication a(argc,argv);
WaitingSpinnerWidget* spinner = new WaitingSpinnerWidget;
/// 设置waiting组件的样式
spinner->setRoundness(50.0);
spinner->setMinimumTrailOpacity(15.0);
spinner->setTrailFadePercentage(70.0);
spinner->setNumberOfLines(16);
spinner->setLineLength(15);
spinner->setLineWidth(5);
spinner->setInnerRadius(30);
spinner->setRevolutionsPerSecond(1);
spinner->setColor(QColor(81, 4, 71));
spinner->start(); // gets the show on the road!
QFrame* f = new QFrame;
QHBoxLayout* hlayout = new QHBoxLayout;
hlayout->addWidget(spinner);
f->setLayout(hlayout);
f->show();
return a.exec();
}
总结一下,笔者分享纯代码实现等待提示UI控件的一种设计方法和流程,在此操作的基础上我们可以发挥想象开发出更多更有意思的控件,源码我放在此处以下地址。如有错误也请各位看官手下留情,欢迎评论区批评指正。
谢谢你的关注和阅读,希望我的回答能帮到你。如果还有其他问题,欢迎随时向我提问。祝你一切顺利!