1. 程式人生 > >Qt總結之七:QPaintEvent繪製雷達圖(二)

Qt總結之七:QPaintEvent繪製雷達圖(二)

前言

這裡使用另一套框架寫的雷達掃描圖

這裡PaintEvent事件比上一個版本寫的好,但是不喜歡Widget巢狀的方式,後續會把兩個版本整合到一起。

 一、實現原理

雷達效果包括三個部分:背景、轉動的扇形和閃爍的點。 
(1)背景的實現很簡單,首先填充背景色,然後繪製橫縱座標軸以及三個同心圓。程式碼如下:

//背景
    painter.fillRect(rect(),QColor(15,45,188));

    //邊長
    int len = m_drawArea.width();

    //底盤(x軸、y軸和3個圓)
    painter.setPen(QPen(Qt::white));
    painter.drawLine(m_drawArea.topLeft() + QPoint(0,len/2),m_drawArea.topRight() + QPoint(0,len/2));
    painter.drawLine(m_drawArea.topLeft() + QPoint(len/2,0),m_drawArea.bottomLeft() + QPoint(len/2,0));
    painter.drawEllipse(m_drawArea.center(),len/2,len/2);
    painter.drawEllipse(m_drawArea.center(),len/3,len/3);
    painter.drawEllipse(m_drawArea.center(),len/6,len/6);

在這裡有個m_drawArea是用來描述繪製雷達的區域的。先確定這個繪製區域,更容易計算繪製的內容。 m_drawArea的大小在resizeEvent事件中確定。

void CRadar::resizeEvent(QResizeEvent *event)
{
    //以較短的邊長作為繪製區域邊長
    if(width() > height())
    {
        m_drawArea = QRect((width() - height())/2,0,height(),height());
    }
    else
    {
        m_drawArea = QRect(0,(height() - width())/2,width(),width());
    }

    m_drawArea.adjust(10,10,-10,-10);
}

為了確保雷達為圓形,我們以較短的邊作為矩形的邊長。然後使用adjust縮小一點。adjust前兩個引數為左上角x和y的增量,後兩個引數為右下角x和y的增量。

(2)轉動部分由一個扇形和一條直線組成。扇形使用了錐形漸變色。程式碼如下:

    //轉動部分
        //---//線
    qreal x = m_drawArea.center().x() + (qreal)len/2 * cos(-m_pieRotate*3.14159/180);
    qreal y = m_drawArea.center().y() + (qreal)len/2 * sin(-m_pieRotate*3.14159/180);
    painter.setPen(QPen(Qt::white));
    painter.drawLine(m_drawArea.center(),QPointF(x,y));

       //----//扇形
    QConicalGradient gradient;
    gradient.setCenter(m_drawArea.center());
    gradient.setAngle(m_pieRotate + 180); //漸變與旋轉方向恰好相反,以扇形相反的邊作為漸變角度。
    gradient.setColorAt(0.4,QColor(255,255,255,100)); //從漸變角度開始0.5 - 0.75為扇形區域,由於Int型別計算不精確,將範圍擴大到0.4-0.8
    gradient.setColorAt(0.8,QColor(255,255,255,0));
    painter.setBrush(QBrush(gradient));
    painter.setPen(Qt::NoPen);
    painter.drawPie(m_drawArea,m_pieRotate*16,90*16);

直線的繪製很簡單,只需要計算轉動角度對應圓上的點的座標即可。扇形的繪製也很簡單。難點在於漸變色的應用,由於錐形漸變是逆時針的,而我們的雷達是順時針的轉動。所以要計算好扇形區域在漸變的哪個範圍。這裡設定了漸變角度為扇形角度旋轉180度,那麼扇形區域應該在漸變角度的0.5 - 0.75範圍內。由於這裡使用的是int型別,怕計算的不精確,將漸變顏色的範圍設定大一些。

(3)第三部分是隨機閃爍的點,用來點綴。在這裡使用了一個list存放點的座標,另一個list存放這些點的顏色alapha值。使用一個定時器隨機改變這些點的座標值和alapha值。


void CRadar::timerEvent(QTimerEvent *event)
{
    if(m_timerId == event->timerId())
    {
        m_pieRotate -= 10;
        update();
    }
    else if(m_pointTimerId == event->timerId())
    {
        //隨機更換裝飾的點
        for(int i = 0; i < m_points.count(); ++i)
        {
            int offsetX = rand()%m_drawArea.width();
            int offsetY = rand()%m_drawArea.width();
            int alapha = rand()%255;
            m_points.replace(i,QPoint(offsetX,offsetY) + m_drawArea.topLeft());
            m_pointsAlapha.replace(i,alapha);
        }

        update();
    }
}

第一個定時器是用來改變轉動部分的角度的。第二個定時器才是用來改變閃爍點的。


二、程式碼區

radar.h

#ifndef RADAR_H
#define RADAR_H

#include <QWidget>

class Radar : public QWidget
{
	Q_OBJECT

public:
	Radar(QWidget *parent);
	~Radar();
protected:
	void paintEvent(QPaintEvent *event);    //繪製事件
	void resizeEvent(QResizeEvent *event);  //大小重置事件
	void timerEvent(QTimerEvent *event);    //定時器事件
private:
	QRect         m_drawArea;      //繪製區域
	int           m_pieRotate;     //扇形旋轉區域
	int           m_timerId;       //定時器ID
	int           m_pointTimerId;  //變更點定時器
	int           m_nSpeed;        //速度
	QList<QPoint> m_points;        //繪製點
	QList<int>    m_pointsAlapha;  //繪製點顏色alapha值

	
};

#endif // RADAR_H

radarscannice.h

#ifndef RADARSCANNICE_H
#define RADARSCANNICE_H

#include <QtWidgets/QWidget>
#include "ui_radarscannice.h"

class RadarScanNice : public QWidget
{
	Q_OBJECT

public:
	RadarScanNice(QWidget *parent = 0);
	~RadarScanNice();

private:
	Ui::RadarScanNiceClass ui;
};

#endif // RADARSCANNICE_H

radar.cpp

#include "radar.h"
#include <QPainter>
#include <QTimerEvent>
#include <QConicalGradient>
#include <qDebug>

Radar::Radar(QWidget *parent)
	: QWidget(parent)
{
	//初始化
	m_pieRotate = 0;
	m_timerId = -1;
	m_nSpeed = 50;
	m_points << QPoint() << QPoint() << QPoint() << QPoint() << QPoint();
	m_pointsAlapha << 100 << 100 << 100 << 100 << 100;

	//啟動定時器
	m_timerId = startTimer(m_nSpeed);
	m_pointTimerId = startTimer(1200);
}

Radar::~Radar()
{

}
void Radar::paintEvent(QPaintEvent *event)
{
	QPainter painter(this);

	//抗鋸齒
	painter.setRenderHint(QPainter::Antialiasing);

	//背景
	painter.fillRect(rect(), QColor(15, 45, 188));

	//邊長
	int len = m_drawArea.width();

	//底盤(x軸、y軸和3個圓)
	painter.setPen(QPen(Qt::white));
	painter.drawLine(m_drawArea.topLeft() + QPoint(0, len / 2), m_drawArea.topRight() + QPoint(0, len / 2));
	painter.drawLine(m_drawArea.topLeft() + QPoint(len / 2, 0), m_drawArea.bottomLeft() + QPoint(len / 2, 0));
	painter.drawEllipse(m_drawArea.center(), len / 2, len / 2);
	painter.drawEllipse(m_drawArea.center(), len / 3, len / 3);
	painter.drawEllipse(m_drawArea.center(), len / 6, len / 6);

	//轉動部分
	//---//線
	qreal x = m_drawArea.center().x() + (qreal)len / 2 * cos(-m_pieRotate*3.14159 / 180);
	qreal y = m_drawArea.center().y() + (qreal)len / 2 * sin(-m_pieRotate*3.14159 / 180);
	painter.setPen(QPen(Qt::white));
	painter.drawLine(m_drawArea.center(), QPointF(x, y));

	//----//扇形
	QConicalGradient gradient;
	gradient.setCenter(m_drawArea.center());
	gradient.setAngle(m_pieRotate + 180); //漸變與旋轉方向恰好相反,以扇形相反的邊作為漸變角度。
	gradient.setColorAt(0.4, QColor(255, 255, 255, 100)); //從漸變角度開始0.5 - 0.75為扇形區域,由於Int型別計算不精確,將範圍擴大到0.4-0.8
	gradient.setColorAt(0.8, QColor(255, 255, 255, 0));
	painter.setBrush(QBrush(gradient));
	painter.setPen(Qt::NoPen);
	painter.drawPie(m_drawArea, m_pieRotate * 16, 90 * 16);

	//裝飾-隨機點
	for (int i = 0; i < m_points.count(); ++i)
	{
		int colorAlaph = m_pointsAlapha.at(i);
		painter.setPen(QPen(QColor(255, 255, 255, colorAlaph), 3));
		painter.drawPoint(m_points.at(i));
	}
}

void Radar::resizeEvent(QResizeEvent *event)
{
	//以較短的邊長作為繪製區域邊長
	if (width() > height())
	{
		m_drawArea = QRect((width() - height()) / 2, 0, height(), height());
	}
	else
	{
		m_drawArea = QRect(0, (height() - width()) / 2, width(), width());
	}

	m_drawArea.adjust(10, 10, -10, -10);
}

void Radar::timerEvent(QTimerEvent *event)
{
	if (m_timerId == event->timerId())
	{
		m_pieRotate -= 10;
		update();
	}
	else if (m_pointTimerId == event->timerId())
	{
		//隨機更換裝飾的點
		for (int i = 0; i < m_points.count(); ++i)
		{
			int offsetX = rand() % m_drawArea.width();
			int offsetY = rand() % m_drawArea.width();
			int alapha = rand() % 255;
			m_points.replace(i, QPoint(offsetX, offsetY) + m_drawArea.topLeft());
			m_pointsAlapha.replace(i, alapha);
		}

		update();
	}
}

 

radarscannice.cpp

#include "radarscannice.h"
#include "radar.h"

RadarScanNice::RadarScanNice(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);

	Radar* radar = new Radar(this);
	radar->setFixedSize(300, 300);
	radar->move(150, 50);
}

RadarScanNice::~RadarScanNice()
{

}

main.cpp

#include "radarscannice.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	RadarScanNice w;
	w.show();
	return a.exec();
}

 


效果圖: