Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I define (and save) Tkinter fonts inside a function?

Defining a font inside a function and in the main body of the script seems to behave differently, and I can't seem to figure out how it's supposed to work.

For example, the Label in this example ends up being in a larger font, as expected:

from Tkinter import *
from ttk import *

import tkFont

root = Tk()

default = tkFont.Font(root=root, name="TkTextFont", exists=True)
large = default.copy()
large.config(size=36)

style = Style(root)
style.configure("Large.TLabel", font=large)

root.title("Font Test")

main_frame = Frame(root)
Label(main_frame, text="Large Font", style="Large.TLabel").pack()

main_frame.pack()
root.mainloop()

Screenshot of working version

However, if I try to define styles inside a function, it seems like the font gets deleted or garbage collected and is not available by the time the widget needs to use it:

from Tkinter import *
from ttk import *

import tkFont

def define_styles(root):
    default = tkFont.Font(root=root, name="TkTextFont", exists=True)

    large = default.copy()
    large.config(size=36)

    style = Style(root)
    style.configure("Large.TLabel", font=large)


root = Tk()

root.title("Font Test")

define_styles(root)

main_frame = Frame(root)
Label(main_frame, text="Large Font", style="Large.TLabel").grid(row=0, column=0)

main_frame.pack()
root.mainloop()

Screenshot of non-working version

Printing out tkFont.names() in the first version just before the main_frame.pack() lists the custom font as font<id>, but printing the same in the second version does not list the custom font outside the define_styles function. Do I have to do something special to save them?

Why can't I put that code in a function? Am I fundamentally misunderstanding something about how Fonts are supposed to be used? tkFont seems to have some kind of font registry, why aren't mine sticking around?

like image 279
Collin Avatar asked Sep 04 '14 17:09

Collin


People also ask

What font does tkinter use by default?

Output. Running the above code will set the default font as "lucida 20 bold italic" for all the widgets that uses textual information.

What is font in Python?

A font can be specified as tuple containing a family name, a height in points, and optionally a string with one or more styles. To get the default size and style, you can give the font name as a single string.


1 Answers

I have no evidence to back this up, but I believe that your large Font object is being garbage collected by Python once define_styles ends. This is because no pure Python objects have any references to it, even though the underlying Tcl implementation is still using it. This is a problem that afflicts Tkinter's PhotoImage class, as well.

The workaround is to keep the object alive by making a long-lived reference to it. Just assign it to any old attribute on the root object, for example.

def define_styles(root):
    default = tkFont.Font(root=root, name="TkTextFont", exists=True)

    large = default.copy()
    large.config(size=36)

    style = Style(root)
    style.configure("Large.TLabel", font=large)
    root.myfont = large

Result:

enter image description here

like image 72
Kevin Avatar answered Sep 27 '22 23:09

Kevin