查看问题,似乎是 ms-windows 的缺陷,tk 没有使用已知的解决方法。
查看tk的源代码,在文件win/tkWinX.c中,函数TkWinDisplayChanged使用windows API调用GetDeviceCaps通过参数HORZRES和VERTRES获取屏幕宽度和高度。
不幸的是,this page 声明(强调我的):
注意 GetDeviceCaps 报告显示驱动程序提供的信息。 如果显示驱动程序拒绝报告任何信息,GetDeviceCaps 会根据固定计算来计算信息。 如果显示驱动程序报告无效信息,GetDeviceCaps 会返回无效信息。此外,如果显示驱动程序拒绝报告信息,GetDeviceCaps 可能会计算不正确的信息,因为它假定固定 DPI (96 DPI) 或固定大小(取决于显示驱动程序提供和未提供的信息)。 不幸的是,在 Windows 显示驱动程序模型 (WDDM)(在 Windows Vista 中引入)实施的显示驱动程序会导致 GDI 无法获取信息,因此 GetDeviceCaps 必须始终计算信息。
我会改写如下; "在 windows Vista 之后,来自GetDeviceCaps 的显示信息不可信"。
有this page about high-dpi displays。
此页面使用其他一些 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)