Qt 绘制南丁格尔玫瑰图

项目中用来统计数据的一个数据分析部件,

可以统计某范围中的角度个数,(该范围可由外部指定),通过ToolTip事件实现鼠标悬浮显示。

可以图上右键保存png透明图片,通过QT事件函数:void QWidget::contextMenuEvent ( QContextMenuEvent * event ) [virtual protected] 实现,该函数当使用设置函数setContextMenuPolicy并传入Qt::DefaultContextMenu参数时被调用。

保存图片使用了QPixmap中的save()函数。

实现效果:

Qt 绘制南丁格尔玫瑰图_第1张图片

以下为程序主要代码:

头文件:

#ifndef GROSE_VIEW_H
#define GROSE_VIEW_H

#include 
#include 
#include 
#include 
#include 
#include 
#include "guitools_global.h"

class GUITOOLS_EXPORT GRoseView : public QWidget
{
	Q_OBJECT

protected:
	QRect					mViewRect;			// 视图矩形区域
	QRect					mMargins;			// 边距
	double					mMaxRadius;			// 最大的半径
	QPoint					mCenter;			// 绘制的中心点,视图区的中心
	QFont					mRoseTitleFont;
	QFont					mRoseCoordFont;
	QString					mRoseTitle;
	QVector			mAngleCounts;		// 不同角度范围对应的数据个数向量集合
	QVector			mAngleRadius;		// 不同角度范围对应的半径
	QVector			mDatas;             // 角度数据集
	int						mDeltAngle;         // 角度集合的范围
	int                     mMaxAngleNumber;    // 方位角个数
	double                  mLineAngle;			// 刻度角度间隔
	double                  mUniRdius;			// 单位扇形半径
	QStringList             mAxisLabelLists;	// 横轴刻度标签列表
	QColor					mColor;				// 颜色
	int						mUpNumber;
	int                     mCountMax;
	double					mBestAzimuth;
public:
	GRoseView(QWidget *parent);
	~GRoseView();

protected:
	virtual void resizeEvent(QResizeEvent* event);

	void paintEvent(QPaintEvent * event);

	
	void drawRosePicture(QPainter *p);            // 绘制玫瑰图

	
	void drawSector(QPainter *p, int radius, int startAngle, int angleLength, QColor color);    // 绘制每个扇形
	
	void drawCoordinate(QPainter *p);             // 绘制坐标系

	void drawRoseTitle(QPainter *p);

	void setScale(int number);                    //设置横轴刻度

	void staAngleCount();                         //设置角度相关内容

	virtual bool event(QEvent *event);

	QString angleMessageTip(double x, double y);   //浮窗显示

	double getPointAngle(double x,double y);       
public:
	void clear();

	// 设置绘图边距
	void setMargins(int marginTop, int marginBottom, int marginLeft, int marginRight);

	// 设置数据
	void setDatas(const QVector &datas);

	// 设置变化角度
	void setDeltAngle(int deltAngle);
	
	// 设置标度字体
	void setCoordFont(const QFont& font);

	// 设置标题字体
	void setTitleFont(const QFont& font);

	// 设置标题
	void setTitle(const QString& title);

	void setColor(QColor color);

	// 获取最佳方位角
	const double &getBestAzimuth();

	void savePicture(QString filename);      //保存图片

	//右键菜单事件
	void contextMenuEvent(QContextMenuEvent *event);   

private slots:
	void on_mSaveRoseView_triggered(bool checked);

};



#endif // GROSEPICTURE_H

源文件:

#include "groseview.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include "gmath.h"

GRoseView::GRoseView(QWidget *parent)
	: QWidget(parent)
{
	mMaxRadius = 0;
	mDeltAngle = 1;
	mMaxAngleNumber = 0;
	mLineAngle = 22.5;
	mRoseCoordFont = QFont("Arial", 8, 50, false);
	mRoseTitleFont = QFont("Arial", 8, 50, false);
	mRoseTitle = "";
	mColor = Qt::red;
	this->setContextMenuPolicy(Qt::DefaultContextMenu);   //全局开启右键菜单
}

GRoseView::~GRoseView()
{

}

void GRoseView::resizeEvent(QResizeEvent* event)
{
	int marginRectWidth = width() - mMargins.left() - mMargins.right();
	int marginRectHeight = height() - mMargins.top() - mMargins.bottom();
	mViewRect.setRect(mMargins.left(), mMargins.top(), marginRectWidth, marginRectHeight); 
	mMaxRadius = qMin(mViewRect.width()/2, mViewRect.height()/2);
	mCenter = QPoint((mViewRect.width() >> 1) + mMargins.left(), (mViewRect.height() >> 1) + mMargins.top());
	staAngleCount();
}

void GRoseView::setScale(int number)
{                 
	
	mMaxAngleNumber = number;
	int scaleValue;     //scaleValue 每个小刻度代表的角度个数

	if(mMaxAngleNumber <= 6)      
	{
		mUniRdius = mMaxRadius / mMaxAngleNumber;
		scaleValue = 1;
		mUpNumber = mMaxAngleNumber;    
	}
	else 
	{
		mUniRdius = mMaxRadius / 6;
		mUpNumber = 6;
		scaleValue = ceil((double)mMaxAngleNumber / 6);
	}

	mAxisLabelLists.clear();
	for(int i = 0;i <= mUpNumber;i += 2)
	{
		mAxisLabelLists <setFont(mRoseCoordFont);

	QPen pen;
	QColor color(Qt::black);
	QFontMetrics mit = fontMetrics();

	pen.setWidthF(0.4);
	p->setPen(pen);
	p->drawEllipse(mCenter,qRound(mMaxRadius),qRound(mMaxRadius));

	//画圆环刻度、标签

	int flagloop = 1;                                           //刻度长短标志位
	for(double i = 0; i < 360;i+=mLineAngle)
	{
		p->save();
		QString loopText = QString::number(i);

		double xloop = mCenter.x() + mMaxRadius * qSin(i*PI/180);                         //从12点刻度处开始,每一刻度的坐标值
		double yloop = mCenter.y() - mMaxRadius + (mMaxRadius-mMaxRadius*qCos(i*PI/180));

		p->translate(xloop,yloop);                              //移动坐标系到相应的刻度点
		p->rotate(i);

		if(flagloop%2 != 0){
			p->drawText(-mit.width(loopText)/2,-mit.height()/2,loopText);
			p->drawLine(0,0,0,6);                              //长刻度
		}
		else
			p->drawLine(0,0,0,4);							   //短刻度

		++flagloop;
		p->restore();

	}

	//画横坐标轴
	p->drawLine(mCenter.x(),mCenter.y(),mCenter.x()+mMaxRadius,mCenter.y());  

	int flagabsc = 1,scaleh,j=0;         

	for (int i =0;i<= mUpNumber;i++)
	{

		if(flagabsc%2 != 0)
		{
			scaleh = 6;
		}
		else
			scaleh = 4;
		p->drawLine(mCenter.x()+mUniRdius*i,mCenter.y(),mCenter.x()+mUniRdius*i,mCenter.y()-scaleh);
		
		if(i == 0 || i % 2 == 0)
		{	
			float strw = mit.width(mAxisLabelLists.at(j));          //横轴刻度值宽度
			p->drawText(mCenter.x()+mUniRdius*i-strw/2,mCenter.y()+10,mAxisLabelLists.at(j));
			j++;
		}
		++flagabsc;
	}	
}

void GRoseView::drawRosePicture(QPainter *p)
{	
	p->save();
	int startAngle = 90;
	int deltaAngle = mDeltAngle;

	p->setPen(QPen(Qt::black));

	// 将画笔指定到矩形视图的中心
	p->translate(mCenter);

	for(int i = 0; i< mAngleRadius.count(); i++)
	{
		if (startAngle - deltaAngle < -270)   
		{
			deltaAngle = 270 + startAngle;
		}
 		drawSector(p, mAngleRadius[i],startAngle, deltaAngle, mColor);
		startAngle -= deltaAngle;
	}
	p->restore();
}

void GRoseView::drawSector(QPainter *p, int radius, int startAngle, int angleLength, QColor color)
{
	p->setBrush(color);

	QRectF rect(-radius, -radius, radius << 1, radius << 1);
	p->drawPie(rect, startAngle * 16, -angleLength * 16);
}

void GRoseView::drawRoseTitle(QPainter *p)
{
	QFontMetrics fm = p->fontMetrics();
	p->setFont(mRoseTitleFont);
	p->drawText( mViewRect.left() + mViewRect.width()/2 - fm.width(mRoseTitle)/2, 
		mViewRect.top()- fm.height() - 5,  mRoseTitle);
}

void GRoseView::staAngleCount()                       //设置数据,要实现根据角度个数
{                                                     //调整刻度数,要求刻度为偶数个,刻度值
	if (mDatas.count() <= 0) return ;                 //为正整数

	int n = ceil(360.0 / mDeltAngle);            
	mAngleCounts.fill(0, n);
	for (int i=0; i= n) continue ;
		mAngleCounts[index]++;
	}

	mCountMax = mAngleCounts[0];
	for (int i=1; i> 1) + mMargins.left(), (mViewRect.height() >> 1) + mMargins.top());
}

void GRoseView::setDatas(const QVector &datas)
{
	mDatas = datas;
	staAngleCount();
}

void GRoseView::setDeltAngle(int deltAngle)                     //设置角度间隔
{
	if (deltAngle >360 || deltAngle< 0)
	{
		mDeltAngle = 10;
	}
	else
	{
		mDeltAngle = deltAngle;
	}

	staAngleCount();
	update();
}


void GRoseView::setCoordFont(const QFont& font)
{
	mRoseTitleFont = font;
}

void GRoseView::setTitleFont(const QFont& font)
{
	mRoseTitleFont = font;
}

void GRoseView::setTitle(const QString& title)
{
	mRoseTitle = title;
}

void GRoseView::setColor(QColor color)
{
	mColor = color;
}

const double & GRoseView::getBestAzimuth()
{
	return mBestAzimuth;
}

bool GRoseView::event(QEvent* event)                       //提示事件
{
	if(event->type() == QEvent::ToolTip)
	{
		QHelpEvent *helpEvent = static_cast(event);
		double px = helpEvent->x();
		double py = helpEvent->y();
		QString s = angleMessageTip(px,py);
		QToolTip::showText(helpEvent->globalPos(),s);
	}
	return QWidget::event(event);
}

QString GRoseView::angleMessageTip(double x,double y)          //实现角度统计
{
	double dx = x - mCenter.x();
	double dy = y - mCenter.y();
	if(qSqrt(dx*dx + dy*dy) > mMaxRadius) return QString::null;

	double pointAngle = getPointAngle(x,y); 
	QString s = QString::null;
	int n = ceil(360.0 / mDeltAngle);
	int index = pointAngle / mDeltAngle;
	int angleL = index * mDeltAngle;
	int angleH = mDeltAngle + index * mDeltAngle;
	if(angleH >360)angleH = 360;

	if(mAngleCounts[index] == 0)return NULL;

	s = ("Angle range :" + QString::number(angleL) + QString::fromLocal8Bit("") + "-" 
		+ QString::number(angleH) + QString::fromLocal8Bit("") +"\n");

	s += "Number of azimuthes : " + QString::number(mAngleCounts[index]) +"\n";

	if(mAngleCounts[index] == mCountMax)
		s += "Best advantage azimuth:" + QString::number(mBestAzimuth);
	return s;
}


double GRoseView::getPointAngle(double x,double y)       //得到鼠标所在坐标系中的位置
{
	double x1,y1;
	double pointRadian,pointAngle;

	y1 = x - mCenter.x();
	x1 = mCenter.y() - y;

	if(x >= mCenter.x())					 //[0,pi]
	{						        	
		pointRadian = qAtan2(y1,x1);
	}
	else									//[pi,2*pi]
	{
		pointRadian = qAtan2(y1,x1) + 2*PI ;
	}
	pointAngle = 180*pointRadian/PI;
	return pointAngle;
}

void GRoseView::contextMenuEvent(QContextMenuEvent *event)      //右键菜单事件
{                                                               //设置函数在构造函数中实现
	Q_UNUSED(event);
	QMenu *menu = new QMenu(this);
	QAction *savepicture = new QAction(this);

	savepicture->setToolTip("保存图片");
	savepicture->setText("Saving Picture");
	menu->addAction(savepicture);
	connect(savepicture,SIGNAL(triggered(bool)),this,SLOT(on_mSaveRoseView_triggered(bool)));
	menu->exec(QCursor::pos());

	delete menu;
	delete savepicture;
}

void GRoseView::savePicture(QString filename)                    //保存图片
{
	QPainter p(this);
	p.setRenderHint(QPainter::Antialiasing);
	p.setRenderHint(QPainter::TextAntialiasing);
	QPixmap pixmap(width(),height());
	pixmap.fill(Qt::transparent);
	p.begin(&pixmap);

	drawRoseTitle(&p);
	drawRosePicture(&p);
	drawCoordinate(&p);

	p.end();
	pixmap.save(filename);
}

void GRoseView::on_mSaveRoseView_triggered(bool checked)
{
	QString fileName = QFileDialog::getSaveFileName(this, windowTitle(),
		"", QString::fromLocal8Bit("图像文件 ( *.png )"));//*.bmp*.jpg *.jpeg
	this->savePicture(fileName);
}


 

你可能感兴趣的:(Qt 绘制南丁格尔玫瑰图)