1

this is probably not much of a coding question.

I developed an application in Tkinter which consists pretty much in a canvas where I load images on (matplots).

When I run it from Spyder or Jupyter notebook I obtain 143 DPI and when I run it directly in terminal I obtain 95 DPI instead.

The most weird thing is that once I run it from terminal it shows as if it was a 143 DPI application but when I load the image the app decides to resize completely and then change the DPI of my screen. So the whole thing resizes, buttons, pop up boxes and etc...

It is very important to me that I can run it from terminal, because afterwards I will transform it into a .exe application.

My first approach was to use the tkinter method .winfo_fpixel() and try to ratio the DPIs and multiply it to every dimension in the application.

Later I tried to find python.exe and pythonw.exe in order to change the compatibility with HIGH DPI Scale.

Do you thing there is a way to solve this without resizing everything?

I would like to insist that I'm 100% sure this has nothing to do with the code, therefore I might not post any coding samples.

Thanks.

obs: I use Windows 10 ps: I tried to run the app with two screens, what happened was really funny, when I use it on an adequate screen it runs normally, but when I slide it to the other screen and make any action/event it will resize it automatically.

Lucas Tonon
  • 117
  • 8

2 Answers2

3

DPI is largely influenced by your PC's resolution settings - specifically the setting on "Scale and layout". 100% is 96 DPI and 150% is 144 DPI. I'm guessing it's just a rounding error from your app that's showing 95 and 143. Laptops tend to have higher resolution screens, so OEM's default to >100% setting so that you don't have to squint to see your apps. Whereas, your typical 1080p display defaults to 100% DPI. But when you "remote desktop" into a PC, Windows may re-adapt and re-stretch the DPI settings to match the local display if its DPI and resolution are different.

enter image description here

Windows will auto-scale most applications that haven't explicitly set their "DPI awareness" with the OS. That's typically fine for most application (although they can appear "blurry"). Applications can be compiled to be dpi aware are not stretched except in certain multi-monitor situations. This is especially likely for graphics applications. And that's where bugs like the one you are seeing tend to show up. You can often change this behavior by right clicking on the EXE, clicking Properties, and finding the setting the compatibility tab:

enter image description here

The most weird thing is that once I run it from terminal it shows as if it was a 143 DPI application but when I load the image the app decides to resize completely and then change the DPI of my screen. So the whole thing resizes, buttons, pop up boxes and etc...

When you remoted into your PC, the system DPI changed. Applications can get confused - especially if they aren't adapting to a changing DPI setting. Or worse, the application registers with Windows that it is DPI aware, but doesn't handle the corner case of when the monitor is swapped out - which is effectively what happens when you remoted into it. You can however, override this behavior with some combination of the Properties dialog above and the available APIs to control dpi-awareness.

I don't much about TKinter (Does it use the Python process or is it its own exe?). If you can compile it yourself, you can use the various Windows APIs and manifest settings to get the DPI behavior you want. My shot in the dark guess is that TKinter( is declaring itself per-monitor dpi-aware, but has a bug that doesn't account for the DPI changing on the fly. Typical fix for this situation is to just restart the application.

Everything you need to know at the links below:

https://learn.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows

https://learn.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness

https://msdn.microsoft.com/en-us/C9488338-D863-45DF-B5CB-7ED9B869A5E2

You can also read my previous answers regarding DPI awareness in Windows apps:

https://stackoverflow.com/search?q=user%3A104458+dpi

selbie
  • 100,020
  • 15
  • 103
  • 173
  • I think that's what I was looking for, I'll try it today and tomorrow and give you some feedback. Thank you anyway for the effort !! – Lucas Tonon Jul 17 '20 at 06:49
  • It worked for the most part, with some adaptations in the code (mismatch with the figure size), I'm willing to transform this app in an executable using py-to-exe. The thing is, I will probably have to change the scale from 150% to 100% in every laptop I install it. Would you have any input/suggestion about that? Thank you very much once again !!! – Lucas Tonon Jul 17 '20 at 07:07
  • 1
    In our app, very early in WinMain when the process starts, we configure out process for system-aware DPI can invoke `GevDeviceCaps(LOGPIXELSX)` then divide by `96` to get the "scale factor". The all subsequent window creations, font sizing, and elements within those windows are sized to match the scale ratio we computed before. So it "just works" and looks good 99% of the time. When the monitor swap happens, as a result of plugging into a projector or "remote desktoping" into it, Windows will happily rescale us without the process aware of being a dpi change. – selbie Jul 17 '20 at 07:39
  • 1
    That's why I like system-aware DPI, it basically means you just have to take note of the DPI setting on process startup, but don't have to worry about it changing later. Per-monitor dpi awareness is much harder. I'm not sure what Python and Tkinter set (if any), but some frameworks like Qt default to per-monitor. We override it. Final note - latest updates to Windows 10 have remarkable improvements to app behavior wrt to monitor and dpi changes. – selbie Jul 17 '20 at 07:43
  • Thats great, probably I will recreate the app in Qt for the next version. I'm really glad you helped me with that !! – Lucas Tonon Jul 17 '20 at 07:45
3

selbie's answer is correct.

Also,You could use winapi to set DPI aware in python directly(this could work in tkinter):

import ctypes, tkinter
try: # >= win 8.1
    ctypes.windll.shcore.SetProcessDpiAwareness(2)
except: # win 8.0 or less
    ctypes.windll.user32.SetProcessDPIAware()

root = tkinter.Tk()
....

MSDN doc: SetProcessDpiAwareness, MSDN doc: SetProcessDPIAware

And this is another answer which mentioned about DPI awareness.

jizhihaoSAMA
  • 12,336
  • 9
  • 27
  • 49
  • It worked perfectly, now I don't have to change the configurations in every computer I install it ! Thank you very much !!! – Lucas Tonon Jul 20 '20 at 09:06
  • Could you explain me why the argument 2? I couldn't find in the documentation. Thanks – Lucas Tonon Jul 20 '20 at 09:11
  • 1
    @LucasTonon You could [check it](https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness) in this page.`2` means `"Per monitor DPI aware. This app checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes. These applications are not automatically scaled by the system."` – jizhihaoSAMA Jul 20 '20 at 09:13
  • Well the problem now is the following: If I start my laptop with the second screen plugged it works just fine. But if I start only my laptop it will adapt to the laptop and get distorted, even if I plug in my second screen, it will continue like that untill I completely restart my computer. – Lucas Tonon Jul 20 '20 at 11:31
  • It is really interesting though, if I run it from Spyder it has a different behavior than if I run it directly from the powershell/anaconda prompt – Lucas Tonon Jul 20 '20 at 11:37
  • 1
    @LucasTonon I used to pack them with pyinstaller.It works fine on lots of devices. – jizhihaoSAMA Jul 20 '20 at 11:41
  • Its is the same I'm using. – Lucas Tonon Jul 21 '20 at 06:36