1. 程式人生 > >QtCharts的簡單用法(2)--Qt

QtCharts的簡單用法(2)--Qt

前言

很多時候,我們用QtCharts不僅僅只是顯示個線條趨勢什麼的,還需要一些小的功能,比如只顯示某條線條,再比如讓動態的線條停止滾動等。所以這篇部落格就是介紹的這些東西,碎而雜。

簡述

本文主要介紹如下幾個功能:

(1)靜態模式(資料依舊接收,但線條靜止;當退出當前模式,線條動態,並將之前的資料刷上去)

(2)橫座標為日期時間,如2018-06-25 14:35:45:000

(3)浮標的顯示(跟示波器相似)

示例

(1)靜態模式:只要將接收到的資料快取下(即找個容器存放下),退出此模式再刷上去就可以了。下面的程式碼有考慮上下限,可能有些地方不太完善。

private:
    Ui::MainWindow *ui;

    QChart *m_chart;
    QDateTimeAxis* m_axisX;
    QValueAxis* m_axisY;
    QLineSeries* m_series;
    QTimer m_timer;
    QDateTime m_maxDateTime;    //橫軸時間最大值
    QGraphicsLineItem* m_upperLineItem;
    QGraphicsLineItem* m_lowerLineItem;

    bool staticModelFlag;      //靜態模式biaoshi標識
    QList<QPointF> m_points;  //裝靜態時產生的資料
    int m_seriesMaxSize;      //series的資料容量
void MainWindow::handleTimeout()
{
    qreal y=qrand() % 50;   //隨機產生的數值
    QDateTime currentDateTime=QDateTime::currentDateTime();

    QPointF point(QDateTime::currentDateTime().toMSecsSinceEpoch(),y);


    if(staticModelFlag)
    {//靜態模式
        m_points.push_back(point);
    }else{
        //非靜態模式
        QList<QPointF> points=m_series->points();

        qreal max=m_axisY->max();

        for(int i=0;i<m_points.size();i++)
        {
            if(m_points[i].y()>max)
                max=m_points[i].y();
        }

        if(!m_points.isEmpty()){
            points.append(m_points);
            m_points.clear();
        }

        points.append(point);

        //清除多餘資料
        while(points.size()>m_seriesMaxSize)
        {
            points.pop_front();
        }

        m_series->replace(points);

        //重置x軸範圍
        if(currentDateTime>m_axisX->max())
        {
            QDateTime max=m_axisX->max();
            if(max.toTime_t()==0)
            {
                m_maxDateTime=currentDateTime.addSecs(60);
                m_axisX->setRange(currentDateTime,m_maxDateTime);
            }else{
                m_maxDateTime=currentDateTime;
                m_axisX->setRange(m_maxDateTime.addSecs(-60),m_maxDateTime);
            }


        }

        //重置y軸範圍
        m_axisY->setMax(qMax(max,y));
    }

}

(2)橫座標為日期時間。這個功能很簡單,修改下橫軸就可以:將橫軸從QValueAxis修改成QDateTimeAxis。

    m_axisX=new QDateTimeAxis;
    m_axisX->setTickCount(4);
    m_axisX->setFormat("yyyy-MM-dd hh:mm:ss:zzz"); //設定格式

    m_chart->addAxis(m_axisX, Qt::AlignBottom);
    m_series->attachAxis(m_axisX);

    m_axisY=new QValueAxis;
    m_axisY->setTickCount(11);
    m_axisY->setRange(0,40);
    m_axisY->setLabelFormat("%i");
    m_chart->addAxis(m_axisY,Qt::AlignLeft);
    m_series->attachAxis(m_axisY);

下面給出以上兩個小功能,結合在一起的介面效果。

(3)浮標的顯示。這個功能我參考了Qt自帶的示例Callout。這個部分將QChart與影象框架部分結合了起來。用到了類QGraphicsItem,QGraphicsLineItem,QGraphicsScene等。

浮標程式碼:

#ifndef CALLOUT_H
#define CALLOUT_H

#include <QtCharts/QChartGlobal>
#include <QtWidgets/QGraphicsItem>
#include <QtGui/QFont>
#include<QChart>

QT_CHARTS_USE_NAMESPACE

class CallOut : public QGraphicsItem
{
public:
    CallOut(QChart *parent);

    void setText(const QString &text);
    void setAnchor(QPointF point);
    void updateGeometry();

    QRectF boundingRect() const;

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);

private:
    QString m_text;
    QRectF m_textRect;
    QRectF m_rect;
    QPointF m_anchor;
    QFont m_font;
    QChart *m_chart;


};

#endif // CALLOUT_H
#include "callout.h"

#include <QPainter>

CallOut::CallOut(QChart *chart):
    QGraphicsItem(chart),
    m_chart(chart)
{

}

void CallOut::setText(const QString &text)
{
    m_text=text;

    QFontMetrics metrics(m_chart->font());
    m_textRect = metrics.boundingRect(QRect(0, 0, 150, 150), Qt::AlignLeft, m_text);
    m_textRect.translate(5, 5);
    prepareGeometryChange();
    m_rect = m_textRect.adjusted(-5, -5, 5, 5);

}

void CallOut::setAnchor(QPointF point)
{
    m_anchor=point;
}

void CallOut::updateGeometry()
{
    prepareGeometryChange();
    QPointF anchor;
    anchor.setX(-m_rect.width()/2+m_anchor.x());
    anchor.setY(m_chart->plotArea().height()/2);
    setPos(anchor);
}

QRectF CallOut::boundingRect() const
{
    QPointF anchor = mapFromParent(m_anchor);
    QRectF rect;
    rect.setLeft(qMin(m_rect.left(), anchor.x()));
    rect.setRight(qMax(m_rect.right(), anchor.x()));
    rect.setTop(qMin(m_rect.top(), anchor.y()));
    rect.setBottom(qMax(m_rect.bottom(), anchor.y()));
    return rect;
}

void CallOut::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)
    QPainterPath path;
    path.addRoundedRect(m_rect,5,5);

    //path=path.simplified();

    painter->setBrush(QColor(93,93,93,90));
    painter->setPen(QPen(QColor(93,93,93,75)));
    painter->drawPath(path);
    painter->setPen(QPen(Qt::white));
    painter->drawText(m_textRect,m_text);


}

檢視:

#ifndef CHARTVIEW_H
#define CHARTVIEW_H

#include<QChartView>
#include<QGraphicsView>
#include<QMouseEvent>
#include<QWheelEvent>
#include<QChart>
#include <QtWidgets/QGraphicsScene>
#include <QtCharts/QChart>
#include <QtCharts/QLineSeries>
#include <QtCharts/QSplineSeries>
#include<QGraphicsLineItem>
#include"callout.h"
#include"chart.h"


QT_CHARTS_USE_NAMESPACE

class ChartView : public QChartView
{
    Q_OBJECT
public:
    ChartView(QWidget *parent = 0);

    qreal getYValue(QPointF p1,QPointF p2,qreal x);


protected:

    bool viewportEvent(QEvent *event)override;
    void mousePressEvent(QMouseEvent *event)override;
    void mouseMoveEvent(QMouseEvent *event)override;
    void mouseReleaseEvent(QMouseEvent *event)override;



public slots:
    void connectMarkers();
    void handleMarkerClicked();


private:
    QGraphicsLineItem* m_LineItem;
    QGraphicsLineItem* m_topLineItem; //上限
    QGraphicsLineItem* m_lowLineItem; //下限
    CallOut* m_tooltip;
    Chart *m_chart ;
    bool m_isTouching;

public:
    bool m_isBuoyShow;

};

#endif // CHARTVIEW_H
#include "chartview.h"
#include<QDebug>
#include<QGraphicsView>
#include <QLegendMarker>
#include <QQueue>
#include <QtCore/QtMath>
#include<QApplication>
#include<QCategoryAxis>

#if _MSC_VER >=1600
#pragma execution_character_set("utf-8")
#endif

ChartView::ChartView(QWidget *parent) :
    QChartView(parent),
    m_tooltip(0),
    m_isTouching(false),
    m_isBuoyShow(false),
    m_topLineItem(0),
    m_lowLineItem(0)
{

    m_chart=new Chart;


    QLineSeries *series = new QLineSeries;
    series->append(1, 3);
    series->append(4, 5);
    series->append(5, 4.5);
    series->append(7, 1);
    series->append(11, 2);
    series->setName("引數一");
    m_chart->addSeries(series);

    QSplineSeries *series2 = new QSplineSeries;
    series2->append(1.6, 1.4);
    series2->append(2.4, 3.5);
    series2->append(3.7, 2.5);
    series2->append(7, 4);
    series2->append(10, 2);
    series2->setName("引數二");
    m_chart->addSeries(series2);

    m_chart->setTitle("Zoom in/out example");
    //m_chart->setAnimationOptions(QChart::SeriesAnimations);
    //m_chart->legend()->setAlignment(Qt::AlignRight);
    m_chart->createDefaultAxes();

    this->setChart(m_chart);

    m_LineItem = new QGraphicsLineItem();

    QPen pen=m_LineItem->pen();
    pen.setColor(QColor(93,93,93,75));
    pen.setWidth(3);
    m_LineItem->setPen(pen);

    this->scene()->addItem(m_LineItem);
    m_LineItem->hide();

    connectMarkers();

    this->setRubberBand(QChartView::RectangleRubberBand);


}

qreal ChartView::getYValue(QPointF p1, QPointF p2, qreal x)
{
    qreal y=(p2.y()-p1.y())/(p2.x()-p1.x())*(x-p1.x())+p1.y();
    return y;

}

bool ChartView::viewportEvent(QEvent *event)
{
    if (event->type() == QEvent::TouchBegin) {

        m_isTouching = true;

        chart()->setAnimationOptions(QChart::NoAnimation);
    }
    return QChartView::viewportEvent(event);
}

void ChartView::mousePressEvent(QMouseEvent *event)
{
    if (m_isTouching)
        return;
    QChartView::mousePressEvent(event);
}

void ChartView::mouseMoveEvent(QMouseEvent *event)
{
    if (m_isTouching)
        return;

    QChartView::mouseMoveEvent(event);

    if(m_isBuoyShow){
        bool flag=m_chart->plotArea().contains(event->pos());
        if(flag)
        {//在座標軸中

            if (m_tooltip == 0)
                m_tooltip = new CallOut(m_chart);

            qreal pointX=m_chart->mapToValue(event->pos()).x();

            QList<QAbstractSeries *> seriesList=m_chart->series();
            QString text="";
            for(int i=0;i<seriesList.size();i++){

               QLineSeries* series=(QLineSeries*)seriesList.at(i);
               QList<QPointF> points=series->points();
               for(int j=0;j<points.size();j++){
                   QPointF curPoint=points.at(j) ;
                   if(curPoint.x()>pointX)
                   {
                       if(j-1>=0){
                           qreal pointY=getYValue(curPoint, points[j-1],pointX);
                           QPointF point(pointX,pointY);
                           text+=tr("X%1:%2 Y%1:%4 \n").arg(i).arg(point.x()).arg(point.y());
                       }
                       break;
                   }
               }

            }
             m_tooltip->setText(text);
             m_tooltip->setAnchor(event->pos());
             m_tooltip->setZValue(11);
             m_tooltip->updateGeometry();
             m_tooltip->show();

             m_LineItem->setLine(QLineF(event->pos().x(),0,event->pos().x(),this->rect().height()));
             m_LineItem->show();

        }else{
            if(m_tooltip!=0)
                m_tooltip->hide();
            m_LineItem->hide();
        }
    }

}

void ChartView::mouseReleaseEvent(QMouseEvent *event)
{
    if (m_isTouching)
        m_isTouching = false;

    // Because we disabled animations when touch event was detected
    // we must put them back on.
    chart()->setAnimationOptions(QChart::SeriesAnimations);

    QChartView::mouseReleaseEvent(event);
}



void ChartView::connectMarkers()
{
    foreach (QLegendMarker* marker, m_chart->legend()->markers()) {
        QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
        QObject::connect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
    }

}

void ChartView::handleMarkerClicked()
{

    QLegendMarker* marker = qobject_cast<QLegendMarker*> (sender());
    Q_ASSERT(marker);

    switch (marker->type())

    {
        case QLegendMarker::LegendMarkerTypeXY:
        {

        marker->setVisible(true);

        qreal alpha;

        QColor color;
        QPen pen = marker->pen();
        color = pen.color();

        if(color.alphaF()==1.0){
            //未點選
            alpha=0.2;

            QFont font=marker->font();
            font.setBold(true);
            marker->setFont(font);

        }else {
            //已點選
            alpha=1.0;

            QFont font=marker->font();
            font.setBold(false);
            marker->setFont(font);
        }

        QList<QAbstractSeries *> seriesList=m_chart->series();

        for(int i=0;i<seriesList.size();i++){

           QLineSeries* series=(QLineSeries*)seriesList.at(i);
           if(series!=marker->series()){
               QPen seriesPen=series->pen();
               QColor color=seriesPen.color();
               color.setAlphaF(alpha);
               seriesPen.setColor(color);
               series->setPen(seriesPen);


           }
        }

        color.setAlphaF(alpha);
        pen.setColor(color);
        marker->setPen(pen);



        break;
        }
    default:
        {
        qDebug() << "Unknown marker type";
        break;
        }
    }


}

介面效果:

結束語

最近沒時間上網,這篇寫的不是很好,幾乎是貼程式碼。將就,有時間進行調整。