问题描述
我已经阅读了,以及有关StackOverflow的许多文章和问题关于Qt中对DPI的高支持。所有这些都集中于移植旧应用程序并使它们尽可能少地更改。
I have read the official Qt documentation and many articles and questions on StackOverflow about high DPI support in Qt. All of them focus on porting old applications and making them work with as least changes as possible.
但是,如果我要启动一个全新的应用程序,目的是支持每个监视器的DPI知道应用程序的最佳方法是什么?
But if I were to start a brand new application, with the intention to support per-monitor DPI aware app, what is the best approach?
如果我理解正确, Qt :: AA_EnableHighDpiScaling
与我想要的相反。我实际上应该禁用HighDpiScaling并在运行时手动计算所有尺寸吗?
If I understand correctly, Qt::AA_EnableHighDpiScaling
is the very opposite of what I want. I should actually disable HighDpiScaling and calculate all the dimensions manually on runtime?
许多建议都说不要完全使用尺寸,而要使用浮动布局。但是在许多情况下,期望至少存在最小宽度和/或最小高度。由于Qt Designer仅允许我将值放入绝对像素中,所以正确的方法是什么?如果显示器分辨率发生变化,我应该在哪里放置代码以重新计算尺寸?
Many of the suggestions say not to use sizes at all, to use floating layouts. But in many cases at least minimum width and/or minimum height is desired to be present. As Qt Designer only allows me to put values in absolute pixels, what is the right approach? Where should I place the code to recalculate dimensions if monitor resolution changes?
还是应该使用自动缩放功能?
Or should I just go with the automatic scaling?
在我尝试添加HighDPI支持的一个较旧的应用程序中,我使用了这种方法-列出所有DOM子级,并按一定比例逐一调整它们的大小比。比率= 1将产生等于我在Qt Designer中指定的尺寸。
In one of my older apps where I tried to add HighDPI support, I used this approach - list all children of DOM and resize them one by one given some ratio. Ratio = 1 would produce dimensions equal to the ones I specified in Qt Designer.
void resizeWidgets(MyApp & qw, qreal mratio)
{
// ratio to calculate correct sizing
qreal mratio_bak = mratio;
if(MyApp::m_ratio != 0)
mratio /= MyApp::m_ratio;
// this all was done so that if its called 2 times with ratio = 2, total is not 4 but still just 2 (ratio is absolute)
MyApp::m_ratio = mratio_bak;
QLayout * ql = qw.layout();
if (ql == NULL)
return;
QWidget * pw = ql->parentWidget();
if (pw == NULL)
return;
QList<QLayout *> layouts;
foreach(QWidget *w, pw->findChildren<QWidget*>())
{
QRect g = w->geometry();
w->setMinimumSize(w->minimumWidth() * mratio, w->minimumHeight() * mratio);
w->setMaximumSize(w->maximumWidth() * mratio, w->maximumHeight() * mratio);
w->resize(w->width() * mratio, w->height() * mratio);
w->move(QPoint(g.x() * mratio, g.y() * mratio));
}
foreach(QLayout *l, pw->findChildren<QLayout*>())
{
if(l != NULL && !(l->objectName().isEmpty()))
layouts.append(l);
}
foreach(QLayout *l, layouts) {
QMargins m = l->contentsMargins();
m.setBottom(m.bottom() * mratio);
m.setTop(m.top() * mratio);
m.setLeft(m.left() * mratio);
m.setRight(m.right() * mratio);
l->setContentsMargins(m);
l->setSpacing(l->spacing() * mratio);
if (l->inherits("QGridLayout")) {
QGridLayout* gl = ((QGridLayout*)l);
gl->setHorizontalSpacing(gl->horizontalSpacing() * mratio);
gl->setVerticalSpacing(gl->verticalSpacing() * mratio);
}
}
QMargins m = qw.contentsMargins();
m.setBottom(m.bottom() * mratio);
m.setTop(m.top() * mratio);
m.setLeft(m.left() * mratio);
m.setRight(m.right() * mratio);
// resize accordingly main window
qw.resize(qw.width() * mratio, qw.height() * mratio);
qw.setContentsMargins(m);
qw.adjustSize();
}
从main调用:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyApp w;
// gets DPI
qreal dpi = a.primaryScreen()->logicalDotsPerInch();
MyApp::resizeWidgets(w, dpi / MyApp::refDpi);
w.show();
return a.exec();
}
我认为这不是一个好的解决方案。鉴于我从头开始,并且可以完全根据最新的Qt标准自定义代码,我应该使用哪种方法来获取HighDPI应用程序?
I don't consider this a good solution. Given that I am starting fresh and I can fully customise my code to the latest Qt standards, what approach should I use to get HighDPI apps?
推荐答案
我们不依靠Qt在每个监视器DPI感知模式下自动缩放。至少具有 Qt :: AA_EnableHighDpiScaling
设置的基于Qt 5.7的应用程序不能做到这一点,并且高DPI缩放更精确地绘制,而不管像素密度如何。
We don't rely on Qt for automatic scaling in per-monitor DPI aware mode. At least Qt 5.7-based app with Qt::AA_EnableHighDpiScaling
set does not do that and 'high DPI scaling' is more of accurate drawing regardless of pixel density.
要调用每个监视器的DPI感知模式,您需要在投影可执行文件的同一目录中修改 Qt.conf
文件是:
And to invoke per-monitor DPI aware mode you need to modify Qt.conf
file at the same directory where you project executable file is:
[Platforms]
# 1 - for System DPI Aware
# 2 - for Per Monitor DPI Aware
WindowsArguments = dpiawareness=2
# May need to define this section as well
#[Paths]
#Prefix=.
否,不是相反,而是不同的事。有一些Qt错误已作为无错误错误关闭:和,以显示该功能的意图。顺便说一句,有一种编程解决方案,用于设置Qt DPI意识而无需修复<$提供了c $ c> qt.conf (可自行决定使用,因为它使用私有 Qt实现类来更改接口,而对于新的Qt版本则不作任何通知)。
No, it is not an opposite but a different thing. There is a couple of Qt bugs closed as no-bugs: QTBUG-55449 and QTBUG-55510 that show the intent behind the feature. BTW, there is QTBUG-55510 a programmatic workaround for setting Qt DPI awareness without fixing qt.conf
is provided (use at own discretion because it uses 'private' Qt implementation classes that change the interface without any notice with newer Qt version).
您表示了在按显示器的DPI感知模式下进行缩放的正确方法。不幸的是,除了当时没有太多选择。但是,当窗口从一个监视器移动到另一个监视器时,有一些编程方法可以辅助事件处理以实现窗口缩放。该问题开头的 resizeWidget
(一个,不多)之类的方法应使用(Windows)之类的方法来调用:
And you expressed the correct approach to do the scaling in per-monitor DPI aware mode. Unfortunately except that there is no much alternative at the time. There programmatic ways to assist the event handling for the window scaling when it is moved from one monitor to another, though. The method like resizeWidget
(one, not many) at the head of this question should be called using something like (Windows):
// we assume MainWindow is the widget dragged from one monitor to another
bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
MSG* pMsg = reinterpret_cast<MSG*>(message);
switch (pMsg->message)
{
case WM_DPICHANGED:
// parameters TBD but mind that 'last' DPI is in
// LOWORD(pMsg->wParam) and you need to detect current
resizeWidget(monitorRatio());
break;
这是一种非常困难且麻烦的方法,我诉诸使该应用程序在System和通过让用户选择模式并重新启动应用程序进程来实现每监视器DPI感知模式(修复 qt.conf
或通过在应用启动时)。我们的希望是Qt公司意识到需要一种针对显示器的DPI感知模式,并且需要自动缩放控件。我们为什么需要它(?)是另一个问题。就我而言,我在自己的应用窗口小部件画布中有按显示器渲染,应该按比例缩放。
This is quite difficult and troublesome way to go and I resorted to enable the app to switch between System and Per Monitor DPI Aware modes by letting the user to choose the mode and restart the app process (either fixing qt.conf
or doing workaround from QTBUG-55510 at the app start). Our hope is that Qt company realizes there is a need for per-monitor DPI aware mode with auto scaling for widgets. Why would we need it (?) is another question. In my case I have per-monitor rendering within own app widget canvas which supposed to be scaled.
首先,从@selbie读到对此问题的评论,我意识到也许有一种方法可以尝试在应用启动时尝试设置QT_SCREEN_SCALE_FACTORS:
At first, reading the comment to this question from @selbie I realized maybe there is a way to try to set QT_SCREEN_SCALE_FACTORS while the app starts:
然后我阅读,介绍如何应用多种屏幕因素,并尝试对首先列出4K(主要)的4K和1080p显示器执行以下操作。
I then read Qt blog on how to apply multiple screen factors and attempted to do the below for 4K and 1080p monitors where 4K is listed first (main).
qputenv("QT_SCREEN_SCALE_FACTORS", "2;1");
这确实有所帮助:几乎正确的渲染但是引入了窗口缺陷大小,同时将窗口从一个监视器移动到另一个监视器,就像一样。如果客户将当前应用程序行为视为错误,我想我会采用 WM_DPICHANGED
+ QT_SCREEN_SCALE_FACTORS
方法通过系统DPI感知为所有显示器DPI提供相同的基础)。仍然没有Qt可以立即使用的解决方案。
That does helps a bit: almost correct rendering but introduces defects with window size while moving the window from one monitor to another pretty much like QTBUG-55449 does. I guess I will go with WM_DPICHANGED
+ QT_SCREEN_SCALE_FACTORS
approach if the customer considers current app behavior as a bug (we make the same base for all monitors DPI via System DPI Aware). Still there is no ready to use solution from Qt yet.
这篇关于如何处理每台监控器DPI感知的新Qt 5.7+高DPI应用程序的开发?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!