Qt之高DPI显示器(一) - 解决方案整理
最近一直在处理高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文件,实际使用的时候在动态调整,具体方案下一篇文章讲解
自绘
如果是自绘文字图图片,那就需要自己控制缩放比,和图片压缩系数,具体方案下一篇文章讲解
五、参考文章
High DPI Desktop Application Development on Windows
PROCESS_DPI_AWARENESS Enumeration
SetProcessDPIAware function:Win Vista开始支持的接口
SetProcessDpiAwareness function:Win8.1开始支持的接口
值得一看的优秀文章:
![]() |
![]() |
很重要--转载声明
本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。
Qt之高DPI显示器(一) - 解决方案整理的更多相关文章
- Qt之高DPI显示器(二) - 自适配解决方案分析
目录 一.回顾 二.框架说明 1.ICallDPIChanged 2.IDPIHelper 3.悬浮窗体管理器 三.方案分析 1.窗口大小 2.字体大小 3.间距 4.图标 四.相关文章 原文链接:Q ...
- Windows高DPI系列控件(一) - 饼图
目录 一.醉一醉 二.效果展示 三.高DPI适配 1.高DPI框架运作 2.适配高DPI 3.适配饼图 四.相关文章 原文链接:Windos高DPI系列控件(一) - 饼图 一.醉一醉 眨眼功夫,20 ...
- Windows高DPI系列控件(二) - 柱状图
目录 一.QCP 二.效果展示 三.高DPI适配 1.自定义柱状图 2.新的柱状图 3.测试代码 四.相关文章 原文链接:Windows高DPI系列控件(二) - 柱状图 一.QCP QCP全称QCu ...
- 关于Windows高DPI的一些简单总结
我们知道,关于高DPI的支持, Windows XP时代就开始有了, 那时关于高DPI的支持比较简单, 但是从Vista/Win7 到现在Win8 /Win8.1, Windows关于高DPI的支持已 ...
- 关于Windows高DPI的一些简单总结(Window上一般默认是96 dpi 作为100% 的缩放比率)
我们知道,关于高DPI的支持, Windows XP时代就开始有了, 那时关于高DPI的支持比较简单, 但是从Vista/Win7 到现在Win8 /Win8.1, Windows关于高DPI的支持已 ...
- Ubuntu下QT控制台程序无法运行的解决方案以及XTerm的配置方法
Ubuntu下QT控制台程序无法运行的解决方案以及XTerm的配置方法 最近由于老师要求要在Ubuntu下QT上进程多线程服务器的开发,虽然只是单纯的调用qt的network模块,但是为了避免麻烦,我 ...
- Windows 下的高 DPI 应用开发(UWP / WPF / Windows Forms / Win32)
本文将介绍 Windows 系统中高 DPI 开发的基础知识.由于涉及到坐标转换,这种转换经常发生在计算的不知不觉中:所以无论你使用哪种 Windows 下的 UI 框架进行开发,你都需要了解这些内容 ...
- 解决VS在高DPI下设计出的Winform程序界面变形问题
在目前高分屏流行的情况下,windows缩放与布局仍然设置为100%就显得太小(特别是笔记本),通常会调整为125%或150%, VS在缩放与布局设置为非100%的时候,就会自动启动DPI感知模式,以 ...
- Win10强制程序高DPI缩放设置
起因 工作原因,需要在win10上安装数个古老vc版本(vc6,vc2008,vc2010),但是显示器是2K的,DPI缩放有问题 尝试 VC6比较好解决:右键,属性,兼容性,更改高DPI设置,勾选替 ...
随机推荐
- oop面向对象知识总结 静态成员和友元
第十一章 静态成员和友元 11.1 静态成员 1.C++类当中的静态数据成员仍借用保留字static,但是与之前的静态全局变量,静态局部变量以及静态函数没有关系. 2.静态数据成员不占用具体对象的数据 ...
- Blue:贪心,单调队列
考场上什么都没想. 显然在扯淡了,应该说是刚开始想了一些没用的. 有决策单调性,所以二分答案? 好,那就二分答案.想想怎么检查每只蛤能不能都跳到终点? 那么每只蛤都不能掉队啊. 如果你现在遇到了一个石 ...
- NOIP的模板--考前复习
距离NOIP还有25天 可以去放弃一些巨难得题目去搞一些模板了 -------在校老师的原话 一·快排 虽然可以手打,最好用STL,里面有很多优化,会快很多 #include<iostream& ...
- 『题解』洛谷P2296 寻找道路
更好的阅读体验 Portal Portal1: Luogu Portal2: LibreOJ Description 在有向图\(\mathrm G\)中,每条边的长度均为\(1\),现给定起点和终点 ...
- Tomcat+nginx+Keepalived部署实现集群
Tomcat+nginx+Keepalived部署实现集群 环境说明: 系统:Centos-7 主机:Centos-7 x3 IP地址: 服务器1(192.168.10.102/24) 服务器2(19 ...
- 201871010114-李岩松《面向对象程序设计(java)》第四周学习总结
项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...
- [LC]237题 Delete Node in a Linked List (删除链表中的节点)(链表)
①中文题目 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点. 现有一个链表 -- head = [4,5,1,9],它可以表示为: 示例 1: 输入: hea ...
- JVM浅谈
**前言** 由于先前也遇到过一些性能问题,OOM算是其中的一大类了.因此也对jvm产生了一些兴趣.自己对jvm略做了些研究.后续继续补充. **从oom引申出去** 既然说到oom,首先需要知道oo ...
- 虚拟机和容器docker
云计算中最主要的技术就是虚拟机,开源虚拟机已经kvm已经集成到Linux内核!针对虚拟机浪费资源(CPU.内存.存储等)较大的缺陷,google力推Docker容器和容器管理平台Kubernetes. ...
- Error: Cannot find module 'less'
这是webpack.config 代码中引入 报错信息(在main.js中引入也是这个报错 解决办法: 安装less: npm install less --save-dev 转自: https:// ...

