【问题标题】:tkinter not recognizing screen resolution correctlytkinter 无法正确识别屏幕分辨率
【发布时间】:2016-07-22 17:30:55
【问题描述】:

我使用的是 4k 显示器 (3840x2160)。

from tkinter import *

root = Tk()

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

print (width, height)

mainloop()

当我运行这段代码时,输​​出是 1536 x 864

谁能解释一下为什么会发生这种情况,以及如何解决它,谢谢。

【问题讨论】:

  • 似乎也发生了here。我不明白为什么(在我的机器上运行良好)。
  • 这是在 Windows 上吗?可能是一些高 DPI 感知标志,表示 Tk 正在缩放。
  • 是的,它在 Windows 10 64 位上。我相信 Tkinter 的默认 DPI 是 72,我的屏幕是 15.6 英寸会有 282 DPI。
  • 很可能您在 Windows 中配置了一些缩放。由于 tkinter 不知道 dpi 缩放(旧版应用程序),Windows 让它相信实际的显示分辨率是真实的显示分辨率除以缩放因子。

标签: python tkinter resolution


【解决方案1】:

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

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

不幸的是,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 值、LOGPIXELSXLOGPIXELSY(x 和 y 中每“逻辑英寸”的像素数)来计算 实际 窗口大小。 GetDeviceCaps 似乎使用的“默认”DPI 是 96。

查看win/tkWinX.c的这段tk代码片段,可以看到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)

【讨论】:

  • 感谢您的努力。使用您的解决方案只会让我得到一个与全屏大小相同的 tkinter 窗口,但仍具有相同的颗粒状分辨率。我真的在寻找一种方法让我的应用程序具有与我的计算机屏幕相同的分辨率。
  • @JadonErwin 另见this answer
  • 行得通!朋友,谢谢!一旦我被允许,我会立即给予赏金。
【解决方案2】:

这应该是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))(以及那些需要传递位置参数的函数)时,可能会得到错误的大小。

【讨论】:

    【解决方案3】:

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

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

    3840 / 1536 = 2.5
    2160 / 864 = 2.5
    

    也许 4K 显示器的屏幕驱动程序会在真实物理像素 (3840x2160) 和某些“逻辑像素”概念之间产生差异。目的是避免某些软件显示,例如,具有 8 个实际物理像素的 8 点文本,因为那将是不可读的。

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

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

    【讨论】:

      猜你喜欢
      • 2016-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-28
      相关资源
      最近更新 更多