1、图的类型

不管什么类型的图,都是QChart类。决定图类型的是:数据系列的类型。各种数据系列类都继承自QAbstractSeries,常用的数据系列类如下图所示。本文主要研究折线图。

在 Grafana 中创建折线图 横坐标去掉时分秒 折线图横坐标改为年份_父类

2、折线图的坐标轴

折线图的坐标轴类型QValueAxis(数字)、QDateTimeAxis(时间),本质上,时间轴这种类型也是数值型,只不过这种类型能把数据系列的X值灵活的显示为想要时间格式(如2017.10:20或者20:03:12.456 等,格式可参考QDateTime),而QValueAxis格式的轴,只能把数据系列的X值原样显示。

//在ui中添加QGraphicView控件,并提升为QChartView类


QChart Chart;//图表
QLineSeries lineseries;//折线图数据系列
QDateTimeAxis dateAxisX;//时间类型轴(用作X轴)
QValueAxis *axisY;//数值类型轴(用作Y轴)

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);    

    //或者从1970年0:0:0到此时此刻的总ms数
    qint64 current_time_total_ms = QDateTime::currentMSecsSinceEpoch();
    lineseries.append(current_time_total_ms, 10);
    &lineseries << (current_time_total_ms+1, 5);
    lineseries.append(current_time_total_ms + 2, 8);

    Chart.addSeries(lineseries);  // 将 series 添加至图表中 


    dataAxisX.setFormat("mm:ss:zzz");//设置X轴数据的显示格式(格式串的写法请参考QDateTime::toString的帮助文档)
    Chart.setAxisX(&dataAxisX, lineseries);

    Chart.setAxisY(axisY);
    
    //下面是自己设置X轴的显示范围,参数必须转换为QDateTime。本例用的是,把从19700101的到此刻的ms数字转为QDateTime格式
    //Chart.axisX()->setRange(QDateTime::fromMSecsSinceEpoch(x_min),
    //                       QDateTime::fromMSecsSinceEpoch(x_max));
    

}

在 Grafana 中创建折线图 横坐标去掉时分秒 折线图横坐标改为年份_折线图_02

有运行结果可以看到,X轴的数据被转换成了时间样式显示了出来。如果我们把上述程序中X轴的类型更换为QValueAxis,那么X轴显示的数据就是上面程序中注释说到的毫秒数。如下图所示:

在 Grafana 中创建折线图 横坐标去掉时分秒 折线图横坐标改为年份_折线图_03

3、设置X轴的显示范围

可参考我的另一篇文章:《QT中图表类QChart 系列之(2):设置显示的区间

4、获取鼠标指向的点的坐标值

这个功能在QChartView类中,很简单,以继承并重写鼠标移动事件为例:

需要注意的是,重写moveEvent之后,框选放大功能会失效,这时可以在自己的重写函数中加上:
QChartView::mouseMoveEvent(e);//调用父类的重写方法

void QChartView_my::mouseMoveEvent(QMouseEvent *e)
{
    //打印鼠标位置处的点的坐标值
    qDebug()<< this->chart()->mapToValue(e->pos());//把鼠标坐标值转化为画出的图中的坐标
    //以防父类的实例接收不到mouseMoveEvent事件。
    QChartView::mouseMoveEvent(e);//调用父类的重写方法。本行程序不同于e->ignore(),ignor是把事件继续向父控件(一般其父控件就是ui界面)传递,而不是向父类传递
}

5、动态滚动

假设我们在采集AD数据,并实时显示AD的值,这时就希望折线图能实时动态滚动,要不然几千个点都挤在屏幕上就看不清了。

void MainWindow::on_pushButton_addPoint_clicked()
{
    static float new_x = 0;
    new_x += 0.3;
    float new_y = sin(new_x);    
    
    lineseries->append(new_x, new_y);//添加到数据系列中的点会被自动显示(如果该点处在显示区间的话)    
    
    QValueAxis *axisX = dynamic_cast<QValueAxis*>(lineChart->axisX());
    qreal cur_x_min = axisX->min();//当前X轴显示区间的最小x值
    qreal cur_x_max = axisX->max();

    if(new_x > cur_x_max)//新的x值处在X轴显示区的之外了
    {
        qreal error = new_x - cur_x_max;//X轴的显示区向右挪error距离即可把新x显示出来
        lineChart->axisX()->setRange(cur_x_min + error, cur_x_max + error);
    }
    else//还能显示出来,无需挪动显示区
    {
        
    }
    lineChart->axisY()->setRange(-2, 2);
}

效果如下图所示:可以发现,当chart能显示出新的点sh时,图像没有发生滚动,一旦点把图像填满了,图像就开始滚动了,也即下面图3的X坐标区间整体变大了。

另外,如果想让最新的点,不是显示在最右侧,而是距离最右侧留出一点距离(这样观察新的点会更清楚),那么可以把上面程序中的lineChart->axisX()->setRange(cur_x_min + error, cur_x_max + error);再加一点偏移就好了,例如:
lineChart->axisX()->setRange(cur_x_min + error + 5, cur_x_max + error + 5);

在 Grafana 中创建折线图 横坐标去掉时分秒 折线图横坐标改为年份_父类_04