Keep in mind that from Windows 8.1 onwards, DPI can be set per-screen (I myself 
have a 4K screen (240 dpi, 250% scaling) + regular HD screen (120 dpi, 125% 
scaling)).  Qt appears to already handle the appropriate new dpi-aware APIs (Qt 
defaults to per-monitor DPI aware on Windows 8.1+).

Sadly, I must agree with you that Qt does indeed not handle high-dpi windows 
very well ☹   I have Qt applications that I cannot use on my system, and have 
resorted to qt.conf to turn off DPI-awareness for them; fuzzy scaling was 
preferable to not being able to use the application.

Mind you, I have not found many Windows applications that handle high-dpi 
correctly, software is definitely lagging behind hardware here.

Mark

From: Development [mailto:[email protected]] On Behalf Of Die 
Waldmanns
Sent: 28 December 2015 20:51
To: [email protected]
Subject: [Development] High-DPI on Win

Hey Everyone,

first, I'd like to apologize if that is the wrong place for the following, but 
I've seen the "High-DPI 5.6 beta update" thread, which encouraged me to post 
here.
Second, you'll quickly realize that I'm new to QT, so not everything I'm saying 
might be prefectly on spot, I beg for pardon, but I think the main message is, 
so please rather look for that. (BTW, I'm using PyQT5.5 as provided by 
WinPython-64bit-3.4.3.5 on a Win7 Pc and a Win8.1 high-dpi laptop).
Third, I also write because after my first amazement with how nicely the first 
steps with PyQT5 worked out I was - yes - shocked to see how badly QT handles 
high-dpi on Win. I think one can't be happy with the current situation, and - 
although I can't claim to really have penetrated QT5.6 beta1 - I'm worried that 
it won't be satisfactorily resolved in QT5.6 either, even though it is - IMHO - 
of immediate relevance.

I'd like to suggest this:

(1) Support of fractional scaling (devicePixelRatio).
This ratio is qreal, but in QT5.5 it nevertheless seems not be possible to set 
it to fractional values (I could get it only to 1.0,2.0,3.0 etc). In 5.6-beta1 
the new environment SCALE mechanisms seem to allow setting it to real values, 
but the examples in the docu are mentioning only devicePixelRatios of 1 and 2, 
which makes me wonder. If in QT5.6 it's now possible to acieve 
devicePixelRatios of 1.0, 1.25, 1.5, 2.0, and 2.5, then this point is obsolete; 
if not I think this neeeds to be enabled.

(2) Allow setting the devicePixelRatio programatically.
The current method via an environment variable might not be considered 
acceptable, or a programmer might want to modify the built-in method of how it 
is determined automatically. Im not sure if this could be achieved now by e.g. 
calling QHighDpiScaling::setGlobalFactor(). Anyway, I think the mechanism 
should be part of the API, i.e. be simple, official, and documented (e.g. a 
virtual method which can be overloaded). It would avoid clumpsy work-arounds 
such as calling os.environ['QT_DEVICE_PIXEL_RATIO'] = str( int(winScale) ) from 
within the code.

Ideally the mechanism would allow one to set devicePixelRatio at a point in 
time where the fonts are already accessible (app.font(),QFontInfo()). (not sure 
if that's possible, but the current mechanism seems to set the pixel ratio in 
two steps, so I recon it might be)

(3) Don't calculate the scale from the display resolution 
(QPlatformScreen::pixelDensity()).
In QT5.5 the devicePixelRatio is obviously set to either 1.0 or 2.0 depending 
on the resolution of the display. I'm not sure for Qt5.6, but it seems to be 
alike here. However, in Win the user can set a scaling such as 100%, 125%, 
150%, 200%, and 250%, and the user can do so - and may want to do so - for 
whatever display she/he is using (anyone with ailing eyes is happy to use 125% 
on regular screens, or 250% on HDPI screens ;)). So - IMHO - the 
devicePixelRatio should actually be set to exactly this value, independent on 
any display resolution.

In QT5.5 the font sizes are apparently set "correctly", i.e. scale with the 
Windows scaling set by the user. Thus, for the 125%, 150%, and 250% scalings 
ugly GUIs result, since the font sizes do not fit to the renderings produced by 
QT5.5 (which are scaled only by 1.0, 2.0, 3.0,...). I'm not sure how exactly QT 
manages the font sizing, but I guess it's just that the font sizes are 
specified as points, and Win when does the correct scaling. Anyway, since the 
fonts scale with the Windows scaling it seems obvious to me that also the rest 
of the GUI should be scaled with this very same factor, at least it's the best 
bet on getting a properly rendered GUI.

As a work-around to the limitations of QT5.5 I tried doing all scaling by hand, 
i.e. to enforce devicePixelRatio=1.0 and scale all Widgets by using my own 
scale factors. This works quite well ... for most parts, it fails e.g. for a 
QMessageBox. So, this approach doesn't yield a fully satisfying result either 
(I note that QtDesigner suffers from the same artefacts, implying that even 
expert Qt programmers didn't get around it).

I got however the clear impression that if I could set devicePixelRatio to 2.5 
when using a scaling of 250%, I would get a perfect GUI, which suggests the 
above 3 points, since with (1) and (2) one could determine the Windows scale 
and set devicePixelRatio accordingly. Ideally, Qt would be "smart" enough to do 
that by itself, which is (3).

As regards determining the Windows scale, I could not identify an API function 
which would give it directly. The most robust approach I found is to go via the 
fonts:

winScale = ( 3.0 * QFontInfo(app.font()).pixelSize() )/( 4.0 * 
QFontInfo(app.font()).pointSizeF() )

I tested this for a couple of fonts and font sizes, and in all cases without 
exception this formula delivered the correct Windows scaling. Unfortunately, 
this works only after the app is created (app = QApplication([])).

As alternative I found:

winScaledYRes = GetSystemMetrics(1) #1 = SM_CYSCREEN
dc= windll.user32.GetDC(0)
winYRes = windll.gdi32.GetDeviceCaps(dc,117) #117 = DESKTOPVERTRES
winScale = float(winYRes)/float(winScaledYRes)

This works before the app is created, and thus can be used to set the 
devicePixelration by calling os.environ['QT_DEVICE_PIXEL_RATIO'] = str( 
int(winScale) ), but, at least on my systems, it provides the correct value 
only for the 100%, 150%, 200%, and 250% scalings. For some reasons I don't 
understand it yields 1.0 instead of 1.25 for the 125%
scaling.

Maybe better methods exist. Anyway, since the font method works so well it 
would be nice if fonts could be accessed in (2).

Sorry for the long post, to summarize in a nutshell:
I came to the conclusion that one would/could get nice QT GUIs, for all Windows 
scalings and any display, if one could set the devicePixelRatio to real values 
which match the Windows user scaling. This led me to the above 3 suggestions. 
I'd like to add again, that a working HDPI is IMHO of immediate importance, and 
thus would think that it should become part of QT5.6, besides its feature 
freeze. I think the 3 points are easy to implement, which warrants this.

Finally, again, if any of the above just reflects my inablitity to discover the 
proper Qt methods to deal with high-DPI on Win systems, then please accept my 
applogies; please consider when to provide a bit more guidance in the docu :)

Cheers, and thanks for all your great work !!
Olli
_______________________________________________
Development mailing list
[email protected]
http://lists.qt-project.org/mailman/listinfo/development

Reply via email to