我已经阅读了有关Qt中高DPI支持的官方Qt documentation和许多关于StackOverflow的文章和问题。所有这些都专注于移植旧应用程序并使它们尽可能少地进行更改。
但如果我要开始一个全新的应用程序,打算支持每个监视器的DPI感知应用程序,最好的方法是什么?
如果我理解正确,Qt::AA_EnableHighDpiScaling
与我想要的完全相反。我应该实际禁用HighDpiScaling并在运行时手动计算所有尺寸?
许多建议说根本不使用尺寸,使用浮动布局。但是在许多情况下,希望存在至少最小宽度和/或最小高度。由于Qt Designer只允许我将值放在绝对像素中,所以正确的方法是什么?如果显示器分辨率发生变化,我应该在何处放置代码以重新计算尺寸?
或者我应该选择自动缩放?
在我试图添加HighDPI支持的一个较旧的应用程序中,我使用了这种方法 - 列出DOM的所有子项,并在给定比例的情况下逐个调整它们的大小。 Ratio = 1将产生的尺寸等于我在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应用程序?
答案 0 :(得分:8)
如果我要开始一个全新的应用程序,意图 支持每个监控DPI意识,最好的方法是什么?
我们不依赖于Qt在每个监视器DPI感知模式下进行自动缩放。至少设置Qt::AA_EnableHighDpiScaling
的基于Qt 5.7的应用程序不会这样做,并且“高DPI缩放”更精确的绘制,无论像素密度如何。
要调用每个监视器的DPI感知模式,您需要在项目可执行文件所在的同一目录中修改Qt.conf
文件:
[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 :: AA_EnableHighDpiScaling就是这样 与我想要的相反。我应该实际禁用HighDpiScaling和 在运行时手动计算所有尺寸?
不,这不是对立面而是另一回事。有几个Qt错误被关闭为无错误:QTBUG-55449和QTBUG-55510,显示该功能背后的意图。顺便说一下,QTBUG-55510提供了一个程序化的解决方法,用于设置Qt DPI意识而不修复qt.conf
(由于它使用'私有'Qt实现类来改变界面,而没有任何新的Qt通知,因此可自行决定使用版本)。
并且您表达了在每个监视器DPI感知模式下进行扩展的正确方法。不幸的是,当时没有其他选择。但是,当程序从一个监视器移动到另一个监视器时,有程序化的方法来协助事件处理窗口缩放。应该使用类似(Windows)的方法调用类似resizeWidget
(一个,不多)的方法:
// 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;
这是一个非常困难和麻烦的方法,我通过让用户选择模式并重新启动应用程序进程(修复qt.conf
)来启用应用程序在系统和每个监视器DPI感知模式之间切换或者在应用程序启动时从QTBUG-55510执行解决方法)。我们希望Qt公司意识到需要为每个监视器DPI识别模式以及小部件的自动扩展。我们为什么需要它(?)是另一个问题。在我的情况下,我在自己的应用程序小部件画布中有每个监视器渲染,应该缩放。
首先,从@selbie阅读此问题的评论我意识到在应用程序启动时可能有办法尝试设置QT_SCREEN_SCALE_FACTORS:
QT_SCREEN_SCALE_FACTORS [list]指定每个的比例因子 屏幕。这不会改变点大小字体的大小。这个 环境变量主要用于调试或解决 监视器有错误的EDID信息(扩展显示识别 数据)。
然后我阅读了Qt blog关于如何应用多个屏幕因素的信息,并尝试在4K和1080p显示器上执行以下操作,其中4K首先列出(主要)。
qputenv("QT_SCREEN_SCALE_FACTORS", "2;1");
这确实有点帮助:几乎正确渲染,但引入窗口大小的缺陷,同时将窗口从一个监视器移动到另一个监视器,就像QTBUG-55449一样。如果客户将当前的应用行为视为错误(我们通过系统DPI Aware为所有监视器DPI建立相同的基础),我想我会采用WM_DPICHANGED
+ QT_SCREEN_SCALE_FACTORS
方法。仍然没有准备好使用Qt的解决方案。