目錄
- 一、QCP
- 二、效果展示
- 三、高DPI適配
- 1、自定義柱狀圖
- 2、新的柱狀圖
- 3、測驗代碼
- 四、相關文章
原文鏈接:Windows高DPI系列控制元件(二) - 柱狀圖
一、QCP
QCP全稱QCustomPlot,是一個基于Qt的圖表庫,同時支持Qt4和Qt5,使用起來還是很方便的,不管是編譯成dll還是直接嵌入到我們自己的程式都是極其容易,畢竟只有兩個檔案,之前寫過幾篇簡單的關于QCP的文章,從使用的角度分析了QCP的一些簡單用法,包括:QCustomplot使用分享(一) 能做什么事、QCustomplot使用分享(二) 原始碼解讀、QCustomplot使用分享(三) 圖、QCustomplot使用分享(四) QCPAbstractItem、QCustomplot使用分享(五) 布局、QCustomplot使用分享(六) 坐標軸和網格線和QCustomplot使用分享(七) 層(完結),感興趣的同學可以抽時間閱讀一遍,高DPI系列控制元件大多數也都是基于QCP庫進行的定制,首先是對QCP的包裝,其次其次也對QCP原始碼進行了高DPI適配,主要還是針對我自己的DPI框架進行適配,
此高DPI適配框架,在DPI為96整數倍下是非常完美的,無失真情況,其他非整數比縮放情況下會有一些小瑕疵,比如非整數縮放比下圖片進行過拉伸,某些拉伸會導致失真的圖片會發生模糊的情況;其次當縮放比不是0.5的整數倍時,字號放大有可能不是按比例的,會導致界面字體不協調,但是字體不會發虛,對于大多數用戶來說,目前的高DPI框架都是可以滿足的,比如我個人的顯示就是一個1080P顯示幕和一個4K顯示幕,兩個顯示幕的縮放比分別是100%和200%,沒有任何失真的情況,
上一篇文章Windows高DPI系列控制元件(一) - 餅圖中講過怎么在高DPI顯示幕下繪制餅圖,并且坐到沒有任何的失真,畢竟都是自己繪制的,餅圖控制元件是很早以前就完成的功能,這一篇文章主要是對他進行了高DPI的適配,讓餅圖控制元件在我的4K顯示幕上也可以很友好的展示,并且可以在兩個顯示幕直接無縫切換,
本篇文章是繼Windows高DPI系列控制元件(一) - 餅圖文章后的第二篇關于高DPI控制元件的實體,包括后續會定制的一些列高DPI控制元件,大多數基于QCP來實作,文章前面也說過,QCP是一個非常強大的繪圖庫,比較遺憾的就是沒有餅圖這個控制元件,上一篇Windows高DPI系列控制元件(一) - 餅圖控制元件是我參考QCP的代碼自己實作的繪制,效率也是杠杠滴,除此之外QCP也是非常容易擴展的,比如可以自己添加新的層,去繪制一些自己需要的東西,多說一句QCP2.0版本區別于1.0版本又一個很大的優勢就是多層繪制的,這樣效率比較高,并且您也可以指定某個層進行單獨重繪,本篇文章講到的柱狀圖上的tooltips提示實作方式就是新增了一個tooltips繪制層,這樣實作的好處就是跟現有框架的其他層代碼是解耦的,比如后期您不要這個層了,可以直接刪掉,或者隱藏都是可以的,這樣對整個代碼運行效率幾乎沒有任何損失,
二、效果展示
如下圖所示,柱狀圖和折線圖適配高DPI后展示效果,
左右兩側的顯示物理尺寸一致,也就是視覺上大小一樣大,不同的是左側是1080P顯示幕,右側是4K顯示幕
因為是視頻錄制原因,可能會有視覺誤差,實際看的話,左右兩個表單給人的視覺感受大小是一樣的,
《來回切換顯示幕》
《柱狀圖》
三、高DPI適配
1、自定義柱狀圖
QCP庫中的柱狀圖支持多種模式,上一小節中的效果圖是普通的柱狀圖展示效果,除此之外QCP中的柱狀圖可以做到多組同時展示,并且兩組柱狀圖之間可以堆疊,由于適配需要花更多的實際,因此更多復雜的展示效果會在后續的文章中會陸續提供,
如《柱狀圖》展示圖中的效果,我們定制了柱狀圖一個新的柱狀圖類,主要是基于該類我們支持了展示tooltips效果,下面主要介紹下比較重要的幾個實作點
tips實作
文章開頭也提到過,QCP2.0是多層繪制的,這樣可以提高繪制效率,并且提升更強的擴展能力,如QCustomplot使用分享(七) 層(完結)這篇文章所分析的那樣,本篇文章中的提示框就是繼承可繪制物件QCPLayerable,界面上幾乎所有的繪制元素包括布局元素都是繼承自這個類,該類有一個draw函式,實作該介面就可以實作我們自己想要繪制的東西,界面繪制時,繪制區域取決于自己所在的布局區域,
#ifndef CHARTTIP_H
#define CHARTTIP_H
#include "qcp/QCustomplot.h"
class CBaseToolTip : public QCPLayerable
{
Q_OBJECT
public:
CBaseToolTip(QCustomPlot * plot);
~CBaseToolTip();
public:
QString LayerName() const;
void SetVisible(bool visible);//éè??2?ê?·?????
void SetTipLabel(const QVector<QString> & labels);
void SetTopLeft(const QPoint & pos);
protected:
virtual void applyDefaultAntialiasingHint(QCPPainter * painter) const override{};
virtual void draw(QCPPainter * painter) override;
virtual void DrawTip(QCPPainter * painter) = 0;
protected:
QPoint m_AnchorPos;
QVector<QString> m_Labels;
private:
};
class CSideToolTip : public CBaseToolTip
{
public:
CSideToolTip(QCustomPlot * plot);
~CSideToolTip(){}
protected:
virtual void DrawTip(QCPPainter * painter) override;
};
class CTopToolTip : public CBaseToolTip
{
public:
CTopToolTip(QCustomPlot * plot);
~CTopToolTip(){}
protected:
virtual void DrawTip(QCPPainter * painter) override;
private:
int radius = 3;
int height = 46;
int width = 125;
};
#endif // CHARTTIP_H
上述頭檔案中,包括三個類,其中CBaseToolTip是tip提示的基類,他負責完成了tip物件的一些公有屬性,比如構造該繪制物件時自動創建tip繪制層,并把層加到QCP視窗物件上;除此之外還可以指定繪制的位置等,
本篇文章中的tip是CSideToolTip類來實作的,下面是該類具體的繪制邏輯,仔細一看下面的diamante,發現這里又出現了我們熟悉的XXX_SCALE_NUMBER宏,不錯這個宏就是用來實作高DPI邏輯的,比如下面繪制矩形框的邏輯,矩形的長和寬都被我們用QCP_PAINTER_SCLAE_NUMBER宏給包裹起來,這樣繪制時繪制的矩形大小就是我們需要的大小,
#define QCP_PAINTER_SCLAE_NUMBER(a) (a) * painter->dpi_scale
這里簡單說下這個宏的作用,QCPPainter原始碼本身是沒有dpi_scale這個變數的,為了更好的適配高DPI顯示幕,這個變數是我自己加上的,表示當前繪制painter需要縮放的比例,比如我們繪制tip矩形區域時,就需要對長和寬進行必要的縮放,
除過QCPPainter我添加了dpi_scale縮放系數成員變數以外,QCPLayerable基類我也添加了該成員變數,并且在合適的實際都會進行變數更新,后續我會專門寫一篇關于QCP適配高DPI的文章,專門講解針對高DPI適配我都做了哪些作業,
void CSideToolTip::DrawTip(QCPPainter * painter)
{
//繪制圓圈
painter->setPen(Qt::transparent);
// painter->setBrush(QColor(255, 204, 51, 80));
// painter->drawEllipse(m_AnchorPos, 5, 5);
painter->setBrush(QColor(255, 181, 26));
painter->drawEllipse(m_AnchorPos, int(QCP_PAINTER_SCLAE_NUMBER(3)), int(QCP_PAINTER_SCLAE_NUMBER(3)));
//繪制矩形 135 * 65
QRect rect(0, 0, QCP_PAINTER_SCLAE_NUMBER(100), QCP_PAINTER_SCLAE_NUMBER(30));
rect.moveBottomRight(m_AnchorPos - QPoint(QCP_PAINTER_SCLAE_NUMBER(8), 0));
if (rect.left() < mParentPlot->axisRect()->outerRect().left())
{
rect.moveBottomLeft(m_AnchorPos + QPoint(QCP_PAINTER_SCLAE_NUMBER(8), 0));
}
painter->setPen(QColor(255, 204, 51));
painter->setBrush(QColor(0, 0, 0, 255 * 0.7));
painter->drawRect(rect);
//繪制矩形框文字
QFont font(QStringLiteral("微軟雅黑"));
font.setPixelSize(QCP_PAINTER_SCLAE_NUMBER(10));
painter->setFont(font);
painter->drawText(QPoint(QCP_PAINTER_SCLAE_NUMBER(8), QCP_PAINTER_SCLAE_NUMBER(13))
+ rect.topLeft(), QStringLiteral("兩融余額:%1").arg(m_Labels[0]));
painter->drawText(QPoint(QCP_PAINTER_SCLAE_NUMBER(8), QCP_PAINTER_SCLAE_NUMBER(25))
+ rect.topLeft(), QStringLiteral("上證指數:%1").arg(m_Labels[1]));
}
2、新的柱狀圖
新的柱狀圖被我命名為CTooltipBars,該類沒有什么比較牛逼的實作,代碼量也不大,其中有一個比較重要的介面OnCheckHover和一個關鍵信號HoverIndex;
#ifndef TIPBAR_H
#define TIPBAR_H
#include "../rluilib/dpi_macro.h"
#include "qcp/QCustomplot.h"
class QMouseEvent;
class CTooltipBars : public QCPBars
{
Q_OBJECT
signals :
void HoverIndex(double index);
public:
CTooltipBars(float scale, QCPAxis * keyAxis, QCPAxis * valueAxis);
~CTooltipBars();
public:
void SetValueVisible(bool visible);
public slots:
void OnCheckHover(QMouseEvent * pos);
protected:
virtual void draw(QCPPainter * painter) override;
private:
bool m_bValueVisible = false;
int m_iLabelHeight = 0;
};
#endif // TIPBAR_H
OnCheckHover:hover行為檢測介面,當我們移動滑鼠時,該介面會重復被觸發去檢測是否hover到了某個柱子上,如果hover成功那么就會觸發HoverIndex信號,引數表示hover的柱子序號,
檢測滑鼠hover事件的代碼如下圖所示,整個代碼的核心思想就是獲取所有可見的柱子資料,然后回圈去判斷滑鼠當前的坐標是否在哪個柱子的區域內,并觸發HoverIndex信號,引數為-1時表示沒有hover到任何柱子,那么此時可能需要隱藏已經展示的tip提示,
void CTooltipBars::OnCheckHover(QMouseEvent * event)
{
QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
getVisibleDataBounds(visibleBegin, visibleEnd);
QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
getDataSegments(selectedSegments, unselectedSegments);
allSegments << unselectedSegments << selectedSegments;
double success = -1;
for (int i = 0; i < allSegments.size(); ++i)
{
bool isSelectedSegment = i >= unselectedSegments.size();
QCPBarsDataContainer::const_iterator begin = visibleBegin;
QCPBarsDataContainer::const_iterator end = visibleEnd;
mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
if (begin == end)
{
continue;
}
for (QCPBarsDataContainer::const_iterator it = begin; it != end; ++it)
{
QRectF rect = getBarRect(it->key, it->value);
rect = rect.adjusted(0, -m_iLabelHeight, 0, 0);
bool contain = rect.contains(event->pos());
if (contain)
{
success = it->key;
break;
}
}
if (success != -1)
{
break;
}
}
if (mParentPlot->axisRect()->rect().contains(event->pos()) == false)
{
success = -1;
}
emit HoverIndex(success);
}
HoverIndex:柱狀圖hover時觸發信號,表示滑鼠在引數指定的柱子內
3、測驗代碼
如下代碼是效果圖中的測驗代碼,我們構造了一個CBarChart物件,然后添加了一些簡單的額測驗資料,用起來是不是很爽呢!
QWidget * CreateBar(float scale)
{
CBarChart * barChart = new CBarChart(scale);
BarDataList datas;
datas.push_back({ 1585816691, 200, });
datas.push_back({ 1588408691, 150, });
datas.push_back({ 1591087091, 220, });
datas.push_back({ 1593679092, 100, });
barChart->SetDatas(datas);
barChart->SetYRange(QCPRange(0, 250), 5);
return barChart;
QStringLiteral("柱狀圖"));
}
CBarChart類是一個對外的柱狀圖使用類,主要是把支持tip的柱狀圖、tip類和legend類進行了組裝,這里就不在詳細講解了,該類的成員變數如下代碼所示,其中還有一些輔助性的成員,比如說折線圖QCPGraph類,QCP唯一視窗類QCustomPlot,該類也是我們繪制圖表時不可或缺的物件,所以的繪制呼叫邏輯都是從該視窗的paintEvent函式開始出發的,并且該類做了各種繪制優化操作,并且支持3種繪制方式,感興趣的同學可以搜索下QCPAbstractPaintBuffer這個類,這個是繪制buffer基類,其他繪制的實作都是基于該類實作,
struct BarChartPrivate
{
QVector<double> m_TickKey;
QVector<QString> m_TickNames;
QLabel * m_pBarLabel = nullptr;
QLabel * m_pGraphLabel = nullptr;
CLegendWidget * m_pLegend = nullptr;
CBaseToolTip * m_pToolTip = nullptr;
QCPGraph * m_pGraph = nullptr;
QList<CTooltipBars *> m_pBars;
QSharedPointer<QCPAxisTickerText> m_pXAxisTicker;
QCustomPlot * m_pWidget = nullptr;
QCPMarginGroup * m_pMarginGroup = nullptr;
};
四、相關文章
- Qt之高DPI顯示幕(一) - 解決方案整理
- Qt之高DPI顯示幕(二) - 自適配解決方案分析
- Qt之自繪制餅圖
- QCustomPlot之布局系統
- QCustomplot使用分享(一) 能做什么事
- QCustomplot使用分享(二) 原始碼解讀
- QCustomplot使用分享(三) 圖
- QCustomplot使用分享(四) QCPAbstractItem
- QCustomplot使用分享(五) 布局
- QCustomplot使用分享(六) 坐標軸和網格線
- QCustomplot使用分享(七) 層(完結)
- Windows高DPI系列控制元件(一) - 餅圖
值得一看的優秀文章:
- 財聯社-產品展示
- 廣聯達-產品展示
- Qt定制控制元件串列
- 牛逼哄哄的Qt庫
如果您覺得文章不錯,不妨給個打賞,寫作不易,感謝各位的支持,您的支持是我最大的動力,謝謝!!!
![]() |
![]() |
很重要--轉載宣告
-
本站文章無特別說明,皆為原創,著作權所有,轉載時請用鏈接的方式,給出原文出處,同時寫上原作者:朝十晚八 or Twowords
-
如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利于轉載者的目的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/3611.html
標籤:C++


