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

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

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

Если я правильно понимаю, Qt::AA_EnableHighDpiScaling это полная противоположность того, что я хочу. Я должен на самом деле отключить HighDpiScaling и рассчитать все размеры вручную во время выполнения?

Во многих предложениях говорится, что не следует использовать размеры вообще, использовать плавающие макеты. Но во многих случаях желательно наличие как минимум минимальной ширины и / или минимальной высоты. Как Qt Designer позволяет мне помещать значения в абсолютные пиксели, каков правильный подход? Где я должен разместить код для пересчета размеров, если изменяется разрешение монитора?

Или я должен просто пойти с автоматическим масштабированием?

В одном из моих старых приложений, где я пытался добавить поддержку HighDPI, я использовал этот подход — перечислите всех дочерних элементов DOM и измените их размер по одному с учетом определенного соотношения. Отношение = 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?

11

Решение

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

Мы не полагаемся на Qt для автоматического масштабирования в режиме DPI для каждого монитора. Как минимум приложение на Qt 5.7 с Qt::AA_EnableHighDpiScaling set не делает этого, и «масштабирование с высоким 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, закрытых как no-bug: 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;

Это довольно сложный и хлопотный способ, и я прибегнул к тому, чтобы приложение могло переключаться между режимами System и Per Monitor DPI Aware, позволяя пользователю выбирать режим и перезапускать процесс приложения (либо исправляя qt.conf или делать обходной путь от QTBUG-55510 в начале приложения). Мы надеемся, что компания Qt понимает, что для каждого монитора необходим режим DPI с автоматическим масштабированием для виджетов. Зачем нам это нужно (?) Это другой вопрос. В моем случае у меня есть рендеринг для каждого монитора в пределах холста виджета собственного приложения, который должен быть масштабирован.

Сначала, читая комментарий к этому вопросу от @selbie, я понял, что, возможно, есть способ установить QT_SCREEN_SCALE_FACTORS во время запуска приложения:

QT_SCREEN_SCALE_FACTORS [список] определяет масштабные коэффициенты для каждого
экран. Это не изменит размер шрифтов точечного размера. это
переменная окружения в основном полезна для отладки или для обхода
мониторы с неверной информацией EDID (расширенная идентификация дисплея
Данные).

Я тогда читаю Qt блог о том, как применить несколько экранных факторов и попытаться сделать следующее для мониторов 4K и 1080p, где 4K указан первым (основной).

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

Это немного помогает: почти правильный рендеринг но вносит дефекты с размером окна при перемещении окна с одного монитора на другой, очень похоже на QTBUG-55449 делает. Я думаю, я пойду с WM_DPICHANGED + QT_SCREEN_SCALE_FACTORS подход, если клиент рассматривает текущее поведение приложения как ошибку (мы создаем одинаковую базу для всех мониторов DPI через System DPI Aware). Пока еще нет готового решения от Qt.

8

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]