我使用的是 4k 显示器 (3840x2160)。
from tkinter import *
root = Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
print (width, height)
mainloop()
当我运行此代码时,输出为 1536 x 864
有人可以解释为什么会发生这种情况,以及如何解决它,谢谢。
这应该是DPI感知的问题,在MSDN官方文档中阅读。
在 windows 10 中:需要使用 SetProcessDpiAwareness(或 SetThreadDpiAwarenessContext),尝试使用:
import tkinter as tk
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(2) # your windows version should >= 8.1,it will raise exception.
root = tk.Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
print(width,height)
root.mainloop()
SetProcessDpiAwareness 的要求
在 Windows XP 或 7 中,您需要使用 SetProcessDPIAware()
所以所有的代码可能是:
import tkinter as tk
import ctypes
try:
ctypes.windll.shcore.SetProcessDpiAwareness(2) # if your windows version >= 8.1
except:
ctypes.windll.user32.SetProcessDPIAware() # win 8.0 or less
root = tk.Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
print(width,height)
root.mainloop()
如果你使用tk.call('tk', 'scaling')
,也可以。但是当你使用ImageGrab.grab((xxxx))
(以及那些需要传递位置参数的函数)时,也许它会得到尺寸错误。
查看问题,这似乎是 ms-windows 中的一个缺陷, tk
没有使用已知的解决方法。
查看tk
的源代码,在文件win/tkWinX.c
中,函数TkWinDisplayChanged
使用 Windows API 调用GetDeviceCaps
以参数HORZRES
和VERTRES
获取屏幕宽度和高度。
不幸的是,这个页面状态(强调我的):
注意
GetDeviceCaps
报告显示驱动程序提供的信息。 如果显示驱动程序拒绝报告任何信息,GetDeviceCaps
会根据固定计算来计算信息。 如果显示驱动程序报告无效信息,GetDeviceCaps 将返回无效信息。 此外,如果显示驱动程序拒绝报告信息,GetDeviceCaps 可能会计算不正确的信息,因为它假定固定 DPI (96 DPI) 或固定大小(取决于显示驱动程序提供和未提供的信息)。 不幸的是,实现到 Windows 显示驱动程序模型 (WDDM)(在 Windows Vista 中引入)的显示驱动程序会导致 GDI 无法获取信息,因此 GetDeviceCaps 必须始终计算信息。
我将改写如下; “在 windows Vista 之后,来自GetDeviceCaps
的显示信息不可信”。
有这个关于高 dpi 显示器的页面。 此页面使用其他一些GetDeviceCaps
值、 LOGPIXELSX
和LOGPIXELSY
(x 和 y 中每“逻辑英寸”的像素数)来计算实际窗口大小。 GetDeviceCaps
似乎使用的“默认”DPI 是 96。
查看来自win/tkWinX.c
的tk
代码片段,您可以看到tk
使用LOGPIXELSX
和LOGPIXELSY
来计算以 mm 为单位的屏幕尺寸。
void
TkWinDisplayChanged(
Display *display)
{
HDC dc;
Screen *screen;
if (display == NULL || display->screens == NULL) {
return;
}
screen = display->screens;
dc = GetDC(NULL);
screen->width = GetDeviceCaps(dc, HORZRES);
screen->height = GetDeviceCaps(dc, VERTRES);
screen->mwidth = MulDiv(screen->width, 254,
GetDeviceCaps(dc, LOGPIXELSX) * 10);
screen->mheight = MulDiv(screen->height, 254,
GetDeviceCaps(dc, LOGPIXELSY) * 10);
这个值似乎是用来计算winfo_fpixels()
所以我认为如果你使用root.winfo_fpixels('1i')
你会得到一个可靠的 DPI 值。
所以,试试这个:
import tkinter as tk
root = tk.Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
dpi = root.winfo_fpixels('1i')
real_width = int(width * dpi / 96)
real_height = int(height * dpi / 96)
print('real width should be', real_width)
print('real height should be', real_height)
编辑一种解决方法是设置tkinter
的缩放因子:
factor = dpi / 72
root.tk.call('tk', 'scaling', factor)
我在我的 Raspberry pi 上运行了你的代码,并为我的显示器(不是 4K 显示器)获得了正确的值。
我没有解决方案,但我观察到您的预期/观察到的答案之间的比率是
3840 / 1536 = 2.5
2160 / 864 = 2.5
也许 4K 显示器的屏幕驱动程序会在真实的物理像素 (3840x2160) 和一些“逻辑像素”的概念之间产生差异。 目的是避免某些软件显示,例如,具有 8 个实际物理像素的 8 点文本,因为那将是不可读的。
我无法对此进行测试(我没有硬件),这只是一个假设。 我也可能没有确切的术语。
(顺便说一句,在 iOS 上有点与像素的概念——你可以搜索这些术语。即使它不能回答你的问题,也可能是类似的问题)。
更多相关内容:请点击查看