为啥在 Tkinter 中计算的字符串的像素宽度和高度因平台而异?

Posted

技术标签:

【中文标题】为啥在 Tkinter 中计算的字符串的像素宽度和高度因平台而异?【英文标题】:Why does the calculated width and height in pixel of a string in Tkinter differ between platforms?为什么在 Tkinter 中计算的字符串的像素宽度和高度因平台而异? 【发布时间】:2011-02-24 17:21:52 【问题描述】:

我有一个 Python 脚本,它需要计算以任意字体显示的任意字符串的确切大小,以便生成简单的图表。我可以用 Tkinter 轻松做到这一点。

import Tkinter as tk
import tkFont
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=200)
canvas.pack()
(x,y) = (5,5)
text = "yellow world"
fonts = []
for (family,size) in [("times",12),("times",24)]:
    font = tkFont.Font(family=family, size=size)
    (w,h) = (font.measure(text),font.metrics("linespace"))
    print "%s %s: (%s,%s)" % (family,size,w,h)
    canvas.create_rectangle(x,y,x+w,y+h)
    canvas.create_text(x,y,text=text,font=font,anchor=tk.NW)
    fonts.append(font) # save object from garbage collecting
    y += h+5
tk.mainloop()

结果似乎取决于 Python 和/或系统的版本:

Python 2.5 Mac 0S X, times 12: (63,12), times 24: (128,24). Python 2.6 Mac OS X, times 12: (64,14), times 24: (127,27). Python 2.6 Windows XP, times 12: (78,19), times 24: (169,36) http://grab.by/grabs/d24a5035cce0d8032ea4e04cb8c85959.png

在 Ned Batchelder 提到它之后,我发现字体的大小因平台而异。只要您坚持使用与自身保持一致的 Tkinter,它可能不会破坏交易。但是我的complete program 确实使用 Tkinter 来执行实际的绘图:它只是依靠其字体大小计算来生成输出(在 SVG 中或作为要发送到 Nodebox 的 Python 脚本) .事情就真的出问题了:

Output of mocodo http://grab.by/grabs/f67b951d092dd1f4f490e1469a53bca2.png

(请查看image in real size。请注意,用于这些输出的主要字体不是Times,而是Trebuchet MS。)

我现在怀疑 Tkinter 无法避免这种差异。您会推荐哪些其他跨平台解决方案?

【问题讨论】:

您是否截取了这些字体的文本,并将它们与您得到的值进行比较? 我看不出您希望拥有什么样的“跨平台”解决方案...不同的平台和不同的硬件使用不同的字体分辨率和字体渲染算法。我认为你很难为所有这些问题得到一个恒定的答案。 如果可以,请更新图像,因为它们现在已经消失了。 @Nae 确实,对此感到抱歉:( TinyGrab 现在似乎已经死了。谷歌没有给我带有文件名的图像,Wayback Machine 告诉我:“没有为这个域捕获 URL”对于这个 SO页面。还有其他选择吗?我没有本地副本。 @Aristide 我不知道是否有。 【参考方案1】:

在 tkinter 中,如果字体大小为正,则以磅为单位(1 磅(缩写为 pt)等于 1/72 英寸) 如果字体大小以负值给出,则以像素为单位。

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案2】:

在找了很久之后,我终于找到了一种方法来获取任何字体和大小的某些文本的宽度!

from tkinter import *

Window = Tk()
Window.geometry("500x500+80+80")

frame = Frame(Window) # this will hold the label
frame.pack(side = "top")

# CALCULATE:
measure = Label(frame, font = ("Purisa", 10), text = "The width of this in pixels is.....", bg = "yellow")
measure.grid(row = 0, column = 0) # put the label in
measure.update_idletasks() # this is VERY important, it makes python calculate the width
width = measure.winfo_width() # get the width

# PROOF IT WORKS:
canvas = Canvas(frame, width = 400, height = 200, bg = "light green")
canvas.grid(row = 1, column = 0, columnspan = 100) # collumnspan is 100 so that the line lines up with the text
line = canvas.create_line(0, 10, width, 10, width = 4) # make a line the same length as the text
canvas.create_text(10, 20, font = ("Purisa", 10), text = "... "+str(width)+" Pixels", anchor = "nw")

我做的这行证明这适用于任何字体。

据我所知,我已经针对不同的字体和大小进行了测试。

这是输出的图片:

【讨论】:

这将阻止所有认为输入 WWWWWWWWWWW 很有趣的用户,因此它不适合屏幕!!!!!!!!!!!!【参考方案3】:

你有两个问题。让我们一次解决一个

1:python 2.5 和 2.6 同平台同字体的区别

这两个版本的python使用不同版本的tk。在我的 mac 机器上,2.5 使用 tk 版本 8.4.19,而 2.6 使用 8.5.7。在 tk 的 8.5.2 版中,对 tk 的字体测量功能进行了一些更改。假设这些更改是改进,我认为可以肯定地假设您从 python 2.6 获得的数字比从 2.5 获得的数字更准确。

2:mac上python 2.6和PC上python 2.6的区别。

显然,从您提供的屏幕截图中可以看出,PC 使用的是更大的字体,因此您会获得更大的测量数字。问题是,为什么?您以磅为单位指定字体大小(1/72 英寸)。为了让 Tk(或任何渲染系统)渲染字体,它需要知道实际显示器上每英寸有多少像素。这在不同的系统上会有所不同,并且底层操作系统并不总是给 Tk 一个准确的数字来进行计算。

从历史上看,Apple 和 Microsoft 已将 72ppi 和 96ppi 标准化,而不管实际显示如何,因此数字总是会有所不同。有关 mac 和 windows 如何计算像素密度的差异的更多信息,请参阅***上的Dots Per Inch 文章。

您可以尝试通过以像素而不是点为单位指定字体来解决此问题。您可以通过使用负数作为字体大小来做到这一点。

最后,您可以添加到您的小示例代码中的一件事是打印出font.actual() 命令的结果——您可能会在 windows 和 mac 框之间看到一些不同的东西,这可以解释那里的差异。这会准确告诉您 Tk 正在使用哪种字体。

【讨论】:

非常感谢您的回答!你让我理解并解决了这两个问题。第一个来自我的 Python 2.6 版本:即 2.6.1,它实际上存在关于字体度量的回归错误:更新到 Python 2.6.5 是一个简单的解决方案。当我使用负数指定字体大小时,第二个问题也消失了。这种行为实际上在 tcl.tk/man/tcl8.4/TkCmd/tk.htm 的 Tk 文档中进行了解释,我不知道。现在一切顺利,我非常感激! @Aristide:如果我的回答或任何其他回答对您有帮助,请考虑将答案标记为“已接受”——这是您作为提问者的职责的一部分。 你的回答对我有帮助。我已经将其标记为有用,但在我余下的职责中,系统一直在说:“你可以接受这个答案并在 10 小时内奖励你的赏金”。我想至少有 24 小时的延迟。请放心,我明天很乐意重试! 但是字体大小仍然以磅为单位吗?和屏幕分辨率有关系吗? @CoolCloud:是的,字体通常以磅为单位定义。但是,不同的显示器具有不同的像素密度。并非所有显示器都是 72dpi。【参考方案4】:

您没有做错任何事:字体的大小确实因平台而异。

我不确定为什么 Python 版本很重要,但差异只有一个像素,因此可能是不同的舍入或相同字体的不同渲染。

【讨论】:

python 版本很重要,因为 tk 版本很重要——字体测量在 tk 8.4.x 和 8.5.x 之间发生了变化(分别由 python 2.5 和 2.6 使用的版本)。

以上是关于为啥在 Tkinter 中计算的字符串的像素宽度和高度因平台而异?的主要内容,如果未能解决你的问题,请参考以下文章

如何计算 JavaFX 中字符串的像素宽度?

等宽字符的像素宽度

Tkinter笔记本-窗口宽度的选项卡太多

表格的宽度不能小于 140 像素。为啥?

tkinter ttk treeview 如何设置固定宽度?为啥它会随着列数而变化?

为啥 Firefox 的最小宽度最小为 615 像素?