QCustomPlot 原生支持滑动区域放大,核心是使用 QCPSelectionRect 配合 QCP::srmZoom 模式。你只需启用对应设置,无需手动编写复杂的鼠标事件逻辑。
实现步骤
-
启用缩放模式 :通过
setSelectionRectMode(QCP::srmZoom)告诉 QCustomPlot,当用户拖拽出矩形选区时,执行缩放操作。 -
配置生效的轴 :如果需要同时缩放 X 和 Y 轴,需确保
setRangeZoomAxes包含这两个轴(默认通常已包含)。如果只想缩放 X 轴,则仅将 X 轴传入即可。
代码示例1
以下代码展示了如何开启滑动区域放大,并自定义选区的外观:
// 假设 customPlot 是你的 QCustomPlot 指针
// 1. 开启矩形缩放模式
customPlot->setSelectionRectMode(QCP::srmZoom);
// 2. 获取当前的选区对象,并自定义样式(可选)
QCPSelectionRect *rect = customPlot->selectionRect();
if (rect) {
// 设置选区边框为蓝色虚线
rect->setPen(QPen(Qt::blue, 1, Qt::DashLine));
// 设置选区内部填充为半透明蓝色
rect->setBrush(QBrush(QColor(0, 0, 255, 30)));
}
// 3. 如果只希望缩放 X 轴,取消勾选 Y 轴(通常用于时域图表)
// QList<QCPAxis*> zoomAxes;
// zoomAxes << customPlot->xAxis;
// customPlot->axisRect()->setRangeZoomAxes(zoomAxes);
//不开启矩形缩放模式
customPlot->setSelectionRectMode(QCP::srmNone);
QCP::SelectionRectMode 的枚举定义。
四种模式对照
| 模式 | 行为 |
|---|---|
srmNone |
禁用选区矩形,鼠标事件直接透传给底层对象(如轴拖动) |
srmZoom |
拖拽出选区,松手后自动 按 setRangeZoomAxes 缩放 |
srmSelect |
拖拽选区,松手后选中选区内的数据点 |
srmCustom |
拖拽出选区,松手后发 accepted 信号,一切处理由程序员负责 |
代码示例2
将框选区域放大到充满整个 QCustomPlot 控件,同时能一键恢复原始视图。
步骤:
-
用户框选时,
QCPSelectionRect会记录选区在屏幕坐标系 下的矩形QRect -
通过
axisRect()->rect()与轴范围的比例关系,把屏幕矩形转换成轴数据范围 -
调用
xAxis->setRange()/yAxis->setRange()让该区域充满整个控件 -
恢复时,保存原始轴范围,一键还原
MyPlot.h
class MyPlot : public QCustomPlot {
Q_OBJECT
public:
explicit MyPlot(QWidget *parent = nullptr);
void restoreView(); // 恢复到非全控件显示
private slots:
void onSelectionAccepted(const QRect &rect, QMouseEvent *event);
private:
bool m_hasSavedRange = false;
QCPRange m_savedXRange; // 保存原始 X 轴范围
QCPRange m_savedYRange; // 保存原始 Y 轴范围
};
MyPlot.cpp
MyPlot::MyPlot(QWidget *parent) : QCustomPlot(parent)
{
// 使用自定义选区模式,方便自己处理
setSelectionRectMode(QCP::srmCustom);
// 自定义选区外观
QCPSelectionRect *selRect = selectionRect();
selRect->setPen(QPen(Qt::red, 1, Qt::DashLine));
selRect->setBrush(QBrush(QColor(255, 0, 0, 30)));
// 连接 accepted 信号:用户松开鼠标、确认选区后触发
disconnect(selRect, &QCPSelectionRect::accepted, nullptr, nullptr);
connect(selRect, &QCPSelectionRect::accepted,
this, &MyPlot::onSelectionAccepted);
}
void MyPlot::onSelectionAccepted(const QRect &rect, QMouseEvent *event)
{
Q_UNUSED(event);
// rect 是屏幕坐标系下的选区矩形(相对于 axisRect)
if (rect.width() < 2 || rect.height() < 2)
return; // 选区太小,忽略
// 首次缩放前保存原始范围,方便恢复
if (!m_hasSavedRange) {
m_savedXRange = xAxis->range();
m_savedYRange = yAxis->range();
m_hasSavedRange = true;
}
QCPAxisRect *ar = axisRect();
// ---------- 计算 X 轴新范围 ----------
// 把屏幕 x 坐标 映射到 轴数据坐标
double x1 = xAxis->pixelToCoord(rect.left());
double x2 = xAxis->pixelToCoord(rect.right());
// 选区可能反向拖拽,保证下界 < 上界
if (x1 > x2) std::swap(x1, x2);
// ---------- 计算 Y 轴新范围 ----------
double y1 = yAxis->pixelToCoord(rect.bottom());
double y2 = yAxis->pixelToCoord(rect.top());
if (y1 > y2) std::swap(y1, y2);
// 设置新范围(该区域将充满整个控件)
xAxis->setRange(x1, x2);
yAxis->setRange(y1, y2);
replot();
}
void MyPlot::restoreView()
{
if (!m_hasSavedRange)
return; // 没有保存过,无需恢复
xAxis->setRange(m_savedXRange);
yAxis->setRange(m_savedYRange);
m_hasSavedRange = false; // 可选:下次缩放重新保存
//取消可能的残留选择框
QCPSelectionRect *selRect = ui->widgetWave2->selectionRect();
if(selRect != nullptr){
selRect->cancel();
}
replot();
}
关键点说明:
1、只缩放 X 轴 / 只缩放 Y 轴
如果你只想横向放大(时域图常见):
xAxis->setRange(x1, x2);
// yAxis 保持原样,不调用 setRange
2、支持多次连续缩放
上面代码里用 m_hasSavedRange 标记,只在第一次 缩放前保存原始范围。这样即使用户连续框选放大多次,恢复时也能回到最初的完整视图。若你希望每次缩放都可回退一步,可以把范围存进 QList<QPair<QCPRange, QCPRange>> 做成栈。
3、按钮触发恢复
在 UI 上加一个按钮或右键菜单,调用 restoreView() 即可。
connect(ui->btnReset, &QPushButton::clicked, myPlot, &MyPlot::restoreView);
4、 srmCustom 的正确姿势
setSelectionRectMode(QCP::srmCustom);
connect(selectionRect(), &QCPSelectionRect::accepted,
this, &MyPlot::onSelectionAccepted);
在 onSelectionAccepted 里你完全掌控
void MyPlot::onSelectionAccepted(const QRect &rect, QMouseEvent *event)
{
// 1. 保存原始范围(首次)
// 2. 用 pixelToCoord 把屏幕矩形换算成数据范围
// 3. 手动 setRange
// 4. 想怎么处理都行------限制最小范围、只缩 X、记录栈......
}