So i have this assignment at uni that i need to build a desktop App with python, and the gui interface we need to use is Tkinter. And i am first figuring out what fonts/simples layouts and designs i am going to do. But one thing that i am stuck with and i have done a test in my other computer, is that if i use a font that i have on the computer i am building the app on and that same font is not installed on the other computer that it's going to use the APP, the font i chose won't be the same. Is there any way to surpass this, like installing/sending the font within the .exe file or knowing what fonts are available in every computer/device/machine? Thank you so much!
Asked
Active
Viewed 138 times
0
-
1Check the answer to this . https://stackoverflow.com/questions/39614027/list-available-font-families-in-tkinter. If a font you are using is missing program a specific fall back choice of fonts. – Carl_M Dec 20 '22 at 14:20
-
For me, the wise thing is to stick to common fonts that are available everywhere. – Bryan Oakley Dec 20 '22 at 14:47
1 Answers
0
I've found this method works well for adding custom fonts to the system at runtime. The caveats assumed here are:
- Your app is running on Windows machines only
- Your executable was built with Pyinstaller
import ctypes
import sys
from pathlib import Path
def is_exe() -> bool:
"""Returns True if the app is running as an exe, False otherwise"""
try:
sys._MEIPASS
except AttributeError:
return False
return True
def fetch_resource(resource_path: str | Path) -> Path:
"""
Fetches resources required by the executable version of the app after it's
built with Pyinstaller. If running in the development env, the resource
path is returned unchanged.
Any required resources must be added to the 'datas' list in the build spec.
"""
if is_exe():
base_path = Path(sys._MEIPASS)
return base_path.joinpath(resource_path)
return resource_path
def load_font(
font_path: Path,
private: bool = True,
enumerable: bool = False
) -> bool:
"""
Loads custom fonts from the given font path
font_path - the path to the font file
private - if True, this font will be unloaded when the parent process dies
enumerable - if True, this font will be listed when enumerating fonts
returns True if a font was loaded successfully, False otherwise
"""
# modified from https://stackoverflow.com/a/30631309
FR_PRIVATE = 0x10
FR_NOT_ENUM = 0x20
flags = (FR_PRIVATE * private) | (FR_NOT_ENUM * (1 - enumerable))
font_fetch = str(fetch_resource(font_path))
pathbuf = ctypes.create_unicode_buffer(font_fetch)
add_font = ctypes.windll.gdi32.AddFontResourceExW
font_added = add_font(ctypes.byref(pathbuf), flags, 0)
return bool(font_added)
This is a lot of mucking around due to how Pyinstaller bundles executables (particularly "single file" builds, which are rather common). All of your app's assets go into a special directory in your system TEMP folder, which is what the fetch_resource
function is dealing with for you.
More info on the load_font
function can be found in this answer, which is what mine is based on.

JRiggles
- 4,847
- 1
- 12
- 27