Qwt開發筆記(二):Qwt基礎框架介紹、折線圖介紹、折線圖Demo以及程式碼詳解

2022-12-06 21:00:28

前言

  QWT開發筆記系列整理集合,這是目前使用最為廣泛的Qt圖表類(Qt的QWidget程式碼方向只有QtCharts,Qwt,QCustomPlot),使用多年,系統性的整理,本系列旨在系統解說並逐步更新其各種Demo範例
  本片文章主要講解折線圖,藉助折線圖展現一個基礎流程框架。

 

Demo

  請新增圖片描述
  請新增圖片描述
  在這裡插入圖片描述

 

QwtPlot

簡介

  QwtPlot是一個用於繪製二維圖形的小部件。畫布上可以顯示無限數量的繪圖專案。繪圖專案可以是曲線(QwtPlotCurve)、標記(QwtPlotMarker)、網格(QwtPrintGrid)或從QwtPlotItem派生的任何其他內容。一個繪圖最多可以有四個軸,每個繪圖專案都連線到一個x軸和一個y軸。軸上的比例可以顯式設定(QwtScaleDiv),或使用演演算法(QwtScale Engine)根據繪圖項計算,該演演算法可以為每個軸單獨設定。

列舉

enum QwtPlot::LegendPosition:圖例相對於畫布的位置。

  此列舉描述圖表中啟用的動畫。
  在這裡插入圖片描述

成員函數(列舉常用的)

autoReplot():自動重新整理

bool QwtPlot::autoReplot () const				

  如果設定了autoReplot選項,則為true。

axisAutoScale():自動縮放

bool QwtPlot::axisAutoScale(QwtAxisId axisId) const

  如果啟用了自動縮放,則為true。

axisFont():軸的刻度標籤的字型


QFont QwtPlot::axisFont (QwtAxisId axisId) const

  返回指定軸刻度的字型。

axisInterval():軸的當前間隔

QwtInterval QwtPlot::axisInterval(QwtAxisId axisId) const

  返回指定軸的當前間隔。同axiscaleDiv(axisId)->interval()類似。

axisMaxMajor():軸的最大主刻度數


int QwtPlot::axisMaxMajor(QwtAxisId axisId) const

  指定軸的最大主刻度數。

axisMaxMinor():軸的最大次刻度數

int QwtPlot::axisMaxMinor (QwtAxisId axisId) const

  指定軸的最大次刻度數。

axisScaleDiv():軸的比例分割

const QwtScaleDiv & QwtPlot::axisScaleDiv(QwtAxisId axisId) const

  返回指定軸的比例分割。
  axiscaleDiv(axisId).lowerBound()和axiscaleDiv(axisId).upperBound()是軸比例的當前限制。

axisScaleDraw():軸的比例繪製

QwtScaleDraw * QwtPlot::axisScaleDraw(QwtAxisId axisId)
const QwtScaleDraw * QwtPlot::axisScaleDraw (QwtAxisId axisId) const

  返回指定軸的比例繪製。

axisScaleEngine():軸的縮放引擎

QwtScaleEngine * QwtPlot::axisScaleEngine(QwtAxisId axisId)
const QwtScaleEngine * QwtPlot::axisScaleEngine(QwtAxisId axisId) const

  返回軸的縮放引擎

axisStepSize():軸的步長

double QwtPlot::axisStepSize(QwtAxisId axisId) const

  返回在setAxisScale中設定的步長引數。這不一定是當前比例的步長。

axisTitle():軸標題

QwtText QwtPlot::axisTitle(QwtAxisId axisId) const

axisWidget():縮放控制元件

QwtScaleWidget * QwtPlot::axisWidget(QwtAxisId axisId)
const QwtScaleWidget * QwtPlot::axisWidget(QwtAxisId axisId) const

  指定軸的縮放控制元件,如果axisId無效,則為NULL。

canvasBackground():畫布背景


QBrush QwtPlot::canvasBackground() const

  繪圖區域的背景畫筆。

canvasMap():畫布貼圖


  QwtScaleMap QwtPlot::canvasMap(QwtAxisId axisId) const virtual

  返回畫布上軸的貼圖。使用該貼圖,畫素座標可以轉換為繪圖座標,反之亦然。

drawCanvas():重繪畫布


void QwtPlot::drawCanvas(QPainter * painter) virtual

  重新繪製畫布。drawCanvas呼叫同樣用於列印的drawItems。喜歡新增單個繪圖項的應用程式更好地過載drawItems()。

drawItems():重繪指定區域畫布元素


void QwtPlot::drawItems(QPainter * painter, 
                     const QRectF &canvasRect,
                     const QwtScaleMap 
                     maps[QwtAxis::AxisPositions]) const virtual

  通常canvasRect是繪圖畫布的contentsRect()。由於Qt中的錯誤,此矩形對於某些框架樣式(例如QFrame::Box)可能是錯誤的,可能需要使用QWidget::setContentsMargins()手動修復邊距

footer():頁尾文字

QwtText QwtPlot::footer() const

  頁尾文字

footerLabel():頁尾標籤


QwtTextLabel * QwtPlot::footerLabel()
const QwtTextLabel * QwtPlot::footerLabel() const

  頁尾標籤小部件。

getCanvasMarginsHint():獲取畫布邊距

void QwtPlot::getCanvasMarginsHint(const QwtScaleMap maps[], 
                               const QRectF & canvasRect, 
                               double & left, 
                               double & top, 
                               double & right, 
                               double & bottom) const virtual

  計算畫布邊距。

insertLegend():插入圖例

void QwtPlot::insertLegend(QwtAbstractLegend * legend,
                        QwtPlot::LegendPosition pos = wtPlot::RightLegend, 
                        double ratio = -1.0)

  插入圖例。
  如果位置圖例是QwtPlot::LeftLegend或QwtPlot::RightLegend,則圖例將從上到下組織在一列中。否則,圖例項將放置在從左到右具有最佳列數的表中。
insertLegend()將把繪圖小部件設定為圖例的父項。圖例將在繪圖的解構函式中刪除,或在插入另一個圖例時刪除。
  未插入繪圖小部件佈局的圖例需要連線到legendDataChanged()訊號。呼叫updateLegend()將為初始更新啟動此訊號。當應用程式程式碼想要實現自己的佈局時,也需要將繪圖渲染到檔案中。

invTransform():軸座標轉換

double QwtPlot::invTransform(QwtAxisId axisId, double pos ) const

  將繪圖區域中位置的x或y座標轉換為值。

isAxisValid():軸有效

bool QwtPlot::isAxisValid (QwtAxisId axisId) const

isAxisVisible():軸可見

bool isAxisVisible (QwtAxisId) const

replot():重繪

void QwtPlot::replot() virtual slot 
 

QwtPlotGrid

簡介

  繪製座標網格的類。
  QwtPlotGrid類可用於繪製座標網格。座標軸網由主要和次要的垂直和水平軸網線組成。格線的位置由X和Y比例劃分確定,可以使用setXDiv()和setYDiv()指定。draw()成員在邊界矩形內繪製網格。

成員函數(列舉常用的)

enableX():啟用或禁用垂直格線。

void QwtPlotGrid::enableX(bool on)

enableY():啟用或禁用水平格線。

void QwtPlotGrid::enableY(bool on)

enableXMin():啟用或禁用次要垂直格線。

void QwtPlotGrid::enableXMin(bool on)

enableYMin():啟用或禁用次要水平格線。

void QwtPlotGrid::enableYMin(bool on)

majorPen():主要格線的筆

const QPen & QwtPlotGrid::majorPen() const

minorPen():用於次要格線的筆

const QPen & QwtPlotGrid::minorPen() const
 

QwtLegend

簡介

  圖例小部件。
  QwtLegend小部件是圖例項的表格排列。圖例項可以是任何型別的小部件,但通常它們都是QwtLegendLabel。
  可設定只顯示、顯示可選擇,顯示可點選並丟擲對應的訊號。
  使用時,直接new即可,沒啥可說的。

 

QwtSymbol

簡介

  用於繪製符號的類,作用於實際資料點。

列舉

enum QwtSymbol::Style:繪製的資料點符號樣式

  在這裡插入圖片描述

成員函數(列舉常用的)

setStyle() :設定點樣式(配合上面列舉Style)

void QwtSymbol::setStyle (QwtSymbol::Style style)

setSize():設定點大小

void QwtSymbol::setSize(const QSize & size)
void QwtSymbol::setSize(int	width, int height = -1)		

  指定符號的大小。
  如果「h」引數被忽略或小於0,而「w」引數大於或等於0,則符號大小將設定為(w,w)。

 

QwtPlotCurve

  (注意:本文只列Demo舉用到的,下一篇文章將會著重講解QwtPlotCurve類)

簡介

  表示一系列點的繪圖項。
  曲線是x-y平面中一系列點的表示。它支援不同的顯示樣式、插值(例如樣條曲線)和符號。

指定曲線特性

  建立曲線時,將設定為使用QwtPlotCurve::lines樣式繪製黑色實線,而不使用符號。您可以通過呼叫setPen()、setStyle()和setSymbol()來更改此符號,使用QwtSymbol類來實現。

連線/分配資料

  QwtPlotCurve使用QwtSeriesData物件獲取其點,該物件為點的實際儲存提供橋樑(如QAbstractItemModel)。
  有幾個從QwtSeriesData派生的便利類,它們也在內部儲存點(如QStandardItemModel)。
  QwtPlotCurve還提供了setSamples()的一些變體,它們從內部陣列構建QwtSeriesData物件。

將曲線附加到繪圖

  將QwtPlotItem附加到QwtPlot。它將首先將QwtPlotItem從任何繪圖中分離出來(如果需要)。如果傳遞了NULL引數,它將從它所附加的任何QwtPlot分離。(可以判斷出,它只能附屬一個繪圖)

成員函數

setTitle():設定曲線的名稱

void QwtPlotItem::setTitle(const QString & title)	
void QwtPlotItem::setTitle(const QwtText & title)

setPen():設定曲線的畫筆

void QwtPlotCurve::setPen(const QColor & color,
                       qreal width = 0.0,
                       Qt::PenStyle style = Qt::SolidLine)
void QwtPlotCurve::setPen(const QPen & pen)

  指定畫筆,寬度,線型等。

setXAxis():關聯X軸

void QwtPlotItem::setXAxis(QwtAxisId axisId)

  該專案將根據其座標軸繪製。

setYAxis():關聯Y軸

void QwtPlotItem::setYAxis(QwtAxisId axisId)

  該專案將根據其座標軸繪製。

setRenderHint():設定曲線渲染模式

void QwtGraphic::setRenderHint(RenderHint hint,
                            bool on = true);

  只能設定QwtPlotItem::RenderAntialiased,然後引數為開啟或者關閉。

setSamples():設定曲線資料

void QwtPlotCurve::setSamples(const QVector< double > & xData,
                           const QVector< double > & yData)

  使用x和y陣列初始化資料(顯式共用)。

void QwtPlotCurve::setSamples(const double * xData,
                           const double * yData,
                           int size)		

  通過從指定的記憶體塊複製x和y值來設定資料。與setRawSamples()相反,此函數生成資料的「深度副本」。

attach():關聯曲線到繪圖

void QwtPlotItem::attach(QwtPlot * plot)

  將專案附著到繪圖。
  此方法將QwtPlotItem附加到QwtPlot引數。它將首先將QwtPlotItem從任何繪圖中分離出來(如果需要)。如果傳遞了NULL引數,它將從它所附加的任何QwtPlot分離。

 

Demo原始碼

LineChartWidget.h

#ifndef LINECHARTWIDGET_H
#define LINECHARTWIDGET_H

#include <QWidget>
#include <QTimer>

#include "qwt.h"
#include "qwt_plot.h"
#include "qwt_plot_grid.h"
#include "qwt_legend.h"
#include "qwt_plot_curve.h"
#include "qwt_symbol.h"
#include "qwt_spline_curve_fitter.h"

namespace Ui {
class LineChartWidget;
}

class LineChartWidget : public QWidget
{
    Q_OBJECT

public:
    explicit LineChartWidget(QWidget *parent = 0);
    ~LineChartWidget();

public:
    QColor getBackgroundColor() const;

public:
    void setBackgroundColor(const QColor &backgroundColor);

protected:
    void initControl();
    void initQwtPlot();

protected slots:
    void slot_addDataTimeOut();

protected:
    void resizeEvent(QResizeEvent *event);
    void timerEvent(QTimerEvent *event);

private:
    Ui::LineChartWidget *ui;

private:
    int _timerId;
    qint64 _startTime;              // 啟動時間

private:
    QColor _backgroundColor;        // 背景顏色

private:
    QwtPlot *_pQwtPlot;             // qwt圖

    QwtPlotGrid *_pGrid;            // 虛線框
    QwtLegend *_pLegend;            // 圖例

    QwtPlotCurve *_pCurve1;         // 曲線1
    QwtSymbol *_pSymbol1;           // 曲線1點符號

    QwtPlotCurve *_pCurve2;         // 曲線2

    QVector<double> _vectorX1;      // 曲線1快取資料
    QVector<double> _vectorY1;      // 曲線1快取資料

    QVector<double> _vectorX2;      // 曲線2快取資料
    QVector<double> _vectorY2;      // 曲線2快取資料

    double _rangeX;                 // 範圍

    QTimer *_pTimerAddData;         // 新增資料定時器
};

#endif // LINECHARTWIDGET_H

LineChartWidget.cpp

#include "LineChartWidget.h"
#include "ui_LineChartWidget.h"

#include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")

LineChartWidget::LineChartWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::LineChartWidget),
    _pQwtPlot(0),
    _pGrid(0),
    _pLegend(0),
    _pCurve1(0),
    _pSymbol1(0),
    _pCurve2(0),
    _timerId(-1),
    _pTimerAddData(0),
    _rangeX(10)
{
    ui->setupUi(this);

    // 背景透明,在介面構架時,若為本視窗為其他視窗提升為本視窗時,
    // 則再qss會在主視窗第一級新增frame_all,防止其他視窗提升本視窗而沖掉qss設定
//    setWindowFlag(Qt::FramelessWindowHint);
//    setAttribute(Qt::WA_TranslucentBackground, true);

    initControl();
    initQwtPlot();

    _startTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
    _timerId = startTimer(16);

    timerEvent(0);
}

LineChartWidget::~LineChartWidget()
{
    delete ui;
}

void LineChartWidget::initControl()
{
    _pTimerAddData = new QTimer(this);
    connect(_pTimerAddData, SIGNAL(timeout()),
            this, SLOT(slot_addDataTimeOut()));
    _pTimerAddData->setInterval(1000);
    _pTimerAddData->start();
}

void LineChartWidget::initQwtPlot()
{
    _pQwtPlot = new QwtPlot(this);

    // 設定背景色
    _pQwtPlot->setCanvasBackground(QBrush(QColor(255, 255, 255)));

    // 設定x1座標軸
    {
        _pQwtPlot->setAxisTitle(QwtPlot::xBottom, "x");
        _pQwtPlot->setAxisAutoScale(QwtPlot::xBottom, true);
    }

    // 設定y1座標軸
    {
        _pQwtPlot->setAxisTitle(QwtPlot::yLeft, "y");
        _pQwtPlot->setAxisAutoScale(QwtPlot::yLeft, true);
    }

    // 設定y2座標軸
    {
        _pQwtPlot->setAxisTitle(QwtPlot::yRight, "y");
        _pQwtPlot->setAxisVisible(QwtPlot::yRight, true);
        _pQwtPlot->setAxisAutoScale(QwtPlot::yRight, true);
    }

    // 設定繪圖區域網格
    {
        _pGrid = new QwtPlotGrid();
        _pGrid->setMajorPen(Qt::gray, 1, Qt::SolidLine);
        _pGrid->attach(_pQwtPlot);
    }

    // 設定圖例
    {
        _pLegend = new QwtLegend();
        _pLegend->setDefaultItemMode(QwtLegendData::ReadOnly);
        _pQwtPlot->insertLegend(_pLegend);
    }
    // 設定曲線1
    {
        _pCurve1 = new QwtPlotCurve();
        _pCurve1->setTitle("y1");
        _pCurve1->setPen(Qt::blue, 1);
        _pCurve1->setRenderHint(QwtPlotItem::RenderAntialiased, true);
        // 關聯軸
        _pCurve1->setXAxis(QwtPlot::xBottom);
        _pCurve1->setYAxis(QwtPlot::yLeft);
    }

    // 曲1符號物件
    {
        _pSymbol1 = new QwtSymbol(QwtSymbol::Ellipse);
        _pSymbol1->setPen(Qt::red);
        _pSymbol1->setBrush(Qt::red);
        _pSymbol1->setSize(2);
        _pCurve1->setSymbol(_pSymbol1);
    }

    // 曲線1資料
    {
        // 這是採用顯示共用的方式
        _pCurve1->setSamples(_vectorX1, _vectorY1);
        // 將曲線新增到繪圖
        _pCurve1->attach(_pQwtPlot);
    }

    // 曲線2
    {
        _pCurve2 = new QwtPlotCurve();
        _pCurve2->setTitle("y2");
        _pCurve2->setPen(Qt::red, 1);
        _pCurve2->setRenderHint(QwtPlotItem::RenderAntialiased, true);
        // 關聯軸
        _pCurve2->setXAxis(QwtPlot::xBottom);
        _pCurve2->setYAxis(QwtPlot::yRight);
    }

    // 曲線2資料
    {
        // 這是採用顯示共用的方式
        _pCurve2->setSamples(_vectorX2, _vectorY2);
        // 將曲線新增到繪圖
        _pCurve2->attach(_pQwtPlot);
    }


}

void LineChartWidget::slot_addDataTimeOut()
{
    // 計算時間
    double currentTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
    currentTime -= _startTime;
    currentTime /= 1000;
    // 新增曲線1資料
    _vectorX1.append(currentTime);
    _vectorY1.append(qrand() % 20 - 10);
    // 新增曲線2資料
    _vectorX2.append(currentTime);
    _vectorY2.append(qrand() % 50);
    // 資料置換(不知換資料進不去,顯示共用無效?)
    _pCurve1->setSamples(_vectorX1, _vectorY1);
    _pCurve2->setSamples(_vectorX2, _vectorY2);
}

void LineChartWidget::resizeEvent(QResizeEvent *event)
{
    if(_pQwtPlot)
    {
        _pQwtPlot->setGeometry(rect());
    }
    QWidget::resizeEvent(event);
}

void LineChartWidget::timerEvent(QTimerEvent *event)
{
    // 計算時間
    double currentTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
    currentTime -= _startTime;
    currentTime /= 1000;
    if(currentTime < 10.0)
    {
        _pQwtPlot->setAxisScale(QwtPlot::xBottom, 0, _rangeX, 1);
    }else{
        _pQwtPlot->setAxisScale(QwtPlot::xBottom, currentTime - _rangeX, currentTime, 1);
    }

    _pQwtPlot->replot();
}

QColor LineChartWidget::getBackgroundColor() const
{
    return _backgroundColor;
}

void LineChartWidget::setBackgroundColor(const QColor &backgroundColor)
{
    _backgroundColor = backgroundColor;
}

 

Demo工程模板v1.1.0

  在這裡插入圖片描述

 

入坑

入坑一:使用顯示setSample關聯QVector失敗

問題

  顯示共用理解為共用資料,但是資料不繪製到圖。
  在這裡插入圖片描述

嘗試

  檢視曲線是否有更新資料到圖的實現,是沒有的。
  直接設定可重新整理資料

解決

  直接setSample一次來解決。
  在這裡插入圖片描述