如何检测与选择矩形相交的所有Qt图表序列
嘿,刚好我之前在做Qt图表交互的时候踩过类似的坑,来给你拆解下这些问题:
1. 能不能用QGraphicsItem::collidesWith()检测QLineSeries?
很遗憾,这条路走不通。Qt Charts的内部实现里,QLineSeries和QScatterSeries对应的图形元素是被封装在私有类里的,外部代码根本拿不到它们的QGraphicsItem实例——你没法直接调用shape()或者collidesWith()来做碰撞检测,官方也没提供暴露这些item的接口,所以这个方案直接放弃吧。
2. 改用QPainterPath是更合适的方案,具体这么做:
这确实是Qt官方推荐的几何检测方式,完全不用手动计算插值点,正好匹配你的需求。步骤如下:
第一步:把鼠标拖拽的矩形转换成图表坐标系的QPainterPath
鼠标拖拽的矩形默认是在场景坐标系(Scene Coordinate)或者视图坐标系(Viewport Coordinate)里的,你需要先把它转换成QChart的绘图区域坐标系(Plot Area Coordinate)(也就是数据点所在的坐标系):
// 假设你已经拿到了拖拽的矩形sceneRect(QRectF类型,scene坐标) QRectF plotRect = chart()->mapToPlotArea(sceneRect); QPainterPath selectionPath; selectionPath.addRect(plotRect);
第二步:遍历所有序列,检测与选区路径的相交/包含关系
对于QLineSeries:
你可以把整个序列的线段构建成一个QPainterPath,直接判断这个路径和选区路径是否相交:QLineSeries *series = ...; // 你的线序列 QPainterPath seriesPath; bool firstPoint = true; for (const QPointF &point : series->points()) { if (firstPoint) { seriesPath.moveTo(point); firstPoint = false; } else { seriesPath.lineTo(point); } } // 判断线段是否和选区相交(哪怕端点不在选区内,中间穿过也会触发) if (selectionPath.intersects(seriesPath)) { // 这个序列和选区相交,标记它 }QPainterPath::intersects()会自动处理线段和矩形的几何相交,完美解决你说的“插值线段”检测需求,不用手动计算中间点。对于QScatterSeries:
判断每个数据点是否在选区内,或者和选区边界相交(给点加个小矩形是适配散点的实际显示大小,避免漏检):QScatterSeries *scatterSeries = ...; for (const QPointF &point : scatterSeries->points()) { // 散点实际有大小,用小矩形代替点做检测 QRectF pointRect(point.x()-2, point.y()-2, 4, 4); if (selectionPath.contains(point) || selectionPath.intersects(pointRect)) { // 点在选区内或和选区相交,标记序列 break; // 只要有一个点符合,整个序列就算选中 } }
小优化:
如果你的序列数据量很大,可以改成逐个检测相邻线段和选区的相交,一旦发现相交就停止遍历该序列,能节省不少性能。
3. hovered()信号在左键按下时不触发的原因
这是Qt事件分发机制的特性:当你按下鼠标左键时,当前被点击的QGraphicsItem会捕获鼠标焦点,进入“拖动”状态,此时Qt会暂停发送hover事件——系统认为你现在是在拖拽操作,而非hover浏览。所以用hovered信号来检测选区序列的方案根本不可行,放弃就好。
内容的提问来源于stack exchange,提问作者Krakkos

