项目中用来统计数据的一个数据分析部件,
可以统计某范围中的角度个数,(该范围可由外部指定),通过ToolTip事件实现鼠标悬浮显示。
可以图上右键保存png透明图片,通过QT事件函数:void QWidget::contextMenuEvent ( QContextMenuEvent * event ) [virtual protected] 实现,该函数当使用设置函数setContextMenuPolicy并传入Qt::DefaultContextMenu参数时被调用。
保存图片使用了QPixmap中的save()函数。
实现效果:
以下为程序主要代码:
头文件:
#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);
}