本例分享如何使用Qt实现一个多边形能力值控件
CCapabilityDiagram
效果如下:
图1
直入主题,
头文件:
CCapabilityDiagram.h
#ifndef CCAPABILITYDIAGRAM_H
#define CCAPABILITYDIAGRAM_H
#include <QWidget>
#include <QMetaObject>
class CCapabilityDiagram: public QWidget
{
Q_OBJECT
public:
struct CapabilityDiagramItem
{
quint64 id;
QString name;
quint64 value;
};
typedef QList<CapabilityDiagramItem> CapabilityDiagramItemList;
CCapabilityDiagram(QWidget* parent = NULL);
~CCapabilityDiagram();
private:
quint64 mMaxValue; //* 最大值
quint64 mMinValue; //* 最小值
float mValueScaleMapping; //* 缩放关系
QColor mVertexPathBorderColor; //* 顶点边框颜色
QColor mVertexPathColor; //* 背景填充色
QColor mPercentVertexLineColor; //* 等分线边框颜色
QColor mValuePathColor; //* 能力值填充色
QColor mValuePathBorderColor; //* 能力值边框色
quint64 mPercentCount; //* 等分线条数
QFont mNameFont; //* item名字的字体
QColor mNameColor; //* item名字的颜色
CapabilityDiagramItemList mCapabilityDiagramItemList;
void paintEvent(QPaintEvent *e);
void mouseMoveEvent(QMouseEvent *e);
public:
void setMaxValue(quint64 v);//* 设置最大值
quint64 getMaxValue();//* 最大值
//* 设置最大值和像素的映射关系,必须 > 0,
//* 例如最大值100, v == 1.5,那么就是150像素来表示100值
void setValueScaleMapping(float v);
void setCapabilityDiagramItemList(const CapabilityDiagramItemList& li); //* 设置能力列表
CapabilityDiagramItemList getCapabilityDiagramItemList(); //* 能力列表
};
Q_DECLARE_METATYPE(CCapabilityDiagram::CapabilityDiagramItem)
Q_DECLARE_METATYPE(CCapabilityDiagram::CapabilityDiagramItemList)
#endif // CCAPABILITYDIAGRAM_H
cpp文件:
CCapabilityDiagram.cpp
#include "CCapabilityDiagram.h"
#include <QPainter>
#include <math.h>
#include <QDebug>
CCapabilityDiagram::CCapabilityDiagram(QWidget *parent)
:QWidget(parent)
,mMaxValue(100)
,mMinValue(0)
,mValueScaleMapping(1.f)
,mPercentCount(10)
{
this->setMouseTracking(true);
mVertexPathBorderColor = QColor(91,93,97); //* 顶点边框颜色
mVertexPathColor = "#5B5E60";
mVertexPathColor.setAlphaF(0.81);
mPercentVertexLineColor = QColor(91,93,97); //* 等分线边框颜色
mValuePathColor = "#C8A666";
mValuePathColor.setAlphaF(0.6);
mValuePathBorderColor = QColor(200,166,102);
mNameColor = "#2D2E2F";
mNameFont.setFamily("GBJenLei-Medium");
mNameFont.setPixelSize(14);;
CapabilityDiagramItemList li;
for(int i = 0; i <6; i++)
{
CapabilityDiagramItem d;
d.id = i;
d.name = QString("Item %1").arg(i);
d.value = i * 20;
li << d;
}
setCapabilityDiagramItemList(li);
}
CCapabilityDiagram::~CCapabilityDiagram()
{
}
void CCapabilityDiagram::paintEvent(QPaintEvent *e)
{
__super::paintEvent(e);
int vertexCount = mCapabilityDiagramItemList.count();
if(vertexCount == 0)
return;
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPointF centerPoint = rect().center();
double stepAngle = 360.f / vertexCount;
const QPointF mousePos = mapFromGlobal(QCursor::pos()) - centerPoint;
//*设置坐标系原点到cetner
p.translate(centerPoint);
//画定点框
//p.fillRect(QRect(-10,-10, 20,20), Qt::red); //* 用于调试
QVector<QPointF> vertexPoints;
quint64 r = mMaxValue;
for(int i = 0; i < vertexCount; i++)
{
//int offsetAngle = stepAngle + (i * stepAngle);
int offsetAngle = 90 + (i * stepAngle);
int offsetX = -cos(offsetAngle * 3.1415 / 180.0 ) * r;
int offsetY = -sin(offsetAngle * 3.1415 / 180.0 ) * r;
vertexPoints << QPointF(offsetX * mValueScaleMapping, offsetY * mValueScaleMapping);
//p.drawLine(QPoint(0,0), vertexPoints.last());
}
QPainterPath vertexPath;
vertexPath.addPolygon(vertexPoints);
p.fillPath(vertexPath, mVertexPathColor);
p.drawPolygon(vertexPoints); //* 图形的顶点
//* percentCount等分线 (max / percentCount) 百分线
p.setPen(QPen(mPercentVertexLineColor, 3, Qt::SolidLine));
int percentStep = mPercentCount;
float scaleValue = 1.f/percentStep;
QList<QVector<QPointF>> percentVertexPointsList;
for(int i = 0; i < percentStep; i++)
{
QVector<QPointF> percentVertexPoints;
float r = mMaxValue * scaleValue * i;
for(int ii = 0; ii < vertexCount; ii++)
{
//int offsetAngle = stepAngle + (ii * stepAngle);
int offsetAngle = 90 + (ii * stepAngle);
int offsetX = -cos(offsetAngle * 3.1415 / 180.0 ) * r;
int offsetY = -sin(offsetAngle * 3.1415 / 180.0 ) * r;
percentVertexPoints << QPointF(offsetX * mValueScaleMapping, offsetY * mValueScaleMapping);
}
percentVertexPointsList << percentVertexPoints;
p.drawPoints(percentVertexPoints); //* 等分线顶点
p.drawPolygon(percentVertexPoints);
}
for(int i = 0; i < percentVertexPointsList.count() - 1; i++) //* 画能力值等高线
{
QPainterPath percentVertexPath;
if(i == 0)
{
QPainterPath firstPercentVertexPath;
firstPercentVertexPath.addPolygon(vertexPoints);
firstPercentVertexPath.addPolygon(percentVertexPointsList[i]);
p.fillPath(firstPercentVertexPath, i % 2 == 0 ? mVertexPathColor.darker() : mVertexPathColor);
}
percentVertexPath.addPolygon(percentVertexPointsList[i]);
percentVertexPath.addPolygon(percentVertexPointsList[i + 1]);
p.fillPath(percentVertexPath, i % 2 != 0 ? mVertexPathColor.darker() : mVertexPathColor);
}
//* 画顶点线
p.setPen(QPen(mVertexPathBorderColor.darker(), 1, Qt::SolidLine));
for(int i = 0; i < vertexPoints.count(); i++)
{
p.drawLine(QPoint(0,0), vertexPoints[i]);
}
//画能力多边形
p.setPen(QPen(mValuePathColor, 3, Qt::SolidLine));
QVector<QPointF> valueVertexPoints;
for(int i = 0; i < vertexCount; i++)
{
float r = mMaxValue * ((float)mCapabilityDiagramItemList[i].value / mMaxValue);
r = r > mMaxValue ? mMaxValue : r;
//int offsetAngle = stepAngle + (i * stepAngle);
int offsetAngle = 90 + (i * stepAngle);
int offsetX = -cos(offsetAngle * 3.1415 / 180.0 ) * r;
int offsetY = -sin(offsetAngle * 3.1415 / 180.0 ) * r;
valueVertexPoints << QPointF(offsetX * mValueScaleMapping, offsetY * mValueScaleMapping);
}
QPainterPath valuePath;
valuePath.addPolygon(valueVertexPoints);
if(QPolygonF(valueVertexPoints).containsPoint(mousePos, Qt::WindingFill))
{
p.fillPath(valuePath, mValuePathColor.lighter());
}
else
{
p.fillPath(valuePath, mValuePathColor);
}
p.drawPolygon(valueVertexPoints);
//画名字
p.setPen(QPen(mNameColor, 1, Qt::SolidLine));
p.setFont(mNameFont);
for(int i = 0; i < vertexCount; i++)
{
QString name = mCapabilityDiagramItemList[i].name;
int nameWidth = p.fontMetrics().width(name);
int nameHegiht = p.fontMetrics().height();
QPoint namePos = vertexPoints[i].toPoint();
if(namePos.x() < 0) //* 避免遮挡
{
namePos.setX(namePos.x() - nameWidth);
}
if(namePos.y() < 0)
{
namePos.setY(namePos.y() - nameHegiht);
}
if(namePos.x() == 0) //* 居中
{
namePos.setX(namePos.x() - nameWidth / 2);
}
if(namePos.y() == 0)
{
namePos.setY(namePos.y() - nameHegiht / 2);
}
QRect nameRect = QRect(namePos, QSize(nameWidth, nameHegiht));
p.drawText(nameRect, Qt::AlignCenter, name);
}
}
void CCapabilityDiagram::mouseMoveEvent(QMouseEvent *e)
{
__super::mouseMoveEvent(e);
update();
}
void CCapabilityDiagram::setMaxValue(quint64 v)
{
mMaxValue = v;
update();
}
quint64 CCapabilityDiagram::getMaxValue()
{
return mMaxValue;
}
void CCapabilityDiagram::setValueScaleMapping(float v)
{
mValueScaleMapping = v;
update();
}
void CCapabilityDiagram::setCapabilityDiagramItemList(const CCapabilityDiagram::CapabilityDiagramItemList &li)
{
mCapabilityDiagramItemList = li;
update();
}
CCapabilityDiagram::CapabilityDiagramItemList CCapabilityDiagram::getCapabilityDiagramItemList()
{
return mCapabilityDiagramItemList;
}
在实现了我们的控件之后来尝试一下如何使用:
#include <QApplication>
#include "CCapabilityDiagram.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CCapabilityDiagram* capabilityDiagram = new CCapabilityDiagram;
CCapabilityDiagram::CapabilityDiagramItemList li;
for(int i = 0; i <6; i++)
{
CCapabilityDiagram::CapabilityDiagramItem d;
d.id = i;
d.name = QString(QStringLiteral("能力值 %1")).arg(i);
d.value = i * 20;
li << d;
}
capabilityDiagram->setCapabilityDiagramItemList(li);
capabilityDiagram->show();
return a.exec();
}
运行结果:
至此我们使用Qt来实现一个多边形能力值控件就成功了。