采用Qt快速绘制多条曲线(折线),跟随鼠标动态显示线上点的值(基于Qt的开源绘图控件QCustomPlot进行二次开发)
程序员文章站
2022-11-21 10:11:47
采用Qt绘制多条曲线,跟随鼠标动态显示线上点的值(基于Qt的开源绘图控件QCustomPlot进行二次开发) ......
qcustomplot是一个开源的基于qt的第三方绘图库,能够绘制漂亮的2d图形。
qcustomplot的官方网址:
从官网下载qcustomplot的源文件,包括qcustomplot.h和qcustomplot.cpp。
本程序的源码下载地址: https://github.com/xiongxw/xcustomplot.git
1 自定义鼠标显示跟随类xxwtracer和xxwtraceline:
xxwtracer用于在图表中显示鼠标所在位置的x,y值
xxwtraceline用于在图中显示水平或垂直的虚线
头文件xxwtracer.h
#ifndef mytracer_h #define mytracer_h #include <qobject> #include "qcustomplot.h" /// /// \brief the xxwtracer class:在图表中显示鼠标所在位置的x,y值的追踪显示器 /// class xxwtracer : public qobject { q_object public: enum tracertype { xaxistracer,//依附在x轴上显示x值 yaxistracer,//依附在y轴上显示y值 datatracer//在图中显示x,y值 }; explicit xxwtracer(qcustomplot *_plot, tracertype _type, qobject *parent = q_nullptr); ~xxwtracer(); void setpen(const qpen &pen); void setbrush(const qbrush &brush); void settext(const qstring &text); void setlabelpen(const qpen &pen); void updateposition(double xvalue, double yvalue); void setvisible(bool m_visible); protected: bool m_visible;//是否可见 tracertype m_type;//类型 qcustomplot *m_plot;//图表 qcpitemtracer *m_tracer;//跟踪的点 qcpitemtext *m_label;//显示的数值 qcpitemline *m_arrow;//箭头 }; /// /// \brief the xxwcrossline class:用于显示鼠标移动过程中的鼠标位置的直线 /// class xxwtraceline : public qobject { public: enum linetype { verticalline,//垂直线 horizonline, //水平线 both//同时显示水平和垂直线 }; explicit xxwtraceline(qcustomplot *_plot, linetype _type = verticalline, qobject *parent = q_nullptr); ~xxwtraceline(); void initline(); void updateposition(double xvalue, double yvalue); void setvisible(bool vis) { if(m_linev) m_linev->setvisible(vis); if(m_lineh) m_lineh->setvisible(vis); } protected: bool m_visible;//是否可见 linetype m_type;//类型 qcustomplot *m_plot;//图表 qcpitemstraightline *m_linev; //垂直线 qcpitemstraightline *m_lineh; //水平线 }; #endif // mytracer_h
源文件mytracer.cpp
#include "mytracer.h" xxwtracer::xxwtracer(qcustomplot *_plot, tracertype _type, qobject *parent) : qobject(parent), m_plot(_plot), m_type(_type) { m_visible = true; m_tracer = q_nullptr;// 跟踪的点 m_label = q_nullptr;// 显示的数值 m_arrow = q_nullptr;// 箭头 if (m_plot) { qcolor clrdefault(qt::red); qbrush brushdefault(qt::nobrush); qpen pendefault(clrdefault); // pendefault.setbrush(brushdefault); pendefault.setwidthf(0.5); m_tracer = new qcpitemtracer(m_plot); m_tracer->setstyle(qcpitemtracer::tscircle); m_tracer->setpen(pendefault); m_tracer->setbrush(brushdefault); m_label = new qcpitemtext(m_plot); m_label->setlayer("overlay"); m_label->setcliptoaxisrect(false); m_label->setpadding(qmargins(5, 5, 5, 5)); m_label->setbrush(brushdefault); m_label->setpen(pendefault); m_label->position->setparentanchor(m_tracer->position); // m_label->setfont(qfont("宋体", 8)); m_label->setfont(qfont("arial", 8)); m_label->setcolor(clrdefault); m_label->settext(""); m_arrow = new qcpitemline(m_plot); qpen arrowpen(clrdefault, 1); m_arrow->setpen(pendefault); m_arrow->setlayer("overlay"); m_arrow->setcliptoaxisrect(false); m_arrow->sethead(qcplineending::esspikearrow);//设置头部为箭头形状 switch (m_type) { case xaxistracer: { m_tracer->position->settypex(qcpitemposition::ptplotcoords); m_tracer->position->settypey(qcpitemposition::ptaxisrectratio); m_tracer->setsize(7); m_label->setpositionalignment(qt::aligntop | qt::alignhcenter); m_arrow->end->setparentanchor(m_tracer->position); m_arrow->start->setparentanchor(m_arrow->end); m_arrow->start->setcoords(0, 20);//偏移量 break; } case yaxistracer: { m_tracer->position->settypex(qcpitemposition::ptaxisrectratio); m_tracer->position->settypey(qcpitemposition::ptplotcoords); m_tracer->setsize(7); m_label->setpositionalignment(qt::alignright | qt::alignhcenter); m_arrow->end->setparentanchor(m_tracer->position); m_arrow->start->setparentanchor(m_label->position); m_arrow->start->setcoords(-20, 0);//偏移量 break; } case datatracer: { m_tracer->position->settypex(qcpitemposition::ptplotcoords); m_tracer->position->settypey(qcpitemposition::ptplotcoords); m_tracer->setsize(5); m_label->setpositionalignment(qt::alignleft | qt::alignvcenter); m_arrow->end->setparentanchor(m_tracer->position); m_arrow->start->setparentanchor(m_arrow->end); m_arrow->start->setcoords(20, 0); break; } default: break; } setvisible(false); } } xxwtracer::~xxwtracer() { if(m_plot) { if (m_tracer) m_plot->removeitem(m_tracer); if (m_label) m_plot->removeitem(m_label); if (m_arrow) m_plot->removeitem(m_arrow); } } void xxwtracer::setpen(const qpen &pen) { if(m_tracer) m_tracer->setpen(pen); if(m_arrow) m_arrow->setpen(pen); } void xxwtracer::setbrush(const qbrush &brush) { if(m_tracer) m_tracer->setbrush(brush); } void xxwtracer::setlabelpen(const qpen &pen) { if(m_label) { m_label->setpen(pen); m_label->setbrush(qt::nobrush); m_label->setcolor(pen.color()); } } void xxwtracer::settext(const qstring &text) { if(m_label) m_label->settext(text); } void xxwtracer::setvisible(bool vis) { m_visible = vis; if(m_tracer) m_tracer->setvisible(m_visible); if(m_label) m_label->setvisible(m_visible); if(m_arrow) m_arrow->setvisible(m_visible); } void xxwtracer::updateposition(double xvalue, double yvalue) { if (!m_visible) { setvisible(true); m_visible = true; } if (yvalue > m_plot->yaxis->range().upper) yvalue = m_plot->yaxis->range().upper; switch (m_type) { case xaxistracer: { m_tracer->position->setcoords(xvalue, 1); m_label->position->setcoords(0, 15); m_arrow->start->setcoords(0, 15); m_arrow->end->setcoords(0, 0); settext(qstring::number(xvalue)); break; } case yaxistracer: { m_tracer->position->setcoords(0, yvalue); m_label->position->setcoords(-20, 0); // m_arrow->start->setcoords(20, 0); // m_arrow->end->setcoords(0, 0); settext(qstring::number(yvalue)); break; } case datatracer: { m_tracer->position->setcoords(xvalue, yvalue); m_label->position->setcoords(20, 0); settext(qstring("x:%1,y:%2").arg(xvalue).arg(yvalue)); break; } default: break; } } xxwtraceline::xxwtraceline(qcustomplot *_plot, linetype _type, qobject *parent) : qobject(parent), m_type(_type), m_plot(_plot) { m_linev = q_nullptr; m_lineh = q_nullptr; initline(); } xxwtraceline::~xxwtraceline() { if(m_plot) { if (m_linev) m_plot->removeitem(m_linev); if (m_lineh) m_plot->removeitem(m_lineh); } } void xxwtraceline::initline() { if(m_plot) { qpen linespen(qt::red, 1, qt::dashline); if(verticalline == m_type || both == m_type) { m_linev = new qcpitemstraightline(m_plot);//垂直线 m_linev->setlayer("overlay"); m_linev->setpen(linespen); m_linev->setcliptoaxisrect(true); m_linev->point1->setcoords(0, 0); m_linev->point2->setcoords(0, 0); } if(horizonline == m_type || both == m_type) { m_lineh = new qcpitemstraightline(m_plot);//水平线 m_lineh->setlayer("overlay"); m_lineh->setpen(linespen); m_lineh->setcliptoaxisrect(true); m_lineh->point1->setcoords(0, 0); m_lineh->point2->setcoords(0, 0); } } } void xxwtraceline::updateposition(double xvalue, double yvalue) { if(verticalline == m_type || both == m_type) { if(m_linev) { m_linev->point1->setcoords(xvalue, m_plot->yaxis->range().lower); m_linev->point2->setcoords(xvalue, m_plot->yaxis->range().upper); } } if(horizonline == m_type || both == m_type) { if(m_lineh) { m_lineh->point1->setcoords(m_plot->xaxis->range().lower, yvalue); m_lineh->point2->setcoords(m_plot->xaxis->range().upper, yvalue); } } }
2 自定义的图表类xcustomplot
xcustomplot是基于qcustomplot二次开发的图表类,在鼠标移动过程中动态显示曲线上点的值。
头文件xcustomplot.h
#ifndef xcustomplot_h #define xcustomplot_h #include "xxwtracer.h" #include "qcustomplot.h" #include <qobject> #include <qlist> class xxwcustomplot:public qcustomplot { q_object public: xxwcustomplot(qwidget *parent = 0); protected: virtual void mousemoveevent(qmouseevent *event); public: /// /// \brief 设置是否显示鼠标追踪器 /// \param show:是否显示 /// void showtracer(bool show) { m_isshowtracer = show; if(m_xtracer) m_xtracer->setvisible(m_isshowtracer); foreach (xxwtracer *tracer, m_datatracers) { if(tracer) tracer->setvisible(m_isshowtracer); } if(m_linetracer) m_linetracer->setvisible(m_isshowtracer); } /// /// \brief 是否显示鼠标追踪器 /// \return /// bool isshowtracer(){return m_isshowtracer;}; private: bool m_isshowtracer;//是否显示追踪器(鼠标在图中移动,显示对应的值) xxwtracer *m_xtracer;//x轴 xxwtracer *m_ytracer;//y轴 qlist<xxwtracer *> m_datatracers;// xxwtraceline *m_linetracer;//直线 }; #endif // xcustomplot_h
源文件xcustomplot.h
#include "xxwcustomplot.h" xxwcustomplot::xxwcustomplot(qwidget *parent) :qcustomplot(parent) ,m_isshowtracer(false) ,m_xtracer(q_nullptr) ,m_ytracer(q_nullptr) ,m_datatracers(qlist<xxwtracer *>()) ,m_linetracer(q_nullptr) { } void xxwcustomplot::mousemoveevent(qmouseevent *event) { qcustomplot::mousemoveevent(event); if(m_isshowtracer) { //当前鼠标位置(像素坐标) int x_pos = event->pos().x(); int y_pos = event->pos().y(); //像素坐标转成实际的x,y轴的坐标 float x_val = this->xaxis->pixeltocoord(x_pos); float y_val = this->yaxis->pixeltocoord(y_pos); if(q_nullptr == m_xtracer) m_xtracer = new xxwtracer(this, xxwtracer::xaxistracer);//x轴 m_xtracer->updateposition(x_val, y_val); if(q_nullptr == m_ytracer) m_ytracer = new xxwtracer(this, xxwtracer::yaxistracer);//y轴 m_ytracer->updateposition(x_val, y_val); int ntracercount = m_datatracers.count(); int ngraphcount = graphcount(); if(ntracercount < ngraphcount) { for(int i = ntracercount; i < ngraphcount; ++i) { xxwtracer *tracer = new xxwtracer(this, xxwtracer::datatracer); m_datatracers.append(tracer); } } else if(ntracercount > ngraphcount) { for(int i = ngraphcount; i < ntracercount; ++i) { xxwtracer *tracer = m_datatracers[i]; if(tracer) { tracer->setvisible(false); } } } for (int i = 0; i < ngraphcount; ++i) { xxwtracer *tracer = m_datatracers[i]; if(!tracer) tracer = new xxwtracer(this, xxwtracer::datatracer); tracer->setvisible(true); tracer->setpen(this->graph(i)->pen()); tracer->setbrush(qt::nobrush); tracer->setlabelpen(this->graph(i)->pen()); auto iter = this->graph(i)->data()->findbegin(x_val); double value = iter->mainvalue(); // double value = this->graph(i)->data()->findbegin(x_val)->value; tracer->updateposition(x_val, value); } if(q_nullptr == m_linetracer) m_linetracer = new xxwtraceline(this,xxwtraceline::both);//直线 m_linetracer->updateposition(x_val, y_val); this->replot();//曲线重绘 } }
3 使用自定义图表类xcustomplot
在需要绘图的地方使用,代码如下:
m_customplot = new xxwcustomplot(); m_customplot->showtracer(true); // add title layout element: m_customplot->plotlayout()->insertrow(0); m_customplot->plotlayout()->addelement(0, 0, new qcptextelement(m_customplot, "标题", qfont("黑体", 12, qfont::bold))); m_customplot->legend->setvisible(true); qfont legendfont = font(); // start out with mainwindow's font.. legendfont.setpointsize(9); // and make a bit smaller for legend m_customplot->legend->setfont(legendfont); m_customplot->legend->setbrush(qbrush(qcolor(255,255,255,230))); // by default, the legend is in the inset layout of the main axis rect. so this is how we access it to change legend placement: m_customplot->axisrect()->insetlayout()->setinsetalignment(0, qt::aligntop|qt::aligncenter); // make left and bottom axes always transfer their ranges to right and top axes: connect(m_customplot->xaxis, signal(rangechanged(qcprange)), m_customplot->xaxis2, slot(setrange(qcprange))); connect(m_customplot->yaxis, signal(rangechanged(qcprange)), m_customplot->yaxis2, slot(setrange(qcprange))); // allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking: m_customplot->setinteractions(qcp::irangedrag | qcp::irangezoom | qcp::iselectplottables); // generate some data: int ncount = 100; qvector<double> x(ncount), y0(ncount), y1(ncount); // initialize with entries 0..100 for (int i = 0; i < ncount; ++i) { x[i] = i; // x goes from -1 to 1 y0[i] = qsin(i * 10.0f / ncount); //sin y1[i] = qcos(i * 10.0f / ncount); //cos } // create graph and assign data to it: qpen pen; int i = 1; qcpgraph *pgraph = m_customplot->addgraph(); // m_customplot->graph(0)->setdata(x, y0); pgraph->setname("sin曲线"); pgraph->setdata(x,y0); pgraph->setpen(qpen(qt::blue)); pgraph = m_customplot->addgraph(); // m_customplot->graph(0)->setdata(x, y0); pgraph->setname("cos曲线"); pgraph->setdata(x,y1); pgraph->setpen(qpen(qt::darkyellow)); // give the axes some labels: m_customplot->xaxis->setlabel("x"); m_customplot->yaxis->setlabel("y"); // set axes ranges, so we see all data: // m_customplot->xaxis->setrange(-1, 1); // m_customplot->yaxis->setrange(0, 1); m_customplot->rescaleaxes(true); m_customplot->replot();
4 效果图
本程序的源码下载地址: https://github.com/xiongxw/xcustomplot.git
参考:
上一篇: 图解分布式系统架构演进之路