等待样式控件是我们在做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..100qreal _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 paintedpainter.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控件的一种设计方法和流程,在此操作的基础上我们可以发挥想象开发出更多更有意思的控件,源码我放在此处以下地址。如有错误也请各位看官手下留情,欢迎评论区批评指正。
谢谢你的关注和阅读,希望我的回答能帮到你。如果还有其他问题,欢迎随时向我提问。祝你一切顺利!