原文链接:Windos高DPI系列控件(一) - 饼图

一、醉一醉

眨眼功夫,2020年过去一半了。回想最近一段时间的工作和生活,总觉得应该写点儿什么!

于是,最近有空就在想啊想,想想可以写点儿什么有用的东西好呢!刚好之前写过几篇关于高DPI的文章,不知道什么原因,阅读量不是很高,因此打算以高DPI为索引开始引入一系列的控件使用案例,包括Qt自带的控件、简单图表和一些复杂的图表。

  1. Qt自带的控件就不说了,高DPI框架几乎可以完美适配
  2. 简单的图表这里主要会引入柱状图、折线图、饼图等
  3. 复杂图表主要是定制一些股票看盘相关图表,例如分时图、k线图等

对于大众软件来说,友好的支持4k显示器真的很有必要呢。一款好的大众桌面端软件需要适配各种操作系统,从快要被人们遗忘的Xp到现在占有率较高的Win10,如果想拥有一个好的用户体验,高DPI是必须要好好适配滴,这里作者准备了一个系列的高DPI控件适配文章分享给大家,主要整理我工作中遇到的各种控件,适配到已经开发好的高DPI框架中,并做出演示demo,提供给有需要的同学,除过整理已有的控件,更多的是会开发一些更有意义的新控件,比如股票中的分时图、k线等。

目的:

  1. 推广我自己适配高DPI的方案,供大家讨论,是否有更好的优化空间
  2. 整理股票相关的控件,适配到我的高DPI框架中,提供给有需要的同学参考
  3. 阶段性整理我自己的知识库,让零散的知识点汇聚起来

二、效果展示

如下图所示,适配高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的饼图绘制代码,仅供大家参考,并不能通过编译。

四、相关文章

  1. Qt之高DPI显示器(一) - 解决方案整理
  2. Qt之高DPI显示器(二) - 自适配解决方案分析
  3. Qt之自绘制饼图

值得一看的优秀文章:

  1. 财联社-产品展示
  2. 广联达-产品展示
  3. Qt定制控件列表
  4. 牛逼哄哄的Qt库
如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!

很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords

  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


Windows高DPI系列控件(一) - 饼图的更多相关文章

  1. Windows高DPI系列控件(二) - 柱状图

    目录 一.QCP 二.效果展示 三.高DPI适配 1.自定义柱状图 2.新的柱状图 3.测试代码 四.相关文章 原文链接:Windows高DPI系列控件(二) - 柱状图 一.QCP QCP全称QCu ...

  2. Windows UWP开发系列 – 控件默认样式

    今天用一个Pivot控件的时候,想修改一下它的Header样式,却发现用Blend和VS无法导出它的默认样式了,导致无法下手,不知道是不是Blend的bug. 在网上搜了一下,在MSDN上还是找到了它 ...

  3. 【Windows编程】系列第二篇:Windows SDK创建基本控件

    在Win32 SDK环境下,怎么来创建常用的那些基本控件呢?我们知道如果用MFC,简单的拖放即可完成大多数控件的创建,但是我们既然是用Windows SDK API编程,当然是从根上解决这个问题,实际 ...

  4. 【WPF】DPI对控件定位产生的影响

    原文:[WPF]DPI对控件定位产生的影响 需求 程序界面上是一个Window,当用户点击桌面上除此Window之外的任何地方,都要把这个window隐藏掉.程序有个托盘图标,点击托盘图标不能隐藏wi ...

  5. [深入浅出Windows 10]QuickCharts图表控件库解析

    13.4 QuickCharts图表控件库解析     QuickCharts图表控件是Amcharts公司提供的一个开源的图表控件库,这个控件库支持WPF.Silverlight.和Windows等 ...

  6. Dev系列控件的AJAX (转)

    介绍Dev系列控件在前台也就是客户端的一些常用方法介绍以及前后台异步通信的方法. 一.Dev Data Edit控件通用属性以及方法: 属性 1.GetEnabled():返回控件是否为可操作状态 2 ...

  7. asp.net2.0安全性(4)--Login系列控件--转载来自车老师

    前面主要说了与安全相关的一系列的类,现在我们使用这些类就可以做出我们自己的安全系统了.其实微软的目的远不至于此,下面我们就来看一下微软为我们提供的Login系列控件. Login系列控件是微软为了简化 ...

  8. Dev系列控件的AJAX使用Demo

    一.Dev Data Edit控件通用属性以及方法: 属性 1.GetEnabled():返回控件是否为可操作状态 2.GetText():返回控件的Text的值 3.SetEnabled():设置控 ...

  9. [深入浅出Windows 10]分屏控件(SplitView)

    4.18 分屏控件(SplitView) 分屏控件(SplitView)是Windows 10新增的控件类型,也是Windows 10通用应用程序主推的交互控件,通常和一个汉堡按钮搭配作为一种抽屉式菜 ...

随机推荐

  1. java实现第三届蓝桥杯数据压缩

    数据压缩 某工业监控设备不断发回采样数据.每个数据是一个整数(0到1000之间).各个数据间用空白字符(空格,TAB或回车换行)分隔.这些数据以文本形式被存储在文件中. 因为大多数时候,相邻的采样间隔 ...

  2. python json unicode utf-8处理总结

    1.直接输出字典中文 在python中经常遇见直接print dict(字典),或者dict转json,但是没有给特定的参数,然后打印json字符串,输出的中文就成了unicode码的情况,如下: d ...

  3. HDFS ha 格式化报错:a shared edits dir must not be specified if HA is not enabled.

    错误内容: Formatting using clusterid: CID-19921335-620f-4e72-a056-899702613a6b2019-01-12 07:28:46,986 IN ...

  4. 网络编程-Netty-Reactor模型

    目录 # 摘要 高性能服务器 Reactor模式 Reactor单线程模型设计 Reactor多线程模型设计 主从Reactor多线程模型设计 Netty Reactor模型设计 参考 你的鼓励也是我 ...

  5. CSS3弹性布局内容对齐(justify-content)属性使用详解

    内容对齐(justify-content)属性应用在弹性容器上,把弹性项沿着弹性容器的主轴线(main axis)对齐. 该操作发生在弹性长度以及自动边距被确定后. 它用来在存在剩余空间时如何加以分配 ...

  6. php实现登录失败次数限制

    需求:同一个账号在同一个IP地址连续密码输错一定次数后,这个账号是会被锁定30分钟的. 实现思路: 需要一个表(user_login_info)负责记录用户登录的信息,不管登录成功还是失败都记录.并且 ...

  7. css固定宽高DIV内部元素垂直居中的方法

    应用案例 案例是这样的,一个外层div,高宽是固定的,但是里面内容不是固定的.很多朋友的做法是头部加一个padding或者margin,这样,里面内容显得貌似是居中了,但是假如内容变化,这样头部的固定 ...

  8. acm对拍程序 以及sublime text3的文件自动更新插件auto refresh

    acm等算法比赛常用---对拍 以及sublime text3的文件自动更新插件auto refresh 对拍 对拍即程序自动对比正确程序的运行结果和错误程序的运行结果之间的差异 废话少说, 直接上操 ...

  9. 2019-02-07 selenium...

    今天是超级郁闷的一天 看教程 下了mysql-----配置-----不会----查资料------2小时后 mongodb-----配置------不会------查资料------1小时后 然后是各 ...

  10. mysql小数类型

    原文链接:https://blog.csdn.net/weixin_42047611/article/details/81449663 MySQL 中使用浮点数和定点数来表示小数. 浮点类型有两种,分 ...