编程语言
首页 > 编程语言> > c – 如何开发新的Qt 5.7高DPI每监视器DPI感知应用程序?

c – 如何开发新的Qt 5.7高DPI每监视器DPI感知应用程序?

作者:互联网

我已经阅读了官方Qt documentation和关于StackOverflow的许多关于Qt中高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();
    }

从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应用程序?

解决方法:

If I were to start a brand new application, with the intention to
support per-monitor DPI awareness, what is the best approach?

我们不依赖于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=.

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错误被关闭为无错误:QTBUG-55449QTBUG-55510显示了该功能背后的意图.顺便说一下,QTBUG-55510提供了一个程序化的解决方法,用于在不修复qt.conf的情况下设置Qt DPI意识(由自己决定使用’私有’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或从QTBUG-55510执行解决方法)来启用应用程序在系统和Per Monitor DPI Aware模式之间切换在应用程序开始).我们希望Qt公司意识到需要为每个监视器DPI识别模式以及小部件的自动扩展.我们为什么需要它(?)是另一个问题.在我的情况下,我在自己的应用程序小部件画布中有每个监视器渲染,应该缩放.

首先,从@selbie阅读这个问题的评论我意识到在应用程序启动时可能有一种方法尝试设置QT_SCREEN_SCALE_FACTORS:

QT_SCREEN_SCALE_FACTORS [list] specifies scale factors for each
screen. This will not change the size of point sized fonts. This
environment variable is mainly useful for debugging, or to work around
monitors with wrong EDID information(Extended Display Identification
Data).

然后我阅读Qt blog关于如何应用多个屏幕因素,并尝试在4K和1080p监视器中执行以下操作,其中4K首先列出(主要).

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

这确实有点帮助:几乎正确的渲染,但引入了窗口大小的缺陷,同时将窗口从一个监视器移动到另一个监视器,就像QTBUG-55449一样.如果客户将当前的应用行为视为错误(我们通过系统DPI Aware为所有监视器DPI建立相同的基础),我想我将使用WM_DPICHANGED QT_SCREEN_SCALE_FACTORS方法.仍然没有准备好使用Qt的解决方案.

标签:c,qt,highdpi
来源: https://codeday.me/bug/20190930/1836289.html