QCustomplot使用分享(六) 座標軸和網格線

朝十晚八發表於2016-11-14

一、概述

    前邊已經寫了5篇對QCustomPlot的講解,看過上述的幾篇文章後,基本就能做一些簡單的使用了,但是如果想要做到高度的控制圖表,那麼座標軸將是很重要的一部分,因為座標軸就是圖表的一個參考系,沒有了參考系那麼一切都是天方夜譚。關於QCustomPlot的座標軸我還是會按照之前的套路,首先對比1.3.2版本和2.0.0beta版本,然後在深入的去分析座標軸使用。

二、歷史版本對比

    首先我需要和大傢伙說明下,我個人覺著在QCustomPlot的定製過程中,座標軸定製算是比較困難的,因為座標軸如果要定製的話,那就是座標軸的刻度需要自己計算,如果這個時候相關的業務邏輯也影響座標軸的計算,那麼就更難計算了,呵呵。。。或許大傢伙可能也不會遇到這些問題,有興趣的同學也可以自己思考下。

  1.3.2版本 2.0.0版本
座標軸
1、QCPAxis:座標軸類,所有座標軸功能都在這一個類總實現,包括:刻度計算和繪製文字
2、預設刻度自動計算,負責計算大刻度和小刻度
3、如果需要外部計算刻度則處理ticksRequest請求
1、QCPAxis:座標軸類,所有座標軸功能都在這一個類總實現,包括:刻度計算和繪製文字
2、預設刻度自動計算,負責計算大刻度和小刻度
3、如果需要外部計算刻度則處理ticksRequest請求

表1 1.3.2版本和2.0.0版本座標軸比較

    下面我將針對2.0.0版本的座標軸刻度計算來加以解釋,為了方便起見,我只解釋QCPAxisTicker這個座標軸刻度計算基類,因為QCPAxis座標軸預設使用的就是這個類,其餘的座標軸刻度計算類比如QCPAxisTickerDateTime、QCPAxisTickerTime、QCPAxisTickerFixed、QCPAxisTickerText、QCPAxisTickerPi和QCPAxisTickerLog等都是根據不同業務需求,重新實現了vitural相關方法。

三、座標軸

1、QCPAxis,如下是QCPAxis的標頭檔案,我從中刪除了大量不需要註釋的部分,但是很是剩下許多,為了寫註釋方便所以我沒有把程式碼摺疊,有興趣的同學可以閱讀下其中的中文註釋,其實這個類僅僅是用來連線計算和繪製座標軸的一個類,也可以說是暴露給使用者的一個匯出類。

  1 class QCP_LIB_DECL QCPAxis : public QCPLayerable
  2 {
  3     enum AxisType {//座標軸型別,在一個座標軸矩形QCPAxisRect中包含左、上、右和下四條座標軸
  4         atLeft = 0x01  ///< <tt>0x01</tt> Axis is vertical and on the left side of the axis rect
  5         , atRight = 0x02  ///< <tt>0x02</tt> Axis is vertical and on the right side of the axis rect
  6         , atTop = 0x04  ///< <tt>0x04</tt> Axis is horizontal and on the top side of the axis rect
  7         , atBottom = 0x08  ///< <tt>0x08</tt> Axis is horizontal and on the bottom side of the axis rect
  8     };
  9     enum LabelSide {//座標軸刻度上的文字的位置,刻度線裡or外
 10         lsInside    ///< Tick labels will be displayed inside the axis rect and clipped to the inner axis rect
 11         , lsOutside  ///< Tick labels will be displayed outside the axis rect
 12     };
 13     enum ScaleType {//座標軸型別,直線or對數線
 14         stLinear       ///< Linear scaling
 15         , stLogarithmic ///< Logarithmic scaling with correspondingly transformed axis coordinates (possibly also \ref setTicker to a \ref QCPAxisTickerLog instance).
 16     };
 17     enum SelectablePart {/座標軸可以被選中的部分
 18         spNone = 0      ///< None of the selectable parts
 19         , spAxis = 0x001  ///< The axis backbone and tick marks
 20         , spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)
 21         , spAxisLabel = 0x004  ///< The axis label
 22     };
 23 
 24     explicit QCPAxis(QCPAxisRect *parent, AxisType type);
 25     virtual ~QCPAxis();
 26   //所有的get介面已經被我刪除  看到對應的set介面,get介面的含義就不言而喻
 27     // setters:
 28     Q_SLOT void setScaleType(QCPAxis::ScaleType type);//設定座標軸型別  直線or對數
 29     Q_SLOT void setRange(const QCPRange &range);//設定座標軸範圍
 30     void setRange(double lower, double upper); 33     void setTicker(QSharedPointer<QCPAxisTicker> ticker);//設定座標軸計算刻度類,該引數是一個shared型智慧指標,因此可以被多個座標軸來同時使用
 31     void setTicks(bool show);//是否顯示座標軸,如果不顯示座標軸,那麼網格線也就沒有啦,因為沒有了座標軸刻度
 32     void setTickLabels(bool show);//是否顯示座標軸文字
 33     void setTickLabelPadding(int padding);//設定座標走文字距離座標軸線距離
 34     void setTickLabelFont(const QFont &font);//設定文字字型
 35     void setTickLabelColor(const QColor &color);//設定文字顏色
 36     void setTickLabelRotation(double degrees);//設定文字角度
 37     void setTickLabelSide(LabelSide side);//設定文字在刻度線裡or外
 38     void setNumberFormat(const QString &formatCode);//設定文字格式
 39     void setNumberPrecision(int precision);//設定文字精度
 40     void setTickLength(int inside, int outside = 0);//設定大刻度高度
 41     void setTickLengthIn(int inside);//設定大刻度在裡邊長度
 42     void setTickLengthOut(int outside);//設定大刻度在外邊長度
 43     void setSubTicks(bool show);//設定是否顯示小刻度
 44     void setSubTickLength(int inside, int outside = 0);//設定小刻度高度
 45     void setSubTickLengthIn(int inside);//設定小刻度在座標軸線裡邊長度
 46     void setSubTickLengthOut(int outside);//設定小刻度在座標軸線外邊長度
 47     void setBasePen(const QPen &pen);//設定基礎線畫筆  基礎線是零線,即可以認為是座標軸刻度為0的線
 48     void setTickPen(const QPen &pen);//設定大刻度畫筆
 49     void setSubTickPen(const QPen &pen);//設定小刻度畫筆
 50     void setLabelFont(const QFont &font);//設定座標軸名稱字型畫筆
 51     void setLabelColor(const QColor &color);//設定座標軸名稱字型顏色
 52     void setLabel(const QString &str);//設定座標軸名稱文字
 53     void setLabelPadding(int padding);//設定座標軸名稱文字距離座標軸刻度線距離
 54     void setPadding(int padding);//設定座標軸距離邊界距離
 55     void setOffset(int offset);//設定偏移量
 56     void setSelectedTickLabelFont(const QFont &font);//設定選中刻度文字時字型
 57     void setSelectedLabelFont(const QFont &font);//設定選中座標軸名稱時字型
 58     void setSelectedTickLabelColor(const QColor &color);//選中刻度文字時顏色
 59     void setSelectedLabelColor(const QColor &color);//選中座標軸名稱時顏色
 60     void setSelectedBasePen(const QPen &pen);//選中基礎線時畫筆
 61     void setSelectedTickPen(const QPen &pen);//選中大刻度時畫筆
 62     void setSelectedSubTickPen(const QPen &pen);//選中小刻度時畫筆
 63     Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts);//設定能選中項的型別
 64     Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts);
 65     void setLowerEnding(const QCPLineEnding &ending);//設定座標軸小刻度端樣式
 66     void setUpperEnding(const QCPLineEnding &ending);//座標軸大刻度端樣式 73 
 67     // non-property methods:
 68     Qt::Orientation orientation() const { return mOrientation; }//座標軸朝向
 69     int pixelOrientation() const { return rangeReversed() != (orientation() == Qt::Vertical) ? -1 : 1; }
 70     void moveRange(double diff);//移動座標軸
 71     void scaleRange(double factor);//按比例因子縮放
 72     void scaleRange(double factor, double center);//按範圍縮放 81     void rescale(bool onlyVisiblePlottables = false);//重新適配座標軸範圍
 73     double pixelToCoord(double value) const;//畫素到座標軸座標系
 74     double coordToPixel(double value) const;//座標軸座標系到畫素 85     QList<QCPAbstractPlottable*> plottables() const;//所有的圖
 75     QList<QCPGraph*> graphs() const;//所有的折線
 76     QList<QCPAbstractItem*> items() const;//所有的示意項 92 
 77 protected:
 78     AxisType mAxisType;//座標軸型別
 79     QCPAxisRect *mAxisRect;//座標軸所在矩形116 
 80     // non-property members:
 81     QCPGrid *mGrid;//網格120     QSharedPointer<QCPAxisTicker> mTicker;//座標軸刻度計算類
 82     QVector<double> mTickVector;//大刻度
 83     QVector<QString> mTickVectorLabels;//大刻度文字
 84     QVector<double> mSubTickVector;//小刻度
 85     bool mCachedMarginValid;
 86     int mCachedMargin;
 87 
 88     // introduced virtual methods:
 89     virtual int calculateMargin();
 90 
 91     // reimplemented virtual methods:
 92     virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;//獲取預設的反鋸齒屬性
 93     virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;//畫座標軸
 94     virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;//選擇策略137 
 95     // non-virtual methods:
 96     void setupTickVectors();//計算刻度
 97     QPen getBasePen() const;//獲取基礎畫筆
 98     QPen getTickPen() const;//獲取大刻度畫筆
 99     QPen getSubTickPen() const;//獲取小刻度畫筆
100     QFont getTickLabelFont() const;//獲取刻度文字畫筆
101     QFont getLabelFont() const;//獲取座標軸名稱文字字型
102     QColor getTickLabelColor() const;//獲取大刻度文字顏色
103     QColor getLabelColor() const;..獲取座標軸名稱文字顏色
104 };

     具體的繪製類其實是QCPAxisPainterPrivate類,這是一個私有類,從名字就可以看出,他是一個QCPAxis類的繪製私有類,事實確實如此。刻度計算類是QCPAxisTicker,這是一個刻度計算基類,也是QCPAxis預設使用的刻度計算類,當然了這個類還有一大堆子類,都是專門用於生成指定型別的座標軸。

2、QCPAxisTicker:刻度計算類,該類完成了大刻度、小刻度和大刻度文字的計算,供QCPAxis來呼叫繪製,其中generate方法是一個公有的虛方法,既可以被重寫,又可以被外部呼叫,QCPAxis座標軸就是呼叫該介面來重新計算刻度。

 1 class QCP_LIB_DECL QCPAxisTicker
 2 {
 3     Q_GADGET
 4 public:
 5     enum TickStepStrategy//刻度生成策略
 6     {
 7         tssReadability    ///< A nicely readable tick step is prioritized over matching the requested number of ticks (see \ref setTickCount)
 8         , tssMeetTickCount ///< Less readable tick steps are allowed which in turn facilitates getting closer to the requested tick count
 9     };
10 
11     QCPAxisTicker();
12     virtual ~QCPAxisTicker();
13 
14     // setters:
15     void setTickStepStrategy(TickStepStrategy strategy);//設定刻度生成策略
16     void setTickCount(int count);//設定大刻度個數 有可能計算出來的刻度數不完全等於設定的刻度個數,取決於刻度生成策略
17     void setTickOrigin(double origin);//設定座標軸領刻度
18 
19     // introduced virtual methods:
20     virtual void generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels);
21 
22 protected:
23     // introduced virtual methods:
24     virtual double getTickStep(const QCPRange &range);//根據座標軸範圍計算步長
25     virtual int getSubTickCount(double tickStep);//根據步長計算自刻度個數
26     virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision);//根據指定語言、文字格式和精度獲取文字
27     virtual QVector<double> createTickVector(double tickStep, const QCPRange &range);//生成大刻度
28     virtual QVector<double> createSubTickVector(int subTickCount, const QVector<double> &ticks);//生成小刻度
29     virtual QVector<QString> createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision);//生成刻度文字
30 
31     // non-virtual methods:
32     void trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const;//去除無效的刻度值
33     double pickClosest(double target, const QVector<double> &candidates) const;//該函式返回範圍內第一個不小於(大於或等於)指定target的值。
34 };

四、網格線

    QCPGrid網格線,這個算是和QCPAxis座標軸類似的實現,和其他模組關係基本都不是很大,直接繼承自QCPLayerable,標頭檔案格式如下,同樣的,我刪除了其中無需註釋的一部分程式碼。

    在QCustomPlot的原始碼設計中,一個QCPAxis座標軸對於一個QCPGrid,這同我之前理解的圖表繪製有些不大一樣,呵呵呵。。。但是QCustomPlot就是這麼幹了,如果想對網格線做一些控制,有時候從QCPAxis就可以做到,因為他們直接的資料在使用上還是比較依賴。

 1 class QCP_LIB_DECL QCPGrid :public QCPLayerable
 2 {
 3     QCPGrid(QCPAxis *parentAxis);
 4 
 5     // setters:
 6     void setSubGridVisible(bool visible);//設定是否顯示自網格線
 7     void setAntialiasedSubGrid(bool enabled);//設定子網格線是否反鋸齒
 8     void setAntialiasedZeroLine(bool enabled);//設定零線(就是刻度值為0的線)是否反鋸齒
 9     void setPen(const QPen &pen);//設定畫筆
10     void setSubGridPen(const QPen &pen);//設定子網格畫筆
11     void setZeroLinePen(const QPen &pen);//設定零線畫筆
12 
13 protected:
14     bool mSubGridVisible;//子網格是否顯示標記
15     bool mAntialiasedSubGrid, mAntialiasedZeroLine;//子網格和零線是否反鋸齒標記
16     QPen mPen, mSubGridPen, mZeroLinePen;//這個就不用說了
17     QCPAxis *mParentAxis;//對於的座標軸,一個網格線對應一個座標軸
18     virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;//獲取預設的反鋸齒屬性
19     virtual void draw(QCPPainter *painter);//繪製網格線,內部呼叫drawGridLines和drawSubGridLines
20 
21     // non-virtual methods: 
22     void drawGridLines(QCPPainter *painter) const;//繪製網格線
23     void drawSubGridLines(QCPPainter *painter) const;//繪製子網格線
24 };

    一般來說,網格線不需要重寫,頂多就是設定一個顏色,控制是否顯示,網格線的疏密成都市和座標軸刻度計算有關係的,因此關於網格線的計算我們就不要考慮了,下面我提供一個我自定義的刻度固定畫素計算類示例

五、簡單的示例

    首先來看下效果,如圖1所示,當圖表放大時,y軸上的刻度間距是保持固定畫素的。

圖1 y軸固定畫素伸縮

    如下是刻度計算類標頭檔案,這個類實現起來還是比較簡單的,根據螢幕畫素返回步長,每次步長都是按當前畫素比例下計算的。

 1 class AxisFixedPixelTicker : public QCPAxisTicker
 2 {
 3 public:
 4     AxisFixedPixelTicker(QCPAxis * axis);
 5     ~AxisFixedPixelTicker();
 6 
 7 public:
 8     void SetTickPixelStep(int pixel);//設定固定畫素
 9     int GetTickPixelStep() const;//獲取固定畫素大小
10 
11 protected:
12     //QCPAxisTicker
13     virtual double getTickStep(const QCPRange & range) override;//重寫父類方法,根據固定畫素返回步長值
14 
15 private:
16     QScopedPointer<AxisFixedPixelTickerPrivate> d_ptr;
17 };

    下面是重寫的父類介面getTickStep方法實現

 1 double AxisFixedPixelTicker::getTickStep(const QCPRange & range) 
 2 {
 3     if (d_ptr->m_pDependAxis)
 4     {
 5         bool vertical;
 6         if (d_ptr->m_pDependAxis->axisType() == QCPAxis::atLeft
 7             || d_ptr->m_pDependAxis->axisType() == QCPAxis::atRight)
 8         {
 9             vertical = true;
10         }
11         else
12         {
13             vertical = false;
14         }
15 
16         int screenLength = vertical ? d_ptr->m_pDependAxis->axisRect()->rect().height() : d_ptr->m_pDependAxis->axisRect()->rect().width();
17         return d_ptr->m_iPixel * range.size() / screenLength;
18     }
19     else
20     {
21         return __super::getTickStep(range);
22     }
23 }

六、相關文章

  QCustomplot使用分享(一) 能做什麼事

  QCustomplot使用分享(二) 原始碼解讀

  QCustomplot使用分享(三) 圖

  QCustomplot使用分享(四) QCPAbstractItem

  QCustomplot使用分享(五) 佈局

相關文章