Qt之高DPI顯示器(一) - 解決方案整理
目錄
- DPI發展
- 1、PPI
- 2、DPI
- 一、Win自適應系統
- 二、Qt機制
- 1、Windows系統DWM縮放
- 2、 Qt適配高DPI
- 3、適配DPI結論
- 三、Qt適配
- 四、自己適配
- 1、視窗大小
- 2、字型大小
- 3、間距
- 4、圖示
- 五、參考文章
最近一直在處理高DPI問題,也花費了不少功夫,前前後後使用了多種解決方案,各種方案也都有利弊,筆者最終採用了自適配方案,雖然複雜一些,但是結果可控。這裡把處理的過程記錄下來,留給有同樣需求的同學
DPI發展
隨著顯示器質量的增高,高分屏逐漸增多,很多使用者平時使用的機器都是2k屏甚至是4k屏。
顯示器解析度變大後,同樣的物理尺寸下可以表示更多的點,也就是我們平時所說的畫素。
誤區說明
我們現在平時所說的軟體畫素應該是PPI(Pixels Per Inch),中文意思是每英寸畫素數,而我們windows系統中修改的DPI其實就是這個PPI。
為什麼會有這個誤區呢?
答案:因為這個世界正在盡人類想象所能地使其變得難懂。
1、PPI
每英寸畫素數,畫素表示的是“圖片的原色”,足夠靠近你顯示屏上的圖片你就會看到他們:一排一排的小方塊。換句話說,他們也是一個電子圖片的最小可定址單元。
其中每一個電子圖片則是由更小的光學單元組成,這些光學單元就是紅色、綠色和藍色。
PPI的始作俑者
可悲的是,一些生產廠商將這些 sub-pixel 描述為“dot”,為什麼這樣認為呢?因為他和DPI的墨點工作方式相同,然後就是一頓商業互吹,用DPI來描述PPI就被開始誤用了。
需要注意的是,pixel 是一個固定大小的物理物體,因此,一個螢幕上的 PPI 是固定的。大部顯示器都是 66 - 130ppi。
PPI 和列印的目標
只有要打印出來時,設定 PPI 才有效。
在列印的過程中,所有在螢幕上組成圖片的物理pixel都被轉換成不同色調的小正方形,這些小正方形是另外一種“畫素”,下文中都使用pixel-d來表示。
當列印時,如果增加圖片的大小為300%,圖片是放大了300%,但是圖片的顆粒度更大了!如果想要清晰度不變,而大小發生變化,這個時候我們就需要修改PPI。
看一個圖片列印的示例
圖片尺寸 | PPI | 物理春 | 結果 | 印表機(DPI) | 每英寸點數 | 總點數 | 結果 |
---|---|---|---|---|---|---|---|
300px * 300px | 10 | 30 * 30英寸 | 非常大 | 60 | 60 * 60 | 30 * 30 * 每英寸點數 | 非常虛 |
300px * 300px | 300 | 1 * 1英寸 | 非常小 | 90 | 90 * 90 | 1 * 1 * 每英寸點數 | 細膩 |
300px * 300px | 60 | 5 * 5英寸 | 可能合適 | 120 | 120 * 120 | 5 * 5 * 每英寸點數 | 非常細膩 |
對於最終的列印輸出而言,將 PPI 輸入認為是一種調節物理大小的方式,而不是解析度。
PPI可以決定列印的大小,想要更加細膩的列印效果則是需要增加更多的pixel-d。
2、DPI
DPI 只是印表機的技術引數,就像是你電腦顯示器的 pixel 解析度。
比如說,你以 600 dpi 來列印一幅 150ppi 的影象,那麼每個“pixel”將會包括16個 dot(600 dot/150 “pixel” = 4 x 4 / “pixel”)。
名稱 | 說明 |
---|---|
Inch英寸 | 物理單位,可以衡量顯示器的物理大小 |
PPI每英寸畫素數 | 物理值,由硬體廠商決定。表示每英寸下可以儲存多少個光學單元 |
DPI每英寸點數 | 印表機單位,一個技術引數,代表印表機的好壞。通常來說,dot 矩陣印表機可列印的 dpi 範圍為 60 - 90,噴墨印表機的 dpi 範圍為 300 - 600,而鐳射印表機為 600 - 1800。 |
Resolution解析度 | 顯示器單位,描述每英寸畫素數 |
windows系統DPI | 個人理解:windows自己的一個標準,表示每英寸畫素數,也就是PPI(顯示器真正的PPI不支援修改);類似於列印時的DPI。一個技術指標或者引數 |
綜上:對於我們軟體開發來說,其實所說的畫素大小都是指PPI,這裡要區分不是顯示器的固定PPI,windows系統上修改DPI時其實修改的系統模擬出來的PPI;就類似於我們列印圖片時輸出的PPI一樣,可以決定圖片列印的物理尺寸。
一、Win自適應系統
High DPI Desktop Application Development on Windows
總的來說可以用,但是會模糊。目前win10效果最好,基本清晰,但是還可以優化;win7系統上基本是糊的,如果您的產品是一個網際網路軟體,那麼系統自適應絕對不是最佳方案。
二、Qt機制
要使用Qt高DPI縮放,首先得禁用系統縮放。
方式1: QApplication構造前設定Qt::AA_EnableHighDpiScaling屬性
方式2: 設定環境變數QT_AUTO_SCREEN_SCALE_FACTOR為1
1、Windows系統DWM縮放
啟用系統縮放時,由於使用的都是圖片拉伸的方式則會產品模糊
--- | DPI Unaware | System DPI Awareness | Per-Monitor and Per-Monitor (V2) DPI Awareness
--- | --- | --- | ---
含義 | 啟用系統縮放 | 應用程式已在啟動的顯示器上支援高DPI,但是沒有對其他顯示器支援,也就是說請系統在其他顯示器上幫我啟用系統縮放 | 永遠不要對我做虛擬化,因為我自己能搞定
啟用系統DPI虛擬化,可以呼叫SetProcessDpiAwareness介面。該介面有一個列舉的引數型別PROCESS_DPI_AWARENESS,但是這個引數只有在Win8.1之後才有。
Win10上有一個增強型虛擬化,可以大大優化DWM效果。
Win7可以呼叫SetProcessDpiAware介面
2、 Qt適配高DPI
基於Qt5.7測試結果
--- | Qt虛擬化 | 推薦度 | 系統虛擬化 | 推薦度 |
---|---|---|---|---|
Win7 | 只能使用整數倍放大,效果會模糊 | * | 支援系統下拉框中的浮點縮放,圖片會模糊(有些系統失效) | ** |
Win10 | 只能使用整數倍放大,字型比較清晰,但是需要適配高DPI圖片 | **** | 圖片拉伸,啟用了Win增強型DWM,效果還可以,但字型沒有Qt適配清楚 | ** |
基於Qt5.13測試結果
--- | Qt虛擬化 | 推薦度 | 系統虛擬化 | 推薦度
--- | --- | --- | --- | ----
Win7 | 同Qt5.7,但是顯示有過好於Qt5.7 | ** | 同Qt5.7 | **
Win10 | 支援系統自定義解析度,例如125%、175%
在不同解析度下分別啟動軟體後,在相同指定解析度下顯示大小不一樣
例如:在解析度125%下啟動軟體,此時不要關閉程式1並修改解析度為150%,然後在啟動一次軟體為程式2,觀察程式1和程式2的大小,發現不一大。 | **** | 同Qt5.7 | **
我們的軟體在Win10上會強制啟用增強型虛擬戶,需要對exe右鍵屬性-相容性-所有使用者設定進行禁用
3、適配DPI結論
1、Qt5.7只支援整數比例縮放
- 100%:0-149%
- 200%:150%-249%
- 300%:250%-349%
- ...往後依次類推
- 100%DPI下啟動程式,切換DPI時無法對已啟動軟體大小做出影響;反之如果非100%DPI啟動程式則是正常的。
2、Qt5.13支援系統預定義縮放比例
- 100%:100%
- 125%:125%
- ...往後依次類推,如果自定義了縮放比,預設按250%(測試結果,不一定準,有待測試多個顯示器)顯示。但不同解析度下啟動同一個軟體後(啟動的軟體不關閉切換解析度),在最後切換的解析度上觀察程式,大小都不一樣。
Qt5.7和Qt5.13都有的問題
100%DPI啟動軟體後,再次修改DPI時,軟體大小不會再次發生變化。
到這這裡高DPI測試基本結束,綜合各種情況,得出如下兩個結論:
- 高DPI適配使用Qt來做。Qt支援高DPI比windows系統縮放效果要好。
- Qt使用5.7。升級Qt到5.13時,需要升級vs至少到2015,並且軟體只能是x64版本的,否則還需要升級工具到更高版本,並且我們的依賴庫也可能需要重新編譯,成本較高,而且5.13支援高DPI比5.7多的地方我們暫時可以不需要(主要是支援系統定義好的浮點縮放),因此不做Qt升級工作。
三、Qt適配
由於升級到5.13有很多成本,暫且使用Qt5.7進行適配
使用Qt5.7適配高DPI
缺點請看一節第三小節中
Qt5.7只支援整數比例縮放
決定使用Qt5.7適配高DPI後,我們需要幹如下幾件事:
- 原生放大比例和web放大比例統一
- 系統DPI修改時,禁用重新整理
- 新增不同DPI下圖片
1、統一比例
100% 200%
2、強制重新整理
WM_DPICHANGED
接收系統DPI發生變化訊息
3、圖片適配
新增不同DPI下圖片
Qt適配完之後還是存在一些問題,比方說只支援系統已有縮放比,不能支援任意比例縮放,而且有時候還存在重新整理問題、軟體異常放大等等。
四、自己適配
業務層不需要考慮scale,只需要使用T打頭控制元件開發即可。
注:由於篇幅的緣故,T打頭的控制元件下一篇文章講解
適配專案
- 視窗大小
- 字型大小
- 間距
- 圖示
1、視窗大小
重寫頂層視窗設定介面大小函式
setFixedWidth
setFixedHeight
......
動態調整
記錄呼叫了哪些設定大小的函式,在dpi發生變化時重新設定一遍
if (testflag("setfixedWidth"))
{
setFixedWidth(width * scale);
}
2、字型大小
重新生成qss檔案
讀取原有qss檔案,使用正則表示式生成scale版本的新qss檔案。
3、間距
佈局的margin
記錄呼叫了哪些設定大小的函式,在dpi發生變化時重新設定一遍,類似於視窗大小變化時所作調整
if (testflag("margin"))
{
setContextMargin(...);
}
padding和margin
讀取原有qss檔案,使用正則表示式生成scale版本的新qss檔案。
4、圖示
工程中需要新增1x 2x 3x等不同解析度的圖示,1x圖示為正常情況下使用的圖示,2x和3x圖示分別是高解析度下的圖示
替換圖示有兩種情況,一種是使用qss方式貼圖,另一種是自繪貼圖
qss方式
預先生成高解析度下的整數倍xxx_2x.qss和xxx_3x.qss檔案,實際使用的時候在動態調整,具體方案下一篇文章講解
自繪
如果是自繪文字圖圖片,那就需要自己控制縮放比,和圖片壓縮係數,具體方案下一篇文章講解
五、參考文章
PPI vs. DPI: 有什麼區別?
High DPI Desktop Application Development on Windows
PROCESS_DPI_AWARENESS Enumeration
SetProcessDPIAware function:Win Vista開始支援的介面
SetProcessDpiAwareness function:Win8.1開始支援的介面
關於Windows高DPI的一些簡單總結
如何開發新的Qt 5.7高DPI每監視器DP