python Tkinter中真正的自定义字体

cvxl0en2  于 2023-01-11  发布在  Python
关注(0)|答案(8)|浏览(158)

我正在做一个界面在Tkinter和我需要有自定义字体。不仅仅是,说,Helvetica在一定的大小或什么,但字体不同于任何给定平台上通常可用的字体。这将是作为图像文件或(最好是)TrueType字体文件或类似的。我不想必须安装所需的字体在每台机器上,这是要使用的程序,我只想把它们和程序放在同一个目录下。
tkFont模块看起来应该做类似这样的事情,但是我看不出它会把一个字体的文件名放在哪里,而这个字体通常不能被运行程序的系统访问。

nwsw7zdq

nwsw7zdq1#

有一种方法可以将外部字体导入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中查找名称并将其硬编码到应用程序中。edit:或者,根据patthoyt下面的评论,在tkFont.families()中查找它(作为最后一项,或者,更可靠地,通过比较加载字体前后的系列列表)。
我在digsby(license)中找到了这个函数;这里定义了一个unloadfont函数,如果你想在程序执行完之前删除字体的话(你也可以只依赖private设置在程序结束时卸载字体)。
对于感兴趣的人,here是几年前在[TCLCORE]上对这个主题的讨论。fonts on MSDN

mitkmikd

mitkmikd2#

这对我在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()
sy5wg1nm

sy5wg1nm3#

不借助特定于平台的技巧是无法将外部字体文件加载到Tkinter中的,Tkinter没有内置的东西来支持它。

vxf3dgd4

vxf3dgd44#

我找到了this discussion,他们在那里介绍了如何使用一行文本作为图像,并使用PIL将其放置到窗口中。这可能是一个解决方案。
我找不到使用tkFont在tkFont man page中导入捆绑字体的方法。

cotxawn7

cotxawn75#

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

ehxuflar6#

对我来说,这是一个简单的解决方案:

import pyglet, tkinter
pyglet.font.add_file("your font path here")
#then you can use the font as you would normally
lrl1mhuk

lrl1mhuk7#

对于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/

在任何一种情况下,我都可以使用一个字符串font-name来加载它(正如所有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))
hvvq6cgz

hvvq6cgz8#

对于将来遇到这个问题的人,他们需要一个非常简单的解决方案和跨平台的实现。这个答案可能是2012年8月16日无效链接中讨论的答案。你可以使用PIL的ImageFont.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

相关问题