欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

采用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 效果图

采用Qt快速绘制多条曲线(折线),跟随鼠标动态显示线上点的值(基于Qt的开源绘图控件QCustomPlot进行二次开发)

 
本程序的源码下载地址: https://github.com/xiongxw/xcustomplot.git


参考: