If I were to start a brand new application, with the intention to
support per-monitor DPI awareness, what is the best approach?
We don't rely on Qt for automatic scaling in per-monitor DPI aware mode. At least Qt 5.7-based app with Qt::AA_EnableHighDpiScaling
set does not do that and 'high DPI scaling' is more of accurate drawing regardless of pixel density.
And to invoke per-monitor DPI aware mode you need to modify Qt.conf
file at the same directory where you project executable file is:
[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?
No, it is not an opposite but a different thing. There is a couple of Qt bugs closed as no-bugs: QTBUG-55449 and QTBUG-55510 that show the intent behind the feature. BTW, there is QTBUG-55510 a programmatic workaround for setting Qt DPI awareness without fixing qt.conf
is provided (use at own discretion because it uses 'private' Qt implementation classes that change the interface without any notice with newer Qt version).
And you expressed the correct approach to do the scaling in per-monitor DPI aware mode. Unfortunately except that there is no much alternative at the time. There programmatic ways to assist the event handling for the window scaling when it is moved from one monitor to another, though. The method like resizeWidget
(one, not many) at the head of this question should be called using something like (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;
This is quite difficult and troublesome way to go and I resorted to enable the app to switch between System and Per Monitor DPI Aware modes by letting the user to choose the mode and restart the app process (either fixing qt.conf
or doing workaround from QTBUG-55510 at the app start). Our hope is that Qt company realizes there is a need for per-monitor DPI aware mode with auto scaling for widgets. Why would we need it (?) is another question. In my case I have per-monitor rendering within own app widget canvas which supposed to be scaled.
At first, reading the comment to this question from @selbie I realized maybe there is a way to try to set QT_SCREEN_SCALE_FACTORS while the app starts:
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).
I then read Qt blog on how to apply multiple screen factors and attempted to do the below for 4K and 1080p monitors where 4K is listed first (main).
qputenv("QT_SCREEN_SCALE_FACTORS", "2;1");
That does helps a bit: almost correct rendering but introduces defects with window size while moving the window from one monitor to another pretty much like QTBUG-55449 does. I guess I will go with WM_DPICHANGED
+ QT_SCREEN_SCALE_FACTORS
approach if the customer considers current app behavior as a bug (we make the same base for all monitors DPI via System DPI Aware). Still there is no ready to use solution from Qt yet.