Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect DPI/scaling factor in Python TkInter application

I'd like my application to be able to detect if it's running on a HiDPI screen, and if so, scale itself up so as to be usable. As said in this question, I know I need to set a scaling factor, and that this factor should be my DPI divided by 72; my trouble is in getting my DPI. Here's what I have:

def get_dpi(window):
    MM_TO_IN = 1/25.4
    pxw = window.master.winfo_screenwidth()
    inw = window.master.winfo_screenmmwidth() * MM_TO_IN
    return pxw/inw

root = Tk()
root.tk.call('tk', 'scaling', get_dpi(root)/72)

This doesn't work (testing on my 4k laptop screen). Upon further inspection, I realized get_dpi() was returning 96.0, and that winfo_screenmmwidth() was returning 1016! (Thankfully, my laptop is not over a meter wide).

I assume that TkInter is here calculating the width in mm from some internally-detected DPI, wrongly detected as 96, but I'm not sure where it's getting this; I'm currently on Linux, and xrdb -query returns a DPI of 196, so it's not getting the DPI from the X server.

Does anyone know a cross-platform way to get my screen DPI, or to make TkInter be able to get it properly? Or, more to the point: how can I make TkInter play nice with HiDPI screens and also work fine on normal ones? Thanks!

like image 544
charliegreen Avatar asked Mar 22 '17 20:03

charliegreen


People also ask

What is DPI tkinter?

What is DPI tkinter? A distance number is a digit followed by a unit, so 3c means 3 centimeters, and the function gives the number of pixels on 3 centimeters of the screen (as found here). So to get dpi, we ask the function for the number of pixels in 1 inch of screen ("1i").

What is the Tk scale?

The Scale widget is used to implement the graphical slider to the python application so that the user can slide through the range of values shown on the slider and select the one among them. We can control the minimum and maximum values along with the resolution of the scale.


3 Answers

This answer is from this link and left as a comment above, but it took hours of searching to find. I have not had any issues with it yet, but please let me know if it does not work on your system!

import tkinter
root = tkinter.Tk()
dpi = root.winfo_fpixels('1i')

The documentation for this says:

winfo_fpixels(number)
# Return the number of pixels for the given distance NUMBER (e.g. "3c") as float

A distance number is a digit followed by a unit, so 3c means 3 centimeters, and the function gives the number of pixels on 3 centimeters of the screen (as found here). So to get dpi, we ask the function for the number of pixels in 1 inch of screen ("1i").

like image 197
Andrew Pye Avatar answered Oct 13 '22 23:10

Andrew Pye


I know I'm answering this question late, but I'd like to expand upon @Andrew Pye 's idea. You are right, GUI's with tkinter look different across different monitors with different DPI's anytime you use a 'width' or 'height' or 'pady' or anything that is measured in pixels. I noticed this when I made a GUI on my desktop, but then later ran the same GUI on my 4K laptop (The window and the widgets appeared much smaller on the laptop). This is what I did to fix it, and it worked for me.

from tkinter import *

ORIGINAL_DPI = 240.23645320197045   # This is the DPI of the computer you're making/testing the script on.

def get_dpi():
    screen = Tk()
    current_dpi = screen.winfo_fpixels('1i')
    screen.destroy()
    return current_dpi

SCALE = get_dpi()/ORIGINAL_DPI    # Now this is the appropriate scale factor you were mentioning.

# Now every time you use a dimension in pixels, replace it with scaled(*pixel dimension*)
def scaled(original_width):
    return round(original_width * SCALE)

if __name__ == '__main__':
    root = Tk()
    root.geometry(f'{scaled(500)}x{scaled(500)}')    # This window now has the same size across all monitors. Notice that the scaled factor is one if the script is being run on a the same computer with ORIGINAL_DPI. 
    root.mainloop()
like image 3
Gabe Morris Avatar answered Oct 13 '22 23:10

Gabe Morris


I'm using TclTk, not TkInter, and the only way I know how to do this is to work it out from the font metrics...

% font metrics Tk_DefaultFont -ascent 30 -descent 8 -linespace 38 -fixed 0

The linespace is approximately 0.2x the DPI (currently set to 192 here)

like image 1
Jasper Taylor Avatar answered Oct 13 '22 22:10

Jasper Taylor