【写在前面】
本篇实际上是接上一篇的。
本来上一篇中在 QML 中使用 Canvas 实现了类似的效果,但是,不仅性能非常差,而且在我的电脑上偶尔还会崩溃,很烦,所以决定使用 QPainter 来实现这种雷达扫描的效果。
之前也提到过的在 QML 中使用 QPainter 的方法,因此这篇讲效果实现过程。
【正文开始】
首先,还是上效果图:
之前说过,继承 QQuickPaintedItem 通过 QPainter 在 C++ 中实现绘图,
scanneritem.h:
#ifndef SCANNERITEM_H
#define SCANNERITEM_H
#include <QQuickPaintedItem>
struct Point
{
QPoint point;
int alpha;
Point(const QPoint &p, int a) : point(p) , alpha(a) { }
};
class ScannerItem : public QQuickPaintedItem
{
Q_OBJECT
public:
ScannerItem(QQuickItem *parent = nullptr);
~ScannerItem();
Q_INVOKABLE void start();
protected:
virtual void paint(QPainter *painter);
private:
bool m_drawable = false;
int m_angle = 0;
QList<Point> m_points;
QTimer *m_updateTimer;
};
#endif // SCANNERITEM_H
这里的 Point 结构主要用来存储上图中的五个随机点的位置 ( QPoint ) 和透明度 ( alpha ),然后 ScannerItem 继承 QQuickPaintedItem 并实现 virtual void paint(QPainter *painter)。
接着来看其实现,scanneritem.cpp:
#include "scanneritem.h"
#include <QQuickWindow>
#include <QPainter>
#include <QTimer>
#include <QtMath>
ScannerItem::ScannerItem(QQuickItem *parent)
: QQuickPaintedItem (parent)
{
qsrand(uint (time(nullptr)));
m_updateTimer = new QTimer(this);
m_updateTimer->setInterval(16);
connect(m_updateTimer, &QTimer::timeout, this, [this](){ update(); });
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this]
{
m_points.clear();
for(int i = 0; i < 5; ++i)
{
int alpha = qrand() % 100 + 40;
int px = qrand() % int(width());
int py = qrand() % int(height());
m_points.append(Point(QPoint(px, py), alpha));
}
});
timer->start(1400);
}
ScannerItem::~ScannerItem()
{
}
void ScannerItem::start()
{
m_drawable = true;
m_angle = 0;
m_updateTimer->start();
}
void ScannerItem::paint(QPainter *painter)
{
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QPen(qRgba(120, 110, 250, 250)));
//格子
for(int i = 0; i < width(); i += 20)
painter->drawLine(i + 0.5, 0, i + 0.5, height());
for(int j = 0; j < height(); j += 20)
painter->drawLine(0, j + 0.5, width(), j + 0.5);
int min = int(qMin(width(), height()));
QPoint center(int(width() / 2), int(height() / 2));
painter->drawEllipse(center, min / 2, min / 2);
painter->drawEllipse(center, min / 3, min / 3);
painter->drawEllipse(center, min / 6, min / 6);
if (m_drawable)
{
int diff = int(qAbs(width() - height()) / 2);
QConicalGradient gradient(width() / 2, height() / 2, m_angle + 180);
gradient.setColorAt(0.1, QColor(15, 45, 188, 200));
gradient.setColorAt(0.7, QColor(15, 45, 188, 0));
painter->setBrush(gradient);
painter->setPen(QPen(Qt::NoPen));
if (width() > height())
painter->drawPie(diff, 0, min, min, m_angle * 16, 60 * 16);
else painter->drawPie(0, diff, min, min, m_angle * 16, 60 * 16);
for(int i = 0; i < 5; ++i)
{
painter->setBrush(QBrush(QColor(15, 45, 188, m_points.at(i).alpha)));
painter->drawEllipse(m_points.at(i).point, 7, 7);
}
m_angle -= 2;
}
}
★ 构造函数中先初始化随机数种子,然后这里有两个定时器,m_updateTimer 是控制 paint 更新的,间隔为16ms(60FPS),而 timer 是控制五个随机点的,1400ms 更换一次。
★ 而 start() 函数开始 update,重置扫描扇形的角度,并且设置 drawable 为 true,这里 start() 使用 Q_INVOKABLE 修饰,即可在 QML 中调用。
★ 然后是 paint():
1、使用 drawLine() 绘制网格。
2、使用 drawEllipse() 绘制三个圆,半径以宽高中较小的来度量。
3、使用 drawable: bool 控制扫描扇形是否绘制。
4、使用 drawPie() 绘制扇形,并且使用锥形渐变进行填充,这里需要注意的是使用 painter->setPen(QPen(Qt::NoPen)) 去掉扇形的外框线。
5、使用 drawEllipse() 绘制五个随机的圆点。
★ 将 ScannerItem 注册进 qml 中即可:
qmlRegisterType<ScannerItem>("an.item", 1, 0, "ScannerItem");
★ 最后,导入 qml 并使用:
.
.
.
import an.item 1.0
.
.
.
ScannerItem
{
id: scanner
clip: true
anchors.fill: parent
}
.
.
.
【结语】
本篇几乎可以看做是上一篇的c++实现 ( 上一篇使用 qml Canvas),当然效果有一些变化,不过性能却好了太多,所以还是觉得以后不用 Canvas 了〒▽〒。。
至于为什么不用 opengl (这也太大材小用了吧)。。
最后,资源地址(hhh赚点积分):QtQuick制作的文件传输器-C++文档类资源-CSDN下载
也可以访问项目地址(多多star呀..⭐_⭐):https://github.com/mengps/FileTransfer