一、概述
前邊已經寫了5篇對QCustomPlot的講解,看過上述的幾篇文章后,基本就能做一些簡單的使用了,但是如果想要做到高度的控制圖表,那么坐標軸將是很重要的一部分,因為坐標軸就是圖表的一個參考系,沒有了參考系那么一切都是天方夜譚。關於QCustomPlot的坐標軸我還是會按照之前的套路,首先對比1.3.2版本和2.0.0beta版本,然后在深入的去分析坐標軸使用。
二、歷史版本對比
首先我需要和大家伙說明下,我個人覺着在QCustomPlot的定制過程中,坐標軸定制算是比較困難的,因為坐標軸如果要定制的話,那就是坐標軸的刻度需要自己計算,如果這個時候相關的業務邏輯也影響坐標軸的計算,那么就更難計算了,呵呵。。。或許大家伙可能也不會遇到這些問題,有興趣的同學也可以自己思考下。
1.3.2版本 | 2.0.0版本 | |
坐標軸 | 1、QCPAxis:坐標軸類,所有坐標軸功能都在這一個類總實現,包括:刻度計算和繪制文本 |
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使用分享(四) QCPAbstractItem