Как подойти к разработке нового приложения Qt 5.7+ High-DPI на монитор DPI?

Я прочитал официальный документации Qt и многие статьи и вопросы по StackOverflow о высокой поддержке DPI в Qt. Все они сосредоточены на портировании старых приложений и их работе с как можно меньшими изменениями.

но если я должен был начать совершенно новое приложение, с намерением поддерживать на мониторе DPI aware app, каков наилучший подход?

Если я правильно понял, то 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?

1 ответов


если бы я начал совершенно новое приложение, с намерением поддержка осознания DPI на мониторе, каков наилучший подход?

мы не полагаемся на Qt для автоматического масштабирования в режиме DPI на мониторе. По крайней мере, Qt 5.7-приложение с Qt::AA_EnableHighDpiScaling set этого не делает, и "высокое масштабирование DPI" является более точным рисунком независимо от плотности пикселей.

и для вызова режима DPI на мониторе вам нужно изменить в тот же каталог, в котором вы проецируете исполняемый файл:

[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 и вычислить все измерения вручную во время выполнения?

QTBUG-55449 и QTBUG-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;

это довольно сложный и хлопотный способ пойти, и я прибегнул, чтобы позволить приложению переключаться между системами и режимами монитора DPI, позволяя пользователю выбирать режим и перезапускать процесс приложения (либо фиксируя qt.conf или делать решение от 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 делает. Думаю, я пойду с WM_DPICHANGED + QT_SCREEN_SCALE_FACTORS подход, если клиент считает текущее поведение приложения ошибкой (мы делаем ту же базу для всех мониторов DPI через System DPI Aware). Еще нет готового к использованию решения от Qt.