草庐IT

Qt5 高分辨率支持

ZeroMing222 2024-05-03 原文

1. 结论

先说结论,在Qt5版本没有比较完美的解决方案。如果使用Qt系统提供的支持方式会出现各种小问题。如果可以的,建议升级为Qt6版本,能够更好支持高分辨率屏。而最终我在Qt5.12.12版本中,采用的方案是通过各种方法组合解决。
详细可参考知乎回答目前Qt有没有比较好解决高分屏下缩放显示的方案?

2. Qt系统自带解决方案说明

2.1 设置环境缩放

  1. qputenv(“QT_AUTO_SCREEN_SCALE_FACTOR”, “2”);
  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
  // 调整布局器的边距
  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);
  
    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_);
    }
    else if (layout->inherits("QFormLayout"))
    {
      QFormLayout *form_layout = qobject_cast<QFormLayout *>(layout);
      form_layout->setHorizontalSpacing(form_layout->horizontalSpacing() * my_window_scale_);
      form_layout->setVerticalSpacing(form_layout->verticalSpacing() * my_window_scale_);
    }
    else
    {
      layout->setSpacing(layout->spacing() * 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种方法组合使用,基本满足了大部分情况,若有哪里没实现到缩放的地方,可按照该思路一步一步解决。
该方案的效果还是比较符合预期,虽然实现起来比较繁琐复杂,所以能够在开发初期考虑到该问题,还是能够比较解决的。

有关Qt5 高分辨率支持的更多相关文章

  1. Qt Designer的简单使用 - 2

    在前面两节的例子中,主界面窗口的尺寸和标签控件显示的矩形区域等,都是用C++代码编写的。窗口和控件的尺寸都是预估的,控件如果多起来,那就不好估计每个控件合适的位置和大小了。用C++代码编写图形界面的问题就是不直观,因此Qt项目开发了专门的可视化图形界面编辑器——QtDesigner(Qt设计师)。通过QtDesigner就可以很方便地创建图形界面文件*.ui,然后将ui文件应用到源代码里面,做到“所见即所得”,大大方便了图形界面的设计。本节就演示一下QtDesigner的简单使用,学习拖拽控件和设置控件属性,并将ui文件应用到Qt程序代码里。使用QtDesigner设计界面在开始菜单中找到「Q

  2. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

  3. ruby - 如何使用 readline 支持重新安装 ruby​​? - 2

    我已经按照https://github.com/wayneeseguin/rvm#installation上的说明通过RVM安装了Ruby.有关信息,我有所有文件(readline-5.2.tar.gz、readline-6.2.tar.gz、ruby-1.9.3-p327.tar.bz2、rubygems-1.8.24.tgz、wayneeseguin-rvm-stable.tgz和yaml-0.1.4.tar.gz)在~/.rvm/archives目录中,我不想在任何目录中重新下载它们方式。当我这样做时:sudo/usr/bin/apt-getinstallbuild-essent

  4. ruby-on-rails - "undefined method ` stub_request '"访问 RSpec 支持文件中的方法时 - 2

    我的Ruby-on-Rails项目中有以下文件结构,用于规范:/spec/msd/serviceservice_spec.rb/support/my_modulerequests_stubs.rb我的request_stubs.rb有:moduleMyModule::RequestsStubsmodule_functiondeflist_clientsurl="dummysite.com/clients"stub_request(:get,url).to_return(status:200,body:"clientsbody")endend在我的service_spec.rb我有:re

  5. ruby - Ruby 是否支持逐字字符串? - 2

    Ruby是否支持(找不到更好的词)非转义(逐字)字符串?就像在C#中一样:@"c:\ProgramFiles\"...或者在Tcl中:{c:\ProgramFiles\} 最佳答案 是的,您需要在字符串前加上%前缀,然后是描述其类型的单个字符。你想要的是%q{c:\programfiles\}。镐书很好地涵盖了这一点here,部分是通用分隔输入。 关于ruby-Ruby是否支持逐字字符串?,我们在StackOverflow上找到一个类似的问题: https:/

  6. ruby - 在 Ruby 1.8 中支持 Ruby 1.9 的哈希语法 - 2

    我正在编写一个Rubygem,在我的代码中使用{key:'value'}哈希语法。我的测试都在1.9.x中通过,但我(可以理解)在1.8.7中得到syntaxerror,unexpected':',expecting')'。是否有支持1.8.x的最佳实践?我是否需要使用我们的老friend=>重写代码,还是有更好的策略? 最佳答案 我认为你运气不好,如果你想支持1.8,那么你必须使用=>。像往常一样,我会提到在1.9的某些情况下您必须使用=>:如果键不是一个符号。请记住,任何对象(符号、字符串、类、float……)都可以是Ruby哈

  7. 关于Qt程序打包后运行库依赖的常见问题分析及解决方法 - 2

    目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'

  8. ruby-on-rails - Rails 是否支持监听 UDP 套接字的简洁方式? - 2

    在Rails中,什么是集成更新模型某些元素的UDP监听过程的最佳方式(特别是它将向其中一个表添加行)。简单的答案似乎是在同一个进程中使用UDP套接字对象启动一个线程,但我什至不清楚我应该在哪里做适合Rails方式的事情。有没有一种巧妙的方法来开始收听UDP?具体来说,我希望能够编写一个UDPController并在每个数据报消息上调用一个特定的方法。理想情况下,我希望避免在UDP上使用HTTP(因为它会浪费一些在这种情况下非常宝贵的空间),但我完全控制消息格式,因此我可以为Rails提供它需要的任何信息。 最佳答案 Rails是一个

  9. ruby - Watir-Webdriver 是否支持点击目标为 javascript 的链接? - 2

    我是Ruby和Watir-Webdriver的新手。我有一套用VBScript编写的站点自动化程序,我想将其转换为Ruby/Watir,因为我现在必须支持Firefox。我发现我真的很喜欢Ruby,而且我正在研究Watir,但我已经花了一周时间试图让Webdriver显示我的登录屏幕。该站点以带有“我同意”区域的“警告屏幕”开头。用户点击我同意并显示登录屏幕。我需要单击该区域以显示登录屏幕(这是同一页面,实际上是一个表单,只是隐藏了)。我整天都在用VBScript这样做:objExplorer.Document.GetElementsByTagName("area")(0).click

  10. Ruby - 不支持的密码算法 (AES-256-GCM) - 2

    我收到错误:unsupportedcipheralgorithm(AES-256-GCM)(RuntimeError)但我似乎具备所有要求:ruby版本:$ruby--versionruby2.1.2p95OpenSSL会列出gcm:$opensslenc-help2>&1|grepgcm-aes-128-ecb-aes-128-gcm-aes-128-ofb-aes-192-ecb-aes-192-gcm-aes-192-ofb-aes-256-ecb-aes-256-gcm-aes-256-ofbRuby解释器:$irb2.1.2:001>require'openssl';puts

随机推荐