2020-7-15 补充:
发现QCustomPlot自带框选放大功能,不需要自己写,
直接启用就好了:
plot->selectionRect()->setPen(QPen(Qt::black,1,Qt::DashLine));//设置选框的样式:虚线
plot->selectionRect()->setBrush(QBrush(QColor(0,0,100,50)));//设置选框的样式:半透明浅蓝
plot->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);
不过使用官方的框选放大功能,也带来一个毛病,就是鼠标左、右、中,三个键都变成了框选放大,失去了拖拽平移功能,这用起来很不爽,我们改一下官方源码,使之同时具备这种功能:1、左键框选时进行放大,2、滚轮按下并拖拽时,可平移曲线,3、右键单击弹出功能菜单。
为实现这3个功能,首先我们要把官方的平移功能,由左键改到右键上,见下文。然后我们再把官方的框选功能限制在左键上,如下图两处修改。齐活儿,enjoy it。
----------------以下为原文----------------
QCustomPlot原本是不带这个功能的,不过既然有源码,也好改。
原理很简单:1、记录鼠标按下/弹起的像素坐标,并转换为图像坐标,然后把轴的显示范围重新设置一下即可。
2、如果在释放鼠标前按下了ESC键,那么就取消框选放大功能。
先看一下效果:用鼠标画出一个矩形区域,释放鼠标后即可把图像放大到框选区。
由于官方默认左键是平移曲线,我们先把平移功能改到右键(或者滚轮中键,根据你的喜好)上去,直接在这个函数里
void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)改就行了,把Qt::LeftButton改成Qt::RightButton即可。
下面开始实现框选放大功能:
1、在QCustomPlot类中添加private变量:
private:
QRubberBand *rb;
QPoint startPos;
bool cancelRb;
2、直接在h文件中,QCustomPlot类中添加ESC按键处理函数
virtual void QCustomPlot::keyPressEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Escape) {
cancelRb = true; //记录标志位,以便释放鼠标后不执行缩放代码
rb->hide();//隐藏选择框
}
}
3、在QCustomPlot的3个鼠标事件函数添加代码
0、在构造函数QCustomPlot::QCustomPlot(QWidget *parent)的初始化列表中添加:
,rb(new QRubberBand(QRubberBand::Rectangle, this))
,startPos(0, 0)
1、左键按下时,记录坐标起点
在QCustomPlot::mousePressEvent(QMouseEvent *event)中添加:
if(event->buttons() & Qt::LeftButton)
{
startPos = event->pos();
cancelRb = false;
rb->resize(0, 0);
rb->show();
}
2、左键按下并移动时,绘制矩形框
在void QCustomPlot::mouseMoveEvent(QMouseEvent *event)中添加:
if(event->buttons() & Qt::LeftButton)
{
QRect normalRect = QRect(startPos, event->pos()).normalized();//任意两点定义矩形
rb->setGeometry(normalRect);
}
3、左键弹起时,记录终点坐标,并把曲线放大到【起点、终点】围成的矩形框中
在void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)中添加:
if(event->button() == Qt::LeftButton)
{
rb->hide();
if(!cancelRb)
{
QRect normalRect = QRect(startPos, event->pos()).normalized();
rb->setGeometry(normalRect);
this->xAxis->setRange(xAxis->pixelToCoord(normalRect.left()),
xAxis->pixelToCoord(normalRect.right()));
this->yAxis->setRange(yAxis->pixelToCoord(normalRect.bottom()),
yAxis->pixelToCoord(normalRect.top()));
this->replot();//立即刷新图像
}
}
所有代码就这些。
以上代码中需要说明的就3点:
1、框选区使用了QT自带的橡皮筋选区QRubberBand,这玩意在我看来就是个半透明带颜色的QWidget,其实我们完全可以用QWidget来显示鼠标画出的矩形区,只不过有点麻烦而已,需要我们自己设置QWidget的漂亮样式(边框、蓝色半透明等)。总的来看,不如直接用QRubberBand来显示选区方便。
2、记录矩形选区
鼠标按下时记录一次坐标,释放时再记录一次坐标,这两个点构成的矩形就作为选区,只不过这里有个问题,如果你是从左上向右下进行框选这没毛病,如果从右下向左上进行框选,右下点和左上点,这两个点不符合QRec的构造函数形参要求,这样直接用来设置rb->setGeometry(rect);就会显示不出来,因为setGeometry的本质形参是(x,y,w,h),右下点和左上点相减得出的宽度w和高度h都是负的,左下和右上点得出的w+y-等等,只有左上和右下才能得出w+y+。当然这个问题可以程序员自行判断两个点的位置,并重新构造一个合理的x、y、w、h,但是我们没必要费这个劲,QRect::normalized()就是做这个事情的,执行normalized之后,QRect::top/bottom/left/right就是矩形区真正的上下左右。
3、QWidget的像素坐标转换为曲线的XY轴坐标,用的是xAxis->pixelToCoord(xx),反向功能函数为xAxis->coordTopixel(xx)