简体   繁体   English

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

[英]tkinter not recognizing screen resolution correctly

I'm using a 4k display (3840x2160).我使用的是 4k 显示器 (3840x2160)。

from tkinter import *

root = Tk()

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

print (width, height)

mainloop()

When I run this code the output is 1536 by 864当我运行此代码时,输​​出为 1536 x 864

Could someone explain why this is happening, and how I can fix it, Thanks.有人可以解释为什么会发生这种情况,以及我如何解决它,谢谢。

Looking at the problem, it seems to be a defect in ms-windows, and tk not using the known workaround for it.看看这个问题,它似乎是 ms-windows 中的一个缺陷,而tk没有使用已知的解决方法。

Looking at the source code for tk , in the file win/tkWinX.c the function TkWinDisplayChanged uses the windows API call GetDeviceCaps to get the screen width and height with the parameters HORZRES and VERTRES .综观的源代码tk ,在文件win/tkWinX.c功能TkWinDisplayChanged使用Windows API调用GetDeviceCaps获取屏幕宽度和高度的参数HORZRESVERTRES

Unfortunately, this page states (emphasis mine):不幸的是, 这个页面指出(强调我的):

Note GetDeviceCaps reports info that the display driver provides.注意GetDeviceCaps报告显示驱动程序提供的信息。 If the display driver declines to report any info, GetDeviceCaps calculates the info based on fixed calculations.如果显示驱动程序拒绝报告任何信息, GetDeviceCaps将根据固定计算计算信息。 If the display driver reports invalid info, GetDeviceCaps returns the invalid info.如果显示驱动程序报告无效信息,GetDeviceCaps 将返回无效信息。 Also, if the display driver declines to report info, GetDeviceCaps might calculate incorrect info because it assumes either fixed DPI (96 DPI) or a fixed size (depending on the info that the display driver did and didn't provide).此外,如果显示驱动程序拒绝报告信息,GetDeviceCaps 可能会计算出不正确的信息,因为它假定固定 DPI (96 DPI) 或固定大小(取决于显示驱动程序提供和未提供的信息)。 Unfortunately, a display driver that is implemented to the Windows Display Driver Model (WDDM) (introduced in Windows Vista) causes GDI to not get the info, so GetDeviceCaps must always calculate the info.不幸的是,为 Windows 显示驱动程序模型 (WDDM)(在 Windows Vista 中引入)实现的显示驱动程序导致 GDI 无法获取信息,因此 GetDeviceCaps 必须始终计算信息。

I would rephrase that as follows;我将改写如下; "after windows Vista, display information from GetDeviceCaps is not to be trusted". “在 Windows Vista 之后,来自GetDeviceCaps显示信息不可信”。

There there is this page about high-dpi displays .这个关于高dpi显示器的页面 This page uses some other GetDeviceCaps values, LOGPIXELSX and LOGPIXELSY (the numbers of pixels per "logical inch" in x and y) to calculate the real window size.此页面使用其他一些GetDeviceCaps值、 LOGPIXELSXLOGPIXELSY (x 和 y 中每“逻辑英寸”的像素数)来计算实际窗口大小。 The "default" DPI that GetDeviceCaps seems to use is 96. GetDeviceCaps似乎使用的“默认”DPI 是 96。

Look at this fragment of tk code from win/tkWinX.c , you can see that tk uses LOGPIXELSX and LOGPIXELSY to calculate the screen size in mm.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);

This value seems to be used to calculate winfo_fpixels() So I think that if you use root.winfo_fpixels('1i') you will get a reliable DPI value.这个值似乎是用来计算winfo_fpixels()所以我认为如果你使用root.winfo_fpixels('1i')你会得到一个可靠的DPI 值。

So, try this:所以,试试这个:

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)

Edit A workaround is to set tkinter 's scaling factor:编辑一种解决方法是设置tkinter的缩放因子:

factor = dpi / 72
root.tk.call('tk', 'scaling', factor)

I ran you code on my Raspberry pi, and got the correct value for my display (which is not a 4K display).我在我的 Raspberry pi 上运行了你的代码,并为我的显示器(不是 4K 显示器)获得了正确的值。

I do not have the solution, but I observe that the ratio between your expected/observed answers are我没有解决方案,但我观察到您预期/观察到的答案之间的比率是

3840 / 1536 = 2.5
2160 / 864 = 2.5

Maybe the screen driver for a 4K display makes a difference between real physical pixels (3840x2160) and some concept of "logical pixels".也许 4K 显示器的屏幕驱动程序在实际物理像素 (3840x2160) 和“逻辑像素”的某些概念之间有所不同。 The purpose would be to avoid some software to display, for example, a 8-point text with 8 real physical pixels, since that would be unreadable.目的是避免某些软件显示,例如,具有 8 个实际物理像素的 8 点文本,因为那将是不可读的。

I cannot test this (I do not have the hardware), it is only a hypothesis.我无法对此进行测试(我没有硬件),这只是一个假设。 I also may not have the exact terminology.我也可能没有确切的术语。

(BTW, on iOS there are the concepts of points vs pixels--you can search for these terms. Even if it doesn't answer your problem, it may be a similar problem). (顺便说一句,在 iOS 上有点 vs 像素的概念——你可以搜索这些术语。即使它没有回答你的问题,也可能是一个类似的问题)。

This should be the problem of DPI aware,Read this in MSDN official document .这应该是DPI意识的问题,在MSDN官方文档中阅读。

In windows 10: You need to use SetProcessDpiAwareness (Or SetThreadDpiAwarenessContext ),Try to use:在 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 SetProcessDpiAwareness 的要求

In Windows XP or 7,you need to use SetProcessDPIAware()在 Windows XP 或 7 中,您需要使用SetProcessDPIAware()

So all the code might be:所以所有的代码可能是:

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

If you use tk.call('tk', 'scaling') ,it is also okay.but when you use ImageGrab.grab((xxxx)) (And those functions which need to pass the position argument),maybe it will get the wrong size.如果您使用tk.call('tk', 'scaling') ,也可以。但是当您使用ImageGrab.grab((xxxx)) (以及需要传递位置参数的那些函数)时,也许它会得到错误的尺寸。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM