Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
168 views
in Technique[技术] by (71.8m points)

python - Truly custom font in Tkinter

I am making an interface in Tkinter and I need to have custom fonts. Not just, say, Helvetica at a certain size or whatever, but fonts other than what would normally be available on any given platform. This would be something that would be kept with the program as an image file or (preferably) Truetype font file or similar. I don't want to have to install the desired fonts on every machine that is going to use the program, I just want to carry them around with the program in the same directory.

The tkFont module looks like it ought to do something like this, but I can't see where it would take a filename for a font not normally accessible to the system running the program. Thanks in advance for your help.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There is a way of getting external fonts into Tkinter [Windows]

(on Windows, at least)

The key piece of code to make this work is the following function:

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)

After you call loadfont with the path to your font file (which can be any of .fon, .fnt, .ttf, .ttc, .fot, .otf, .mmm, .pfb, or .pfm), you can load the font like any other installed font tkFont.Font(family=XXX, ...). and use it anywhere you like. [See MSDN for more info]

The biggest caveat here is that the family name of the font won't necessarily be the name of the file; it's embedded in the font data. Instead of trying to parse out the name, it would probably be easier to just look it up in a font browser GUI and hardcode into your application. edit: or, per patthoyt's comment below, look it up in tkFont.families() (as the last item, or, more robustly, by comparing the list of families before and after loading the font).

I found this function in digsby (license); there's an unloadfont function defined there if you want to remove the font before your program finishes executing. (You can also just rely on the private setting to unload the font when your program ends.)

For anyone interested, here is a discussion on this topic on [TCLCORE] from a few years ago. Some more background: fonts on MSDN


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...