我有一个有效的 python tkinter 代码,我在其中使用了我之前下载到我的计算机上的字体(它是“Trajan Pro Regular.ttf”)。我希望在任何计算机上运行 .exe 程序的任何人都能看到该字体。
我试过使用pyglet,但是没有用。我看过其他答案,但它们对我也不起作用。 使用 pyglet,我得到了这个错误:
Traceback (most recent call last):
File "ModifierTool.py", line 66, in <module>
File "pyglet\font\__init__.py", line 156, in add_file
FileNotFoundError: [Errno 2] No such file or directory: 'Trajan Pro Regular.ttf'
通过将 .ttf 文件放在与 .exe 应用程序相同的文件夹中来修复它。这当然不是我想要的,我希望将字体打包在.exe 中。 即使将 .ttf 文件放在与 .exe 相同的文件夹中,尽管清除了错误,但未下载字体的计算机上仍未显示该字体。
我正在使用 auto-py-to-exe 进行打包。
是否没有一种简单(或不)的方法来使用 ressource_path 加载嵌入的字体文件,类似于图像或其他文件?
对不起,如果我没有提供太多信息,这是一个普遍的问题,我没有太多要补充的。
我尝试使用 pyglet 但它失败了。
我之前尝试过这种方式(resource_path 也用于加载 .png 和 .pak 文件)。这是一个简化的代码片段:
import tkinter as tk
import tkinter.font as tkfont
def resource_path(relative_path):
try:
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
font_file = resource_path('Trajan Pro Regular.ttf')
trajan_pro = tkfont.Font(family='Trajan Pro', file=font_file, size=10, weight='bold')
def create_widgets(self):
button_text = tk.Label(frame, text=option, font=trajan_pro, bg='#222222', fg='white')``
那也没用。
我也尝试过使用 tkextrafont 但我无法让它工作,测试时甚至没有加载字体。见:
import tkinter as tk
import pathlib
import tkextrafont
from tkextrafont import Font
def create_widgets(self):
fontpath = pathlib.Path(__file__).parent/"Trajan Pro Regular.ttf"
font = tkextrafont.Font(file=str(fontpath), family= "Trajan Pro Regular", size= 12, weight= 'bold')
...
all_button_label = tk.Label(frame, text='All', font=font, bg='#222222', fg='white')
...
if __name__ == '__main__':
root = tk.Tk()
编辑1
我现在尝试按照@relent95 的建议使用 AddFontResourceEx 函数。实际上,在写这篇文章之前,我已经看过它,但放弃了这个想法,因为我不明白如何使用它。
使用此代码(简化):
import tkinter as tk
import tkinter.font as tkfont
import os
FR_PRIVATE = 0x10
FR_NOT_ENUM = 0x20
def loadfont(fontpath, private=True, enumerable=False):
if isinstance(fontpath, bytes):
pathbuf = create_string_buffer(fontpath)
AddFontResourceEx = windll.gdi32.AddFontResourceExA
elif isinstance(fontpath, str):
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)
class App(tk.Frame):
...
def create_widgets(self):
font_path = ("Trajan Pro Regular.ttf")
loadfont(font_path)
然后简单地提到这样的字体:
all_button_label = tk.Label(frame, text='All', font=tkfont.Font(family='Trajan Pro', size=12, weight='bold'), bg='#222222', fg='white')
我设法正确加载了字体,但没有将其下载到我的计算机上(运行 .py 和运行 .exe 时)。问题是,该文件仍然需要与要加载的程序位于同一文件夹中,这是我不想要的。我仍然不知道如何在可执行文件中包含字体。
我也尝试用
font_path = ("Trajan Pro Regular.ttf")
替换font_path = os.path.abspath("Trajan Pro Regular.ttf")
并得到相同的结果。
我正在使用 auto-py-to-exe 来打包我的 python 代码,我确实添加了
--add-data "D:/Desktop/Modifier Tool/Trajan Pro Regular.ttf;."
在命令中。我尝试在 python 中运行 PyInstaller 命令,但得到的结果与使用 auto-py-to-exe 时的结果相同。
重现问题
这里是一个突出问题的简单代码。字体不会加载,除非它在同一目录中(或已经下载),即使在 PyInstaller 中使用 --add-data 后也是如此。
import tkinter as tk
import tkinter.font as tkfont
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):
if isinstance(fontpath, bytes):
pathbuf = create_string_buffer(fontpath)
AddFontResourceEx = windll.gdi32.AddFontResourceExA
elif isinstance(fontpath, str):
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)
class App(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.master = master
self.create_widgets()
def create_widgets(self):
font_path = ("MyFont.ttf")
loadfont(font_path)
label = tk.Label(text='Hello', font=tkfont.Font(family='My Font'))
label.pack()
root = tk.Tk()
app = App(root)
root.mainloop()
我希望它是清楚的。