tkinter无法正确识别屏幕分辨率

时间:2016-04-03 03:10:45

标签: python tkinter resolution

我正在使用4k显示器(3840x2160)。

from tkinter import *

root = Tk()

width = root.winfo_screenwidth()
height = root.winfo_screenheight()

print (width, height)

mainloop()

当我运行此代码时,输​​出为1536 x 864

有人可以解释为什么会这样,以及我如何解决它,谢谢。

3 个答案:

答案 0 :(得分:2)

看着问题,这似乎是ms-windows中的缺陷,tk没有使用已知的解决方法。

查看tk的源代码,在文件win/tkWinX.c中,函数TkWinDisplayChanged使用Windows API调用GetDeviceCaps来获取带有参数的屏幕宽度和高度HORZRESVERTRES

不幸的是,this page声明(强调我):

注意 GetDeviceCaps报告显示驱动程序提供的信息。 如果显示驱动程序拒绝报告任何信息,则GetDeviceCaps将基于固定的计算来计算该信息。。如果显示驱动程序报告了无效的信息,则GetDeviceCaps将返回无效的信息。另外,如果显示驱动程序拒绝报告信息,则GetDeviceCaps可能会计算不正确的信息,因为它假定使用固定的DPI(96 DPI)或固定的大小(取决于显示驱动程序提供和未提供的信息)。 不幸的是,已实施到Windows Display Driver Model(WDDM)(Windows Vista中引入)的显示驱动程序导致GDI无法获取信息,因此GetDeviceCaps必须始终计算信息。

我将其表述如下: “在Windows Vista之后,将不信任来自GetDeviceCaps的显示信息。”

this page about high-dpi displays。 此页面使用其他GetDeviceCapsLOGPIXELSXLOGPIXELSY值(x和y中每“逻辑英寸”的像素数)来计算 real 窗口尺寸。 GetDeviceCaps似乎使用的“默认” DPI是96。

看一下tk中的win/tkWinX.c代码片段,您可以看到tk使用LOGPIXELSXLOGPIXELSY来计算屏幕尺寸,单位为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)

答案 1 :(得分:1)

这应该是DPI感知的问题,请在MSDN official document中阅读。

在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()

Requirements of 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))(以及那些需要传递position参数的函数)时,可能会得到错误的大小。

答案 2 :(得分:0)

我在Raspberry pi上运行了代码,并为我的显示器(不是4K显示器)获得了正确的值。

我没有解决方案,但我发现您的预期/观察到的答案之间的比例是

3840 / 1536 = 2.5
2160 / 864 = 2.5

4K显示器的屏幕驱动程序可能会影响实际物理像素(3840x2160)和某些概念("逻辑像素")。目的是避免某些软件显示,例如,具有8个真实物理像素的8点文本,因为这是不可读的。

我无法测试这个(我没有硬件),这只是一个假设。我也可能没有确切的术语。

(顺便说一句,在iOS上有点与像素的概念 - 你可以搜索这些术语。即使它没有解答你的问题,也可能是一个类似的问题)。