原文地址:Pyside 开发 DPI 自适应窗口程序技巧
作者:风叔
现在都是超清屏的时代了。PC虽然没有手持设备需要考虑尺寸的问题,但是由于这些超清屏的出现,导致不同电脑上的DPI设置大不相同。而此时如果程序开发如果还偷懒使用像素对齐的话,在高DPI设备上就会出现扭曲变形的问题。
最近我一直在用 PySide 在 Python 下做 GUI 界面开发。今天忽然想到 DPI 缩放的问题,于是做了些尝试。网上似乎现在也没有比较好的教程(中英文我都没搜到),所以我把心得写在这里,希望对同样遇到这个问题的人有用。
一、利用 layout 来组织控件
为了使窗口在不同DPI下不变形,而且能够在高DPI下自适应的关键是两点: 利用 layout 来组织窗口,使空间能随着窗口尺寸和显示内容自动调整大小;以及,根据DPI自动调整窗口的大小。
layout 是 Qt 一个非常强大的功能,主要用来对齐和排列窗口,个人推荐无论在做任何程序开发都是用 layout 来对控件进行排列,而不是使用像素对齐。其在 Qt Designer 中 layout 控件看起来是这样的:
具体使用方式自己去感受下。这里有个很好的教程(需翻墙):
http://www.youtube.com/watch?v=2edb0VOkx-k
我的窗口排完了后是这样的:
运行一下就可以发现无论如何调整窗口大小,控件都会自适应。而且就算里面文字随DPI大小缩放后,空间依然能保持整齐。
二、根据系统DPI自动调整
为了保持窗口大小的一致性,另外需要做的就是针对系统DPI大小进行窗口调整。
Windows 下高分辨显示器的放大功能,主要是通过调整系统DPI实现的。比如默认的100%大小的系统界面是96 DPI (奇怪我记得早些年我的印象是75,可能是我记茬了)。而放大到125% 则是将 DPI x 125 %,即将系统DPI设定到120 DPI。
要保持窗口在不同 DPI 下有一致的外观,其实就是简单将窗口尺寸放大和 Windows 一样的倍数。比如下120 DPI 下将窗口放大1.25倍,由于控件是使用 layout 对齐的,因此其位置也会进行相应调整。
放大的倍数 = 当前系统DPI / 默认DPI(96)。Windows API 中还发现,其实 DPI 可以在 X 轴和 Y 轴实现不同比例缩放。所以程序的长和宽最好分开针对 X 和 Y 轴的DPI缩放比例进行设置。
上面的操作需要对 Windows API 进行访问,在 Python 下需要安装一个第三方的库 PyWin32,下载地址如下:
通过调用 win32print.GetDeviceCaps 函数可以获得系统在 X 和 Y 轴的当前DPI值(查找这个函数简直用掉了我一生的时间!)。我写了两个函数,代码如下,通过这两个函数可以返回系统的 DPI 以及所需要的放大倍数。
#!/usr/bin/env python2.7
"""
Contains some functions related to windows api.
Linzhou (linzhou.zhang.china@gmail.com)
"""
import win32gui
import win32print
def get_win_dpi():
""" this function get the dpi on X and Y axis of default windows desktop.
In:
none
Out:
x_dpi: dpi on x axis. [int]
y_dpi: dpi on y axis. [int]
"""
para_x = 88 # magic number of windows API for x axis
para_y = 90 # magic number of windows API for y axis
hdc = win32gui.GetDC(0)
x_dpi = win32print.GetDeviceCaps(hdc, para_x)
y_dpi = win32print.GetDeviceCaps(hdc, para_y)
return x_dpi, y_dpi
#enddef get_windows_dpi
def get_program_scale_factor():
""" This function calculate the scale factor based on the current DPI setting.
In:
none
Out:
scale_x: scale factor on x axis. [float]
scale_y: scale factor on y axis. [float]
"""
default_dpi_x = 96.0 # default x axis dpi setting for windows
default_dpi_y = 96.0 # default y axis dpi setting for windows
current_dpi_x, current_dpi_y = get_win_dpi()
current_dpi_x = float(current_dpi_x)
current_dpi_y = float(current_dpi_y)
scale_x = current_dpi_x / default_dpi_x
scale_y = current_dpi_y / default_dpi_y
return scale_x, scale_y
#enddef get_window_scale_factor
if __name__ == '__main__':
#Test
scale_x, scale_y = get_program_scale_factor()
print scale_x
print scale_y
scale_x 和 scale_y 就是需要放大的倍数,如果系统当前DPI是96的话,那么它们就是1。
在代码中将窗口大小乘以这个系数,窗口就自动对DPI实现大小调整了。示例如下:
#generate window
app=QtGui.QApplication(sys.argv)
Window=NewMainWindow()
# Get scale factor and resize
scale_x, scale_y = get_program_scale_factor()
init_size_x, init_size_y = 820, 505
Window.resize(init_size_x * scale_x, init_size_y * scale_y)
Window.show()
Done!就是这么简单。
三、示例:
下面是我自己的一个程序在 Windows 8.0 下 100%、120% 和150% DPI 状态下运行情况,可以看到基本是等比例放大,而且没有任何因为DPI调整而变形的情况。
![[转载]Pyside <wbr>开发 <wbr>DPI <wbr>自适应窗口程序技巧 [转载]Pyside <wbr>开发 <wbr>DPI <wbr>自适应窗口程序技巧](https://i-blog.csdnimg.cn/blog_migrate/74fd552a7dcc7c39c27c3abf44a1ed3b.jpeg)
120%
![[转载]Pyside <wbr>开发 <wbr>DPI <wbr>自适应窗口程序技巧 [转载]Pyside <wbr>开发 <wbr>DPI <wbr>自适应窗口程序技巧](https://i-blog.csdnimg.cn/blog_migrate/e6c11a12412c9b360ab30d7202ff1463.jpeg)
150%