QCustomPlot学习
QCustomPlot默认提供了6个层,如下代码所示,分别是:背景层、网格层、主层、坐标轴层、图例层和矩形选择区域层。
1.在一张视图中表现两个线
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
demo();
}
Widget::~Widget()
{
delete ui;
}
void Widget::demo()
{
QVector<double> x,y,z;
for (double i=0;i<10 ;i+=0.1 ) {
x.append(i);
y.append(i);
z.append(i*i);
}
ui->cw->addGraph();
ui->cw->graph()->setData(x,y);
ui->cw->addGraph();
ui->cw->graph()->setData(x,z);
ui->cw->graph()->setPen(QPen(QColor(Qt::green)));
ui->cw->graph()->setLineStyle(QCPGraph::LineStyle::lsNone);//设置连线的样式
ui->cw->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCross, 10));//设置散点的样式,ssCross为十字样式
ui->cw->graph()->setSelectable(QCP::SelectionType::stMultipleDataRanges);//设置图形中可选的元素,这里配合选择框使用多点选择,可以通过选取框选择多个点,也可以按住ctrl多选
ui->cw->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables|QCP::iMultiSelect);
ui->cw->replot();
}
//可以把矩形框设置放在外面
void Widget::on_checkBox_2_clicked(bool checked)
{
if(checked){
ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmSelect);//添加鼠标选取矩形框,并设置为选择操作,此时鼠标的拖拽功能失效。按住ctrl可以多选
}else{
ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmNone);//取消矩形框选取,鼠标拖拽功能返回
ui->cw->deselectAll();
ui->cw->replot();
}
}
2.一张视图中多个图表:使用QCPLayoutGrid
void Widget::demo2()
{
//一张视图中多个图表
//创建QCPAxisRect
QCPAxisRect* a = new QCPAxisRect (ui->cw);
QCPAxisRect* b = new QCPAxisRect (ui->cw);
//添加到QCPLayoutGrid
ui->cw->plotLayout()->clear();
QCPLayoutGrid* lay = ui->cw->plotLayout() ;
lay->addElement(0,0,a);
lay->addElement(1,0,b);
//创建数据
QVector<QCPGraphData> d1,d2;
for (double i=0;i<10 ;i+=0.1 ) {
d1.append(QCPGraphData(i,i));
d2.append(QCPGraphData(i,i*i));
}
//创建数据,添加到QCPGraph
QCPGraph* ga = ui->cw->addGraph(a->axis(QCPAxis::atBottom),a->axis(QCPAxis::atLeft));
ga->data()->set(d1);
ga->rescaleAxes();
QCPGraph* gb = ui->cw->addGraph(b->axis(QCPAxis::atBottom),b->axis(QCPAxis::atLeft));
gb->data()->set(d2);
gb->rescaleAxes();
//同时改变X轴大小,可以用来进行对比
ui->cw->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
connect(ga->keyAxis(),SIGNAL(rangeChanged(QCPRange)),gb->keyAxis(),SLOT(setRange(QCPRange)));
connect(gb->keyAxis(),SIGNAL(rangeChanged(QCPRange)),ga->keyAxis(),SLOT(setRange(QCPRange)));
ui->cw->axisRect(0)->setRangeZoom(Qt::Horizontal);
ui->cw->axisRect(1)->setRangeZoom(Qt::Horizontal);
}
同步X轴大小:
{
//同时改变X轴大小,可以用来进行对比
ui->cw->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
for(int i=0 ; i < ui->cw->graphCount(); ++i){
connect(ui->cw->graph(i)->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(setXRange(QCPRange)));
ui->cw->axisRect(i)->setRangeZoom(Qt::Horizontal);
}
}
void Widget::setXRange(QCPRange range)
{
for(int i=0 ; i < ui->cw->graphCount(); ++i){
ui->cw->graph(i)->keyAxis()->setRange(range);
}
}
添加checkbox是否同步:
void Widget::on_checkBox_clicked(bool checked)
{
if(checked){
for(int i=0 ; i < ui->cw->graphCount(); ++i){
connect(ui->cw->graph(i)->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(setXRange(QCPRange)));
ui->cw->axisRect(i)->setRangeZoom(Qt::Horizontal);
ui->cw->axisRect(i)->setRangeDrag(Qt::Horizontal);
ui->cw->graph(i)->rescaleAxes();
}
}else{
for(int i=0 ; i < ui->cw->graphCount(); ++i){
disconnect(ui->cw->graph(i)->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(setXRange(QCPRange)));
ui->cw->axisRect(i)->setRangeZoom(Qt::Horizontal|Qt::Vertical);
ui->cw->axisRect(i)->setRangeDrag(Qt::Horizontal|Qt::Vertical);
}
}
}
遗憾的是,现在QCPLayoutGrid这个方法还没找到有QSplitter这样可以分割的部件实现调整窗口的大小。
3.一张视图中多个图表:使用QSplitter
使用两个QCustomPlot窗口,加上一个QSplitter实现调整大小
void Widget::demo3()
{
ui->cw->plotLayout()->clear();
//创建数据
QVector<QCPGraphData> d1,d2,d3;
for (double i=0;i<10 ;i+=0.1 ) {
d1.append(QCPGraphData(i,i));
d2.append(QCPGraphData(i,i*i));
d3.append(QCPGraphData(i,qSqrt(i)));
}
QCustomPlot* qcp1 = new QCustomPlot();
QCustomPlot* qcp2 = new QCustomPlot();
QCustomPlot* qcp3 = new QCustomPlot();
qcp1->addGraph()->data()->set(d1);
qcp2->addGraph()->data()->set(d2);
qcp3->addGraph()->data()->set(d3);
qcp1->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
qcp2->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
qcp3->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom|QCP::iSelectPlottables);
/*QSplitter**/ pSplitter = new QSplitter(Qt::Vertical,this);
pSplitter->addWidget(qcp1);
pSplitter->addWidget(qcp2);
pSplitter->addWidget(qcp3);
ui->show->addWidget(pSplitter); //注意这里的 ui->show为 QGridLayout类型
QCustomPlot* q = nullptr;
for(int i=0;i < pSplitter->count();++i){
q = static_cast<QCustomPlot*>(pSplitter->widget(i));
connect(q->graph()->keyAxis(),SIGNAL(rangeChanged(QCPRange)),this,SLOT(set3Range(QCPRange)));
}
}
void Widget::set3Range(QCPRange range)
{
QCustomPlot* q = nullptr;
for(int i=0;i < pSplitter->count();++i){
q = static_cast<QCustomPlot*>(pSplitter->widget(i));
q->graph()->keyAxis()->setRange(range);
q->replot();
}
}
4.时间轴
double now = QDateTime::currentDateTime().toSecsSinceEpoch();
QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);
dateTicker->setDateTimeFormat("d MMMM\n yyyy");
ui->cw->xAxis2->setTicker(dateTicker);
ui->cw->xAxis2->setRange(now,now+24*3600*10);
ui->cw->xAxis2->setVisible(true);
5.实现鼠标左键选框,中键平移,右键菜单
以下鼠标键都在qcustomplot.cpp中修改,注意不要搞错成员函数所在的类名
左键选框:
把官方的框选功能限制在左键上,在函数void QCustomPlot::mousePressEvent(QMouseEvent *event)中修改如下:
void QCustomPlot::mousePressEvent(QMouseEvent *event)
{
emit mousePress(event);
// save some state to tell in releaseEvent whether it was a click:
mMouseHasMoved = false;
mMousePressPos = event->pos();
//添加 event->button() == Qt::LeftButton ,将选取框按键绑定在左键
if (event->button() == Qt::LeftButton && mSelectionRect && mSelectionRectMode != QCP::srmNone)
{
if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect
mSelectionRect->startSelection(event);
} else if(event->button() != Qt::LeftButton)//添加if
{//...
}
}
中键平移:
官方默认左键是平移曲线,我们把平移功能改到中键上去,以便右键菜单。直接在函数
void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)改,把Qt::LeftButton改成Qt::RightButton即可。
void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)
{
Q_UNUSED(details)
if (event->buttons() & Qt::midButton)//将Qt::LeftButton改成你要绑定的拖拽的按键
{
//...
}
}
右键菜单:
void demo(){
ui->cw->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->cw,&QCustomPlot::customContextMenuRequested,this,&Widget::MenuRequested);
}
void Widget::MenuRequested(QPoint p)
{
QMenu *menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addAction("resize",this,[=](){
ui->cw->rescaleAxes();
ui->cw->replot();
});
menu->addAction("zoom",this,[=](){
ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);
});
menu->addAction("select",this,[=](){
ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmSelect);
});
menu->addAction("None",this,[=](){
ui->cw->setSelectionRectMode(QCP::SelectionRectMode::srmNone);
});
menu->popup(ui->cw->mapToGlobal(p));//必须要,否则不显示菜单
}
写进新的类:plotView
//hpp
#include "../../plotLib/qcustomplot.h"
class plotView : public QCustomPlot
{
public:
plotView(QWidget *parent = nullptr);
void slotMenuRequested(QPoint p);
};
//cpp
#include "plotview.h"
plotView::plotView(QWidget *parent)
:QCustomPlot(parent)
{
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this,&QCustomPlot::customContextMenuRequested,this,&plotView::slotMenuRequested);
}
void plotView::slotMenuRequested(QPoint p)
{
QMenu *menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addAction("resize",this,[=](){
this->rescaleAxes();
this->replot();
});
menu->addAction("zoom",this,[=](){
this->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);
});
menu->addAction("select",this,[=](){
this->setSelectionRectMode(QCP::SelectionRectMode::srmSelect);
});
menu->addAction("None",this,[=](){
this->setSelectionRectMode(QCP::SelectionRectMode::srmNone);
});
menu->popup(this->mapToGlobal(p));
}
6.设置游标,显示当前坐标值
//游标设置
QCPItemTracer* tracer = new QCPItemTracer(ui->cw);
ui->cw->setMouseTracking(true);//让游标自动随鼠标移动,若不想游标随鼠标动,则禁止
tracer->setPen(QPen(Qt::DashLine));
tracer->setStyle(QCPItemTracer::tsPlus);
tracer->setSize(50);
tracer->setGraph(ui->cw->graph(0));
QCPItemText* tracerLabel = new QCPItemText(ui->cw);
tracerLabel->position->setParentAnchor(tracer->position);//让标签自动随着游标移动
connect(ui->cw,&QCustomPlot::mouseMove,[=](QMouseEvent* e){
double x = ui->cw->xAxis->pixelToCoord(e->pos().x());
tracer->setGraphKey(x);
// tracer->setInterpolating(true);//自动计算y值,若只想看已有点,不需要这个
tracer->updatePosition();
tracerLabel->setText(QString("x:%1,y:%2").arg(tracer->position->key()).arg(tracer->position->value()));
ui->cw->replot(QCustomPlot::rpQueuedReplot);
});
connect(ui->cw, &QCustomPlot::selectionChangedByUser, [=](){
for (int i = 0; i < ui->cw->graphCount(); ++i)
{
QCPGraph *graph = ui->cw->graph(i);
QCPPlottableLegendItem *item = ui->cw->legend->itemWithPlottable(graph);
if (item->selected() || graph->selected())//选中了哪条曲线或者曲线的图例
{
tracer->setGraph(graph);
}
}
});
7.显示点信息和其他图片
显示图像上点的信息
//使用plottableClick信号获取最近的一个数据点数据
connect(ui->cw,&QCustomPlot::plottableClick,[=](QCPAbstractPlottable* plottable,int dataIndex,QMouseEvent* e){
double x = plottable->interface1D()->dataMainKey(dataIndex);
double y = plottable->interface1D()->dataMainValue(dataIndex);
ui->plainTextEdit->clear();
ui->plainTextEdit->appendPlainText(QString("x:")+QString::number(x,'f',2));
ui->plainTextEdit->appendPlainText(QString("y:")+QString::number(y,'f',2));
});
自制散点样式
ui->cw->graph(1)->setLineStyle(QCPGraph::LineStyle::lsNone);
//自定义样式,填色原点
QCPScatterStyle ss(QCPScatterStyle::ssCircle, 10);
ss.setPen(QPen(QColor(Qt::black)));
ss.setBrush(QColor(Qt::yellow));
ui->cw->graph(1)->setScatterStyle(ss);
ui->cw->graph(1)->setSelectable(QCP::SelectionType::stMultipleDataRanges);
直线
//游标直线设置,使直线随着鼠标移动
{
//游标直线设置
_lineV = new QCPItemLine(ui->cw);
_lineV->setLayer(ui->cw->axisRect()->layer());
QPen pen(QColor(Qt::green),20);
_lineV->setPen(pen);
_lineV->start->setTypeY(QCPItemPosition::PositionType::ptAxisRectRatio);
_lineV->end->setTypeY(QCPItemPosition::PositionType::ptAxisRectRatio);
}
connect(ui->cw,&QCustomPlot::mouseMove,[=](QMouseEvent* e){
double x = ui->cw->xAxis->pixelToCoord(e->pos().x());
//使线起始位置和终点位置在上下
_lineV->start->setCoords(x,1);
_lineV->end->setCoords(x,0);
}
使标签固定在直线下面
//游标标签设置
tracerLabel = new QCPItemText(ui->cw);
tracerLabel->setLayer(ui->cw->axisRect()->layer());
tracerLabel->setPadding(QMargins(0,0,0,20));
tracerLabel->position->setParentAnchor(_lineV->start);//将锚点固定到直线的下面
tracerLabel->setClipToAxisRect(false);//可以让标签显示在axisRect外面