目錄
- 一、醉一醉
- 二、效果展示
- 三、高DPI適配
- 1、高DPI框架運作
- 2、適配高DPI
- 3、適配餅圖
- 四、相關文章
原文鏈接:Windos高DPI系列控制元件(一) - 餅圖
一、醉一醉
眨眼功夫,2020年過去一半了,回想最近一段時間的作業和生活,總覺得應該寫點兒什么!
于是,最近有空就在想啊想,想想可以寫點兒什么有用的東西好呢!剛好之前寫過幾篇關于高DPI的文章,不知道什么原因,閱讀量不是很高,因此打算以高DPI為索引開始引入一系列的控制元件使用案例,包括Qt自帶的控制元件、簡單圖表和一些復雜的圖表,
- Qt自帶的控制元件就不說了,高DPI框架幾乎可以完美適配
- 簡單的圖表這里主要會引入柱狀圖、折線圖、餅圖等
- 復雜圖表主要是定制一些股票看盤相關圖表,例如分時圖、k線圖等
對于大眾軟體來說,友好的支持4k顯示幕真的很有必要呢,一款好的大眾桌面端軟體需要適配各種作業系統,從快要被人們遺忘的Xp到現在占有率較高的Win10,如果想擁有一個好的用戶體驗,高DPI是必須要好好適配滴,這里作者準備了一個系列的高DPI控制元件適配文章分享給大家,主要整理我作業中遇到的各種控制元件,適配到已經開發好的高DPI框架中,并做出演示demo,提供給有需要的同學,除過整理已有的控制元件,更多的是會開發一些更有意義的新控制元件,比如股票中的分時圖、k線等,
目的:
- 推廣我自己適配高DPI的方案,供大家討論,是否有更好的優化空間
- 整理股票相關的控制元件,適配到我的高DPI框架中,提供給有需要的同學參考
- 階段性整理我自己的知識庫,讓零散的知識點匯聚起來
二、效果展示
如下圖所示,適配高DPI互動效果,
左右兩側的顯示物理尺寸一致,也就是占地面積一樣大,不同的是左側是1080p顯示幕,右側是4k顯示幕
因為是視頻錄制原因,可能會有視覺誤差,實際看的話,左右兩個表單給人的視覺感受大小是一樣的,
《來回切換顯示幕》
《餅圖支持操作》
三、高DPI適配
高DPI的適配思路之前已經系統的分析過,詳情參看Qt之高DPI顯示幕(一) - 解決方案整理和Qt之高DPI顯示幕(二) - 自適配解決方案分析兩篇文章,
除過上述兩篇文章外,之前計劃中還有一篇文章要寫,后來實在是太忙了,一直沒有寫完,第三篇文章主要是想講下怎么優化現有框架,讓DPI適配效率變的更高,后邊有機會補上,
本票文章開始算是適配高DPI實踐系列文章的開篇之作,餅圖控制元件很早之前就分享過,詳情參考Qt之自繪制餅圖,怎么繪制餅圖這里就不再描述,本篇文章的核心主要是進行了餅圖高DPI的顯示適配,后邊會主要描述下適配的細節,后期還會陸續接入更多更豐富的組件,
下面先帶大家回顧下適配高DPI我們都干了哪些事情,最后在看看餅圖是怎么適配高DPI的,
1、高DPI框架運作
看過前兩篇文章的同學應該知道,我們適配高DPI主要從兩個方面進行的,分別是表單的物理尺寸和字體
物理尺寸
物理尺寸從字面上看就是軟體大小,不過我們這里的物理尺寸也包含子視窗的大小,那也就是說子視窗之間的間隙也在物理尺寸這里進行適配,
為了讓開發同學無感知的使用,我們新增了同樣數量的可能會使用到的界面類,作為我們自己的基礎類,并且重寫了界面類中跟尺寸相關的函式,讓大家使用界面類的時候只換類名稱,介面用法還是跟以前一樣,
重寫了尺寸函式,如果我們也知道當前顯示幕的DPI,縮放界面是不是就很簡單了!當前開發設定的尺寸乘以需要縮放的系數就是最終需要顯示的尺寸,框架只需要把最后需要顯示的尺寸設定給Qt的介面,也就是當前類的父類介面,這樣我們的軟體界面就實作了放大,做到這里算不算完呢?仔細想一想,開發同學使用這些介面的時候都是設定了96DPI下的尺寸,如果視窗移動到另一臺不同DPI的顯示幕上,難道我們要把所有set介面重新呼叫一遍?如果數量少了還行,但一個復雜的軟體這樣的set介面會多的令你發指,如果你讓開發同學都呼叫一遍,我估計他們會打死你,除過需要呼叫以外,呼叫順序也至關重要,試想這樣一種場景,如果需要縮放的表單很多,你會希望表單區域突然變大,沒有匯率的跳動嗎?答案當然是否,
為了讓表單有規律的縮放,那我們就需要控制縮放的順序了,這里就需要額外的繼續重寫一些關鍵方法,之前我們重寫類的時候重寫了尺寸相關函式,這一次我們還需要把布局相關的函式也進行重寫,為的就是在界面布局的時候我們把他們的關系記錄下來,后續在出現DPI變化時,我們根據之前維護的布局關系,按層呼叫每一個需要縮放的界面,
界面布局
這里普及一個知識點,平時我們所看到的軟體界面是平面的,是一個二維的概念,但是軟體界面在開發的程序中,界面的布局關系其實是一棵樹,比如像下圖這樣的界面,界面A包括了界面B和界面C,而界面B又包括了兩個界面D,這樣當我們構造兩個界面A時,其實所有的界面都被構造了兩份,并且界面D被構造了四份,
字體大小
我們適配的高DPI框架,除過自繪文字以外,其他情況是不需要關注字體怎么變動的,這些字體適配都在我們的高DPI適配框架中完成了,這里簡單描述下字體適配高DPI的方式,
高DPI框架運行程序中,主動維護了1x、2x和3x下的qss檔案,如果檢測到程式所需要的Qss檔案不在這三個配置中,那么框架會動態的根據一個最接近當前縮放比的qss檔案生成一個臨時qss檔案,比如當前縮放比是1.8x,那么程式將會根據2xqss檔案,生成一個適合1.8x縮放比的qss檔案,首先就是圖片進行壓縮顯示,字號會乘以1.8然后除以2轉換成1.8x縮放比下的字號,可能會有舍入,但是對于大家常用的0.5整數倍縮放比基本都是沒有問題的,因為字號一般都是2的整數倍,
2、適配高DPI
高DPI框架設計之初就是想讓開發同學盡量少的去操心DPI的事情,但是實際情況是還有少部分情況是需要自己去適配的,比方說自毀界面,這個時候我們就需要自己去獲取高DPI相關資訊合理繪制
繪制文字
繪制文字時,我們需要獲取當前的縮放系數,在合適的實際去縮放繪制相關引數,比方說,當我們繪制12px字體時,如果是在4k顯示幕下,我們的dpi可能為192,那么縮放系數就是2x,繪制文字時就需要繪制24px字體
縮放系數 = 當前顯示幕DPI / 96.0
有時候繪制文字時可能會附帶限制文字所在區域,96DPI下我們不需要操心區域是否會出現問題,但是如果顯示幕DPI大于96時,文字繪制的區域我們也就需要相應的適配下,否則可能會出現你不希望的結果,比方說,我們需要在坐標為10,10這個位置,以邊框100px正方形框內繪制一段文本,96DPI下可能剛剛好能繪制完這段文字,如果192DPI下,我們把字號放大了一倍,如果繪制區域還是100px的正方形,那么文字很可能連一半都繪制不完,問題出在哪里呢?很顯然,字體變大了,我們的繪制區域肯定也需要進行相應的放大,不妨試試200px的正方形是不是可以呢!答案是Yes,
繪制圖片
自繪界面時往往少不了繪制圖片,下面具體分析下怎么在任意DPI下繪制圖片!
首先是獲取1x縮放比下的圖片路徑,然后我們通過一個轉換函式轉換成我們當前顯示幕下需要的圖片路徑,并縮放圖片,以達到最好的顯示效果,
如下代碼所示,是一個封裝好的函式,主要完成了根據1x圖片路徑獲取我們將要繪制的圖片,并且給我們回傳的是記憶體地址,這里需要額外補充下,Qt中的QPixmap是有做快取機制的,當我們第二次獲取同一張圖片時,Qt會直接從記憶體中獲取到上一次圖片的記憶體直接回傳給我們,因此這里不需要擔心效率問題,
QPixmap TIGERQTCOMM_EXPORT ImagePath::GetStretchPixmap(const std::string & path, float scale)
{
std::string tpath = ImagePath::GetPixmapPath(path, (int)(scale + 0.5001));
float factor = ImagePath::GetStretchFactor(scale);
QPixmap pixmap(tpath.c_str());
if (factor != 1.0)
{
pixmap = pixmap.scaled(pixmap.size() * factor);
}
return pixmap;
}
3、適配餅圖
自繪界面時需要我們自己去適配高DPI,主要是繪制所需要的的幾何大小需要調整,說的直白一點兒就是看下圖,左側1080P顯示幕,右側4K顯示幕,并且兩個顯示幕尺寸是一樣大的,看左右兩側的矩形區域坐標很清楚的展示出來了,右側看著一樣大的舉行是左側舉行的兩倍大,并且左上角的坐標也是兩倍,
左側矩形幾何大小是(83, 104, 168, 211),右側矩形幾何大小是(166, 208, 336, 422) ,按照我們高DPI適配的叫法左側顯示幕的縮放比就是1x,右側是2X
《1080P vs 4K》
前邊小節說過了,自繪界面時適配高DPI主要是針對繪制的幾何大小,餅圖也不例外,這里我貼一個餅圖各模塊幾何大小計算的函式,已經適配過高DPI,方法也很簡單
適配程序主要是用宏來完成的,宏定義如下:#define SCALE_NUMBER(n) ((n) * dpi_scale),dpi_scale為每個高DPI框架下類的成員變數,該變數由框架維護,表示當前視窗需要縮放的系數
廢話不多說,如下代碼是適配過高DPI后的函式,主要是對一些影響幾何位置計算的引數進行了縮放,
void CPieChart::ConstructCornerLayout(const QSize & size)
{
int currentR = SCALE_NUMBER(d_ptr->m_MinDiameter);
int diameter;
int horiWidth = size.width();
if (d_ptr->m_bLegendVisible)
{
horiWidth -= SCALE_NUMBER(d_ptr->m_LegendWidth * 2);
}
int PieHeight;
if (d_ptr->m_MutiDay.size() >= 1)
{
PieHeight = size.height() - SCALE_NUMBER(d_ptr->m_BarHeight) * d_ptr->m_MutiDay.size()
- SCALE_NUMBER(d_ptr->m_BottomMargin + d_ptr->m_Space + d_ptr->m_BarSpace * (d_ptr->m_MutiDay.size() - 1))
- SCALE_NUMBER(d_ptr->m_LabelHeight * 2);
}
else
{
PieHeight = size.height();
}
if (horiWidth > PieHeight)
{
diameter = PieHeight;
}
else
{
diameter = horiWidth;
}
int x, y;
int r = diameter - SCALE_NUMBER(d_ptr->m_Minx * 2);
currentR = r > currentR ? r : currentR;
if (d_ptr->m_bLegendVisible)
{
d_ptr->m_Items.resize(4);
x = width() / 2 - currentR / 2;
y = (PieHeight - currentR) / 2;
d_ptr->m_Items[1].m_LegendRect = QRect(SCALE_NUMBER(d_ptr->m_Minx), SCALE_NUMBER(d_ptr->m_Miny)
, SCALE_NUMBER(d_ptr->m_LegendWidth), SCALE_NUMBER(30));
d_ptr->m_Items[0].m_LegendRect = QRect(size.width() - Margin - SCALE_NUMBER(d_ptr->m_LegendWidth)
, SCALE_NUMBER(d_ptr->m_Miny)
, SCALE_NUMBER(d_ptr->m_LegendWidth), SCALE_NUMBER(30));
d_ptr->m_Items[3].m_LegendRect = QRect(size.width() - Margin - SCALE_NUMBER(d_ptr->m_LegendWidth)
, PieHeight - SCALE_NUMBER(d_ptr->m_Miny + 30)
, SCALE_NUMBER(d_ptr->m_LegendWidth), SCALE_NUMBER(30));
d_ptr->m_Items[2].m_LegendRect = QRect(SCALE_NUMBER(d_ptr->m_Minx)
, PieHeight - SCALE_NUMBER(d_ptr->m_Miny + 30)
, SCALE_NUMBER(d_ptr->m_LegendWidth), SCALE_NUMBER(30));
d_ptr->m_Items[0].m_bAlign = false;
d_ptr->m_Items[3].m_bAlign = false;
}
else
{
x = SCALE_NUMBER(d_ptr->m_Minx);
y = SCALE_NUMBER(d_ptr->m_Miny);
}
d_ptr->m_PieRect = QRect(x, y, currentR, currentR);
d_ptr->m_BarsRect = QRect(SCALE_NUMBER(20), 2 * y + currentR + SCALE_NUMBER(d_ptr->m_Space)
, width() - SCALE_NUMBER(50)
, size.height() - PieHeight);
}
到目前為止,本篇文章要分享的內容算基本完成了,餅圖控制元件的其他的代碼邏輯,包括繪制邏輯適配高DPI方式都合上述函式類似,大家自行腦補即可,感興趣的朋友可以到餅圖-高DPI下載,CSDN鏈接中的資源只包含適配過高DPI的餅圖繪制代碼,僅供大家參考,并不能通過編譯,
四、相關文章
- Qt之高DPI顯示幕(一) - 解決方案整理
- Qt之高DPI顯示幕(二) - 自適配解決方案分析
- Qt之自繪制餅圖
值得一看的優秀文章:
- 財聯社-產品展示
- 廣聯達-產品展示
- Qt定制控制元件串列
- 牛逼哄哄的Qt庫
如果您覺得文章不錯,不妨給個打賞,寫作不易,感謝各位的支持,您的支持是我最大的動力,謝謝!!!
![]() |
![]() |
很重要--轉載宣告
-
本站文章無特別說明,皆為原創,著作權所有,轉載時請用鏈接的方式,給出原文出處,同時寫上原作者:朝十晚八 or Twowords
-
如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利于轉載者的目的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/3605.html
標籤:C++
上一篇:進制轉換
下一篇:c++ 標準庫檔案


