Tkinter 中真正自定义的字体
- 2025-04-10 09:45:00
- admin 原创
- 17
问题描述:
我正在 Tkinter 中制作一个界面,我需要自定义字体。不仅仅是特定大小的 Helvetica 字体,还包括特定平台上通常可用的字体以外的字体。这些字体将作为图像文件或(最好)Truetype 字体文件或类似文件与程序一起保存。我不想在每台要使用该程序的机器上安装所需的字体,我只想将它们与程序一起放在同一个目录中。
tkFont 模块看起来应该做这样的事情,但我看不出它会在哪里获取运行程序的系统通常无法访问的字体的文件名。提前感谢您的帮助。
解决方案 1:
有一种方法可以将外部字体放入 Windows 上的 Tkinter。
实现该功能的关键代码是以下函数:
from ctypes import windll, byref, create_unicode_buffer, create_string_buffer
FR_PRIVATE = 0x10
FR_NOT_ENUM = 0x20
def loadfont(fontpath, private=True, enumerable=False):
'''
Makes fonts located in file `fontpath` available to the font system.
`private` if True, other processes cannot see this font, and this
font will be unloaded when the process dies
`enumerable` if True, this font will appear when enumerating fonts
See https://msdn.microsoft.com/en-us/library/dd183327(VS.85).aspx
'''
# This function was taken from
# https://github.com/ifwe/digsby/blob/f5fe00244744aa131e07f09348d10563f3d8fa99/digsby/src/gui/native/win/winfonts.py#L15
# This function is written for Python 2.x. For 3.x, you
# have to convert the isinstance checks to bytes and str
if isinstance(fontpath, str):
pathbuf = create_string_buffer(fontpath)
AddFontResourceEx = windll.gdi32.AddFontResourceExA
elif isinstance(fontpath, unicode):
pathbuf = create_unicode_buffer(fontpath)
AddFontResourceEx = windll.gdi32.AddFontResourceExW
else:
raise TypeError('fontpath must be of type str or unicode')
flags = (FR_PRIVATE if private else 0) | (FR_NOT_ENUM if not enumerable else 0)
numFontsAdded = AddFontResourceEx(byref(pathbuf), flags, 0)
return bool(numFontsAdded)
在您loadfont
使用字体文件路径调用后(可以是.fon
、.fnt
、.ttf
、.ttc
、.fot
、.otf
、.mmm
或.pfb
中的任意一个.pfm
),您可以像任何其他已安装的字体一样加载该字体。并在任何您喜欢的地方使用它。[有关更多信息,tkFont.Font(family=XXX, ...)
请参阅MSDN ]
这里最大的警告是字体的系列名称不一定是文件的名称;它嵌入在字体数据中。与其尝试解析名称,不如在字体浏览器 GUI 中查找并硬编码到应用程序中,这可能更容易。编辑tkFont.families()
:或者,根据下面 patthoyt 的评论,在(作为最后一项,或者更可靠地,通过比较加载字体之前和之后的系列列表)中查找它。
我在digsby ( license )中找到了此功能;unloadfont
如果您想在程序完成执行之前删除字体,那里定义了一个函数。 (您也可以依靠设置private
在程序结束时卸载字体。)
有兴趣的可以看看几年前在 [TCLCORE] 上关于这个话题的讨论。更多背景信息:MSDN 上的字体
解决方案 2:
如果不借助特定于平台的 hack,就无法将外部字体文件加载到 Tkinter 中。Tkinter 中没有内置任何支持此功能的东西。
解决方案 3:
这在 Windows 上对我有用,但在 Linux 上似乎不起作用:
import pyglet,tkinter
pyglet.font.add_file('file.ttf')
root = tkinter.Tk()
MyLabel = tkinter.Label(root,text="test",font=('font name',25))
MyLabel.pack()
root.mainloop()
解决方案 4:
我找到了这个讨论,其中介绍了如何使用一行文本作为图像并使用 PIL 将其放入窗口中。这可能是一个解决方案。
我在tkFont 手册页中找不到使用 tkFont 导入捆绑字体的方法。
解决方案 5:
tkextrafont在我看来是最轻量和最简单的,在 PyPI 上为 Windows 和 Linux 预建了轮子。例如:
import tkinter as tk
from tkextrafont import Font
window = tk.Tk()
font = Font(file="tests/overhaul.ttf", family="Overhaul")
tk.Label(window, text="Hello", font=font).pack()
window.mainloop()
解决方案 6:
对我来说这是一个简单的解决方案:
import pyglet, tkinter
pyglet.font.add_file("your font path here")
#then you can use the font as you would normally
解决方案 7:
对于 Linux,我能够将otf
我拥有的字体文件安装到系统字体目录中:
mkdir /usr/share/fonts/opentype/my_fonts_name
cp ~/Downloads/my_fonts_name.otf /usr/share/fonts/opentype/my_fonts_name/
我发现这个主目录有效,并最终使用它:
mkdir ~/.fonts/
cp ~/Downloads/my_fonts_name.otf ~/.fonts/
无论哪种情况,我都可以使用字体名称的字符串加载它(正如所有 tkinter 文档所示):
# unshown code
self.canvas = tk.Canvas(self.data_frame, background="black")
self.canvas.create_text(event.x, event.y, text=t, tags='clicks',
fill='firebrick1',
font=("My Fonts Name", 22))
解决方案 8:
对于将来遇到此问题的人来说,他们想要一个非常简单的解决方案和跨平台实现。这个答案可能是 2012 年 8 月 16 日无效链接中讨论的答案。您可以使用 PILImageFont.truetype()
来渲染字体,然后使用Image.new()
和ImageDraw.Draw
。我把它放到了一个类中。
class RenderFont:
def __init__(self, filename, fill=(0, 0, 0):
"""
constructor for RenderFont
filename: the filename to the ttf font file
fill: the color of the text
"""
self._file = filename
self._fill = fill
self._image = None
def get_render(self, font_size, txt, type_="normal"):
"""
returns a transparent PIL image that contains the text
font_size: the size of text
txt: the actual text
type_: the type of the text, "normal" or "bold"
"""
if type(txt) is not str:
raise TypeError("text must be a string")
if type(font_size) is not int:
raise TypeError("font_size must be a int")
width = len(txt)*font_size
height = font_size+5
font = ImageFont.truetype(font=self._file, size=font_size)
self._image = Image.new(mode='RGBA', size=(width, height), color=(255, 255, 255))
rgba_data = self._image.getdata()
newdata = []
for item in rgba_data:
if item[0] == 255 and item[1] == 255 and item[2] == 255:
newdata.append((255, 255, 255, 0))
else:
newdata.append(item)
self._image.putdata(newdata)
draw = ImageDraw.Draw(im=self._image)
if type_ == "normal":
draw.text(xy=(width/2, height/2), text=txt, font=font, fill=self._fill, anchor='mm')
elif type_ == "bold":
draw.text(xy=(width/2, height/2), text=txt, font=font, fill=self._fill, anchor='mm',
stroke_width=1, stroke_fill=self._fill)
return self._image
扫码咨询,免费领取项目管理大礼包!