2016-10-03 136 views
11

我已阅读官方Qt documentation以及StackOverflow上关于Qt中对高DPI支持的许多文章和问题。他们都专注于移植旧应用程序,并尽可能少地进行修改。如何开发新的Qt 5.7+高DPI每个监视器DPI感知应用程序?

但是,如果我要启动一个全新的应用程序,并且打算支持每台显示器支持DPI的应用程序,那么最佳方法是什么?

如果我理解正确,Qt::AA_EnableHighDpiScaling是我想要的相反。我应该实际上禁用HighDpiScaling并在运行时手动计算所有尺寸?

许多建议表示不使用大小,而是使用浮动布局。但是在许多情况下,希望存在至少最小宽度和/或最小高度。由于Qt Designer只允许我以绝对像素来表示值,所以正确的方法是什么?如果显示器分辨率发生变化,我应该在哪里放置代码来重新计算尺寸?

或者我应该只是去自动缩放?

从以前的Qt应用程序我的解决方案(没有得到很好的测试)

在我的旧的应用程序,我试图添加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(); 
    } 

这是从主叫:

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

在Win32上,我们发现成功简单地查询显示器dpi设置的操作系统(GetDeviceCaps),除以96.0,并设置env变量'QT_SCALE_FACTOR'以匹配。有权衡。在Mac上,我们不需要做任何事情。因人而异。 – selbie

+0

@selbie,如何设置QT_SCALE_FACTOR地址每监视器缩放? – AlexanderVX

+0

也许这个QT_SCREEN_SCALE_FACTORS [list]指定每个屏幕的比例因子。这不会更改磅值字体的大小。此环境变量主要用于调试,或者用错误的EDID信息(扩展显示标识数据)解决显示器问题。但不确定,任何人都在尝试从应用内设置它? – AlexanderVX

回答

7

如果我要启动一个全新的应用程序,并打算通过支持每台显示器的DPI认识 ,那么最佳方法是什么?

我们不依赖Qt自动缩放每个显示器的DPI感知模式。至少基于Qt 5.7的应用程序Qt::AA_EnableHighDpiScaling集不会这样做,“高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-55449QTBUG-55510显示了该功能背后的意图。顺便说一句,有QTBUG-55510编程解决方案设置Qt DPI意识而不修复qt.conf提供(由于它使用'私人'的Qt实现类,没有任何通知与更新的Qt版本改变接口自行决定使用。

而且您表达了正确的方法来执行每个显示器DPI感知模式下的缩放比例。不幸的是除了当时没有太多选择。不过,有一些编程方式可以帮助窗口缩放时的事件处理,当它从一个监视器移动到另一个监视器时。在这个问题的头似resizeWidget的方法(一个,也不多)应该使用类似(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; 

这是相当困难的,去麻烦的路,我使出使应用通过让用户选择模式并重新启动应用程序进程(修复qt.conf或在应用程序启动时从QTBUG-55510开始解决方法)在系统和每台显示器DPI感知模式之间切换。我们希望Qt公司意识到需要针对小部件自动缩放的每个监视器DPI感知模式。为什么我们需要它(?)是另一个问题。在我的情况下,我有每个监视器在自己的应用程序小部件画布内渲染,应该缩放。

首先,从@selbie我意识到读书的评论对这个问题也许有一种方法来尝试设置QT_SCREEN_SCALE_FACTORS而应用程序启动:

QT_SCREEN_SCALE_FACTORS [名单]指定每个 屏幕比例因子。这不会更改磅值字体的大小。这个 环境变量主要用于调试,或者为解决错误的EDID信息(扩展显示标识 数据)而工作的 监视器。

我然后读取关于如何应用多个屏幕要素Qt blog并试图对于其中4K列出第一(主)4K和1080p监视器执行以下。

qputenv("QT_SCREEN_SCALE_FACTORS", "2;1"); 

这确实有点帮助:几乎正确渲染引入缺陷与窗口的大小,同时从一个监视器移动窗口到另一个很像QTBUG-55449一样。如果客户认为当前的应用程序行为是一个错误(我们通过System DPI Aware为所有显示器DPI创建相同的基础),我想我会采用WM_DPICHANGED + QT_SCREEN_SCALE_FACTORS方法。尽管如此,Qt还没有准备好使用的解决方案。

+0

谢谢你的答案。在Android编程中缺乏对简单的highdpi解决方案的支持使我疯狂。有什么我应该添加到我的resizeWidgets功能?你知道任何与Qt highdpi功能的开源演示应用程序吗? thx –

+0

@TomášNavara尚未察觉到开源解决方案。我尝试过这种方式是一个做太多例程缩放的矿区。您已经显示了对需要以编程方式完成的事情的理解,但是在Windows中,您可以利用WM_DPICHANGED,MAYBE设置QT_SCREEN_SCALE_FACTORS [list]将在您启动应用程序时适用于已连接的显示器。我已经为此添加了几个声明。 – AlexanderVX

+0

所以只是为了澄清 - 当我在接收dpichange事件时从代码中完成所有工作时,它有什么优势来指定dpiawareness = 2?那么linux呢? –