Я прочитал официальный Документация 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?
Если бы я должен был запустить новое приложение, с намерением
поддержка осознания 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.
Других решений пока нет …