提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
最近做了一个项目,需要当鼠标在饼状图上移动的时候展示Slice的内容,现有的饼状图接口不能满足我的需求,所以我想到了手动实现追踪鼠标移动并判断落点是不是在图例内部。
主要难点在于将x,y点转变成极坐标
并判断鼠标落在哪一个Slice上,从而实现定制化效果。
一、饼状图的关键接口
cpp
setMouseTracking(true); // 鼠标追踪,必须打开
_chart->layout()->setContentsMargins(0, 0, 0, 0); // 设置图表外边距,0无边距
_chart->setMargins(QMargins(0, 0, 0, 0)); // 设置图表内边距,0无边距
_series->setPieSize(pPieSize); //这里pPieSize是0.6
二、关键代码
1.鼠标移动事件
先打开上面的鼠标追踪,一定要打开鼠标追踪,否则鼠标在Widget上的时候只会触发一次Move。
cpp
void XPieChart::mouseMoveEvent(QMouseEvent *event) {
calculateInSlice(event->pos());
QChartView::mouseMoveEvent(event);
}
2.核心判断逻辑
核心要点不复杂,先判断距离是不是小于半径(注意场景坐标转换)。其次xy坐标转换成极坐标
,判断是不是在角度范围内即可。
cpp
void XPieChart::calculateInSlice(const QPoint &point) {
// 坐标转换
QPointF scenePos = mapToScene(point);
QPointF chartPos = chart()->mapFromScene(scenePos);
QRectF plotArea = chart()->plotArea();
QPointF center = plotArea.center();
qreal radius = qMin(plotArea.width(), plotArea.height()) / 2 * pPieSize;
// 计算距离和角度
qreal dx = chartPos.x() - center.x();
qreal dy = chartPos.y() - center.y();
qreal distance = sqrt(dx * dx + dy * dy);
if (distance > radius) { // 饼图之外
updatePopupState(false);
return;
}
qDebug() << "chartPos: " << chartPos << " center: " << center;
qreal angle = qRadiansToDegrees(qAtan2(dy, dx));
qDebug() << "angle: " << angle;
qreal pieAngle = 90 + angle; // 转换为饼图角度系统
if (pieAngle < 0) pieAngle += 360;
// 遍历series查找slice
QPieSeries *series = nullptr;
foreach (QAbstractSeries *s, chart()->series()) {
series = qobject_cast<QPieSeries *>(s);
if (series) break;
}
if (!series) {
updatePopupState(false);
return;
}
foreach (QPieSlice *slice, series->slices()) {
qreal start = slice->startAngle();
qreal span = slice->angleSpan();
qreal end = start + span;
bool inSlice = false;
if (end > 360) {
if ((pieAngle >= start && pieAngle <= 360) || (pieAngle >= 0 && pieAngle <= end - 360)) {
inSlice = true;
}
}else {
if (pieAngle >= start && pieAngle <= end) {
inSlice = true;
}
}
if (inSlice) {
qDebug() << "pieAngle: " << pieAngle;
qDebug() << "slice: " << slice->label() << " start: " << start << " span: " << span;
_popupLabel->setText(slice->label());
_popupValue->setText(QString::number(slice->value()));
updatePopupState(true, point);
break;
} else {
updatePopupState(false);
}
}
注意:如果Slice展开可能影响判断结果,需要计算Slice展开的距离差。
总结
1、思路比较透彻,没什么难度,适合定制化需求