I'm currently running Mac OS Sonoma 14.2.1 and I'm trying to set up a tkinter canvas which is scaled to "actual" real-life size. I.e., if I draw a line between any two points on the canvas, I'd like to calculate its length in inches.
I've tried getting the DPI in tkinter like so:
root = tk.Tk()
dpi = round(root.winfo_fpixels('1i'))
On my MacBook, this returns 72, which seems like a sensible DPI
Then I used the distance formula to calculate the length of the line in pixels
(I don't need crazy precision, so that's why round is used here)
x1, y1, x2, y2 = canvas.bbox(line)
length = round(math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2))
Here's a barebones example of everything together. Note that I'm using bbox() here because in actuality the lines will be drawn between two points set with the mouse, so they can be at any angle, not just a static straight line like in this example...
import math
import tkinter as tk
root = tk.Tk()
# get dpi
dpi = round(root.winfo_fpixels('1i'))
# init canvas
canvas = tk.Canvas(root)
canvas.pack(expand=True, fill='both')
# create a (supposedly) 1 inch line, for example
line = canvas.create_line(50, 50, dpi + 50, 50, width=2)
# get line endpoints
x1, y1, x2, y2 = canvas.bbox(line)
# calculate line length
length_in_px = round(math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2))
length_in_inches = length_in_px / dpi
# result
print(length_in_inches) # => 1.0833333333333333 inches?
if __name__ == '__main__':
root.mainloop()
So the math (sort of?) checks out. I suspect this floating point weirdness is related to bbox, so if anyone has a better suggestion for getting the endpoints of the line, let me know!* (For completeness' sake, removing round from both dpi and length_in_px results in a slightly different weird float: 1.0857189357495975)
*SEE FIRST UPDATE
But the real issue is that when I physically measure the line on my laptop's display (literally, with calipers), it's actually 0.555 inches. This, to me, means at least one of two things:
- There's some UI scaling going on, in which case the question becomes how do I figure out this scaling factor on Mac OS? (I have experience doing this on Windows, but I can't figure it out on my Mac)
- The DPI value returned by
winfo_fpixels()is incorrect; this seems less likely to me, but not impossible. In this case, how do I find the actual DPI of my display(s)?
How can I accurately go about translating pixels to real-world units? Any help is appreciated, and thanks in advance!
UPDATE 1
using canvas.coords(line) instead of canvas.bbox(line) to get the endpoints resolves the floating point weirdness, but not the scaling issue - that's one thing down!
UPDATE 2
Tech specs for my laptop (2021 M1 MacBook Pro) state that the display DPI is 254...I'm not sure what to do with this info as yet