1. 结论
先说结论,在Qt5版本没有比较完美的解决方案。如果使用Qt系统提供的支持方式会出现各种小问题。如果可以的,建议升级为Qt6版本,能够更好支持高分辨率屏。而最终我在Qt5.12.12版本中,采用的方案是通过各种方法组合解决。
详细可参考知乎回答目前Qt有没有比较好解决高分屏下缩放显示的方案?
2. Qt系统自带解决方案说明
2.1 设置环境缩放
- qputenv(“QT_AUTO_SCREEN_SCALE_FACTOR”, “2”);
- QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
上面两种实现本质差不多。第一种设置环境参数的需要根据dpi计算缩放比例。第二种则系统自动进行缩放。该方案能够简单实现高分辨率屏的支持,如果使用建议直接使用第二种即可。
该方案的问题:在于在Qt5.14.x以下版本,只支持整数倍缩放。
- 在设置为100% ~ 149%范围内,Qt缩放的倍数为1。
- 在设置为150% ~ 249%范围内,Qt缩放的倍数为2。
- 在设置为250% ~ 349%范围内,Qt缩放的倍数为3。
- 往后依次类推。
这导致了当系统设置为150%,Qt程序界面会缩放成2倍,看起来效果非常的大,与系统并不协调。
2.2 使用标准配置文件
在资源qrc里添加qt.conf文件,qt/etc/qt.conf, 内容为:
[Platforms]
WindowsArguments = dpiawareness=0
这方案使得Qt程序让windows系统接管控制缩放,整体比例协调,实现简单。
缺点在于会导致界面的模糊,在我电脑测试中,效果十分模糊,个人不太接受。
2.3 结论
有条件的升级为Qt6版本,支持效果更好。无法升级的,根据实际情况选择一种接受的方案。
3. 组合方案
由于作者对于Qt自带的解决方案都不满意, 于是只好一步一步解决。
3.1 解决字体问题
所有的字体使用pt单位,不是用px单位。转换公式为 pt = px * 3 / 4,比如12px * 3 / 4 = 9pt大小。
QFont font("Microsoft YaHei");
// 小数使用
font.setPointSizeF(10.5);
// 整数使用
font.setPointSize(10);
app.setFont(font);
qss中也改为pt单位。
QMenuBar {
background: #F6F6F6;
font: 10.5pt;
}
pt单位的字体,系统根据分辨率缩放字体大小,详细可查看文章pt和px的区别是什么。
3.2 解决尺寸问题
获取系统当前的dpi,与96相除得到当前系统的缩放比例,ui使用dpi=96设计界面,根据尺寸进行按比例拉伸。包括layout布局的margins,spacing属性也需要同步拉伸(如果没修改过默认的layout的margins,spacing属性,Qt底层其实会自动拉伸的,不过为了方便我对全局的布局器都一同拉伸)。
/**
* @file style_helper.h
*/
#ifndef STYLE_HELPER_H
#define STYLE_HELPER_H
#include <QSize>
class QWidget;
class QLayout;
class StyleHelper
{
public:
explicit StyleHelper();
static QSize mainwindowSize();
static QSize mainwindowSubSize();
static QSize dialogSize();
static qreal dpiScaled(qreal value);
static void sizeScaled(QWidget *widget);
private:
///< 屏幕分辨率
static qint32 my_window_width_;
static qint32 my_window_height_;
///< 屏幕dpi值
static qint32 my_window_dpi_;
///< 屏幕缩放倍数
static qreal my_window_scale_;
};
#endif // STYLE_HELPER_H
/**
* @file style_helper.cpp
*/
#include <QScreen>
#include <QWidget>
#include <QLayout>
#include <qformlayout.h>
#include <QApplication>
#include "style_helper.h"
qint32 StyleHelper::my_window_width_ = 1920;
qint32 StyleHelper::my_window_height_ = 1080;
qint32 StyleHelper::my_window_dpi_ = 96;
qreal StyleHelper::my_window_scale_ = 0;
/**
* @brief 构造函数
*/
StyleHelper::StyleHelper()
{
QScreen *screen = QApplication::primaryScreen();
my_window_width_ = screen->geometry().width();
my_window_height_ = screen->geometry().height();
my_window_dpi_ = screen->logicalDotsPerInchX();
// ui设计使用dpi = 96
my_window_scale_ = qreal(my_window_dpi_) / 96.0;
}
/**
* @brief 根据dpi计算缩放尺寸
* @param[in] value 设计时尺寸
* @return 缩放后尺寸
*/
qreal StyleHelper::dpiScaled(qreal value)
{
#ifdef Q_OS_MAC
// mac系统dpi一直保持72
return value;
#else
return (value * my_window_scale_);
#endif
}
/**
* @brief 根据dpi缩放控件大小
* @param[in] widget 控件
*/
void StyleHelper::sizeScaled(QWidget *widget)
{
#ifdef Q_OS_MAC
return;
#else
// 修改尺寸
widget->resize(widget->width() * my_window_scale_, widget->height() * my_window_scale_);
// 调整布局器的边距
foreach (QLayout *layout, widget->findChildren<QLayout*>())
{
QMargins margins = layout->contentsMargins();
margins.setBottom(margins.bottom() * my_window_scale_);
margins.setTop(margins.top() * my_window_scale_);
margins.setLeft(margins.left() * my_window_scale_);
margins.setRight(margins.right() * my_window_scale_);
layout->setContentsMargins(margins);
layout->setSpacing(layout->spacing() * my_window_scale_);
if (layout->inherits("QGridLayout"))
{
QGridLayout *grid_layout = qobject_cast<QGridLayout *>(layout);
grid_layout->setHorizontalSpacing(grid_layout->horizontalSpacing() * my_window_scale_);
grid_layout->setVerticalSpacing(grid_layout->verticalSpacing() * my_window_scale_);
}
}
#endif
}
使用api调整widget的尺寸
// 在页面的构造函数中调用
MyDialog::MyDialog(QWidget *parent) :
QDialog(parent, Qt::MSWindowsFixedSizeDialogHint),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
// 适配分辨率大小
StyleHelper::sizeScaled(this);
}
// 在一些设置大小的地方使用
ui->tool_button->setIconSize(QSize(StyleHelper::dpiScaled(24), StyleHelper::dpiScaled(24)));
3.3 qss尺寸问题
qss中尺寸使用的px单位,改为em使用。根据实际情况,可以灵活调整,共同使用px和em单位。
/* 对控件进行拉伸,使用了em单位 */
QMenu::item {
min-width: 6.5em;
min-height: 1.2em;
background-color: transparent;
margin: 0.1em;
padding: 0em 0.5em 0em 0em;
}
/* 某些地方希望固定尺寸,不进行拉伸,则使用px单位 */
QToolButton {
border: 1px solid #FFFFFF;
border-radius: 0px;
padding: 2px;
}
3.4 结论
以上3种方法组合使用,基本满足了大部分情况,若有哪里没实现到缩放的地方,可按照该思路一步一步解决。
该方案的效果还是比较符合预期,虽然实现起来比较繁琐复杂,所以能够在开发初期考虑到该问题,还是能够比较解决的。