skip to Main Content

I wish to retrieve the font object currently assigned to an arbitrary tkinter.ttk.Label widget.

(To be more exact, I want to obtain the exact configuration attributes of the widget’s current tkinter.font.Font instance — family, size, weight, slant, underline, and overstrike — in order to programmatically define modified variants of the same base font.)

This is easy to do with a plain, unthemed tkinter.Label:

import tkinter.font

# Create root object
root = tkinter.Tk()

# Create label widget
label = tkinter.Label( root, text="Hello world!" )
label.pack()

# Get font name (key) from the widget
font_name = label['font']
print( " font_name:", repr(font_name) )

# Get Font instance and print attributes
font = tkinter.font.nametofont( font_name )
for key, value in font.config().items():
    print( f"{key:>10}: {repr(value)}" )

# Display the widget
root.mainloop() 

The above program prints out the font details as follows:

 font_name: 'TkDefaultFont'
    family: 'sans-serif'
      size: 10
    weight: 'normal'
     slant: 'roman'
 underline: 0
overstrike: 0

However, if I change it to use a tkinter.ttk.Label instead of a tkinter.Label

import tkinter.font
import tkinter.ttk

[...]

# Create label widget
label = tkinter.ttk.Label( root, text="Hello world!" )
label.pack()

…it no longer works. This is because the expression label['font'] now returns an empty string instead of a valid Tkinter font name. The empty string gets assigned to font_name and then used in a nametofont function call, resulting to:

 font_name: ''
Traceback (most recent call last):
  File "fonttest.py", line 14, in <module>
    font = tkinter.font.nametofont( font_name )
  File "/usr/lib/python3.10/tkinter/font.py", line 23, in nametofont
    return Font(name=name, exists=True, root=root)
  File "/usr/lib/python3.10/tkinter/font.py", line 87, in __init__
    raise tkinter._tkinter.TclError(
_tkinter.TclError: named font font1 does not already exist

If I change the program yet further and specify a named ttk theme by adding the following lines after the creation of the root object but before creating the ttk.Label widget…

# Set up a ttk theme
style = tkinter.ttk.Style()
style.theme_use( 'breeze' )

…I get a somewhat different result, but still no valid font name:

 font_name: <font object: 'Helvetica 10'>
Traceback (most recent call last):
  File "fonttest.py", line 20, in <module>
    font = tkinter.font.nametofont( font_name )
  File "/usr/lib/python3.10/tkinter/font.py", line 23, in nametofont
    return Font(name=name, exists=True, root=root)
  File "/usr/lib/python3.10/tkinter/font.py", line 87, in __init__
    raise tkinter._tkinter.TclError(
_tkinter.TclError: named font Helvetica 10 does not already exist

Note how label['font'] no longer returned a string — empty or otherwise — but a “font object”.


Adding some more debug prints…

print( " font_name:", repr(font_name) )
print( "font_name.__class__:", font_name.__class__ )
print( "font_name.__str__():", repr(font_name.__str__()) )
print( "dir( font_name ):", dir( font_name ) )
print( "tkinter.font.names():", tkinter.font.names() ) 

…gives us this output:

 font_name: <font object: 'Helvetica 10'>
font_name.__class__: <class '_tkinter.Tcl_Obj'>
font_name.__str__(): 'Helvetica 10'
dir( font_name ): ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'string', 'typename']
tkinter.font.names(): ('TkCaptionFont', 'TkSmallCaptionFont', 'TkTooltipFont', 'TkFixedFont', 'TkHeadingFont', 'TkMenuFont', 'TkIconFont', 'TkTextFont', 'TkDefaultFont')

It is not really a “font name”, and not an instance of tkinter.font.Font either, but an instance of the class _tkinter.Tcl_Obj. The dir output lists a member called typename so I added this line, too…

print( "font_name.typename:", repr(font_name.typename) )

…which yields:

font_name.typename: 'font'

To summarize: sometimes the themed ttk widgets return an empty string for their font, some other times they return a “font object”. The “font object”, if present, is not a tkinter.font.Font instance but a _tkinter.Tcl_Obj instance — apparently a Python wrapper for an internal TCL font object whose properties are inaccessible to Python code?

Since this curious font object is convertible to a string (Helvetica 10), it could technically be used as a Tkinter “font name” in a font lookup call. Alas, the string that the object holds or converts into is still not a registered “font name” (that tkinter.font.names() ortkinter.font.nametofont( font_name ) would recognize) so the attributes of the underlying font remain inaccessible.

So, my question is: what is the correct way to programmatically obtain the current tkinter.font.Font instance (or the equivalent attribute details — family, size, weight, slant, underline, overstrike) of the font assigned to a tkinter.ttk.Label widget both when a theme has been explicitly set and when it has not been set?

My test environment in the above was Python 3.10.12 that comes standard with Kubuntu (Ubuntu) 22.04 LTS. The packages python3-tk (version 3.10.8-1~22.04) and python3-ttkthemes (version 3.2.2+git20220101+07e6509cc6bf-1) were manually installed from the standard Ubuntu repositories.

2

Answers


  1. not sure my code is how it should be done, but googling around SO I figured out that there are ways to solve your question using tkinter.font.Font toghether with tkinter.ttk.Style class (tkinter.ttk.Style) , see code below :

    import tkinter.font
    
    import tkinter.ttk
    
    from tkinter.font import BOLD, Font 
    
    # Create root object
    root = tkinter.Tk()
    
    
    # Set up a ttk theme
    stylez = tkinter.ttk.Style()
    # style.theme_use( 'breeze' )  ### not used see below
    
    
    default_font_root = tkinter.font.nametofont(stylez.lookup(root , 'font')).actual()
    
    print('default_font_root : ' , default_font_root)
    
    print("default_font_root size : " , default_font_root['size'])
    
    # Set up a ttk theme
    print(stylez.theme_names())
    
    stylez.theme_use(stylez.theme_names()[2])
    
    #same of 
    stylez.theme_use('default')
    
    #create Font
    fontz = Font(root , family = 'Ubuntu',  size = 64 , weight = BOLD , underline = 1)
    
    
    stylez.configure('my.TLabel', font = fontz)
    
    
    # Create label widget
    label = tkinter.ttk.Label(root, text="Hello world!" , style = 'my.TLabel')
    
    label.pack()
    
    
    print(""""style.lookup("TLabel", "font" : """ , stylez.lookup("TLabel", "font"))                    
                           
    print(""""style.lookup("my.TLabel", "font" : """ , stylez.lookup("my.TLabel", "font"))
                           
    
    
    label_font = tkinter.font.nametofont(stylez.lookup(label['style'] , 'font')).actual()
    
    label_font_name = tkinter.font.nametofont(stylez.lookup(label['style'] , 'font')).name
    
    print('label_font : ' , label_font, label_font_name)
    
    
    print("fontz['size'] : ", fontz['size'])
    
    # Display the widget
    root.mainloop() 
    
    
    """
    this is used to get available Font families in your system :
        
        from tkinter import Tk, font
    
    
        root = Tk()
    
        for i in sorted(font.families()):
            
            
            print('n',i)
    
    """
    
    
    Login or Signup to reply.
  2. You can do that by using tkinter.ttk.Style():

    import tkinter as tk
    import tkinter.font
    import tkinter.ttk
    
    root = tk.Tk()
    label = tkinter.ttk.Label(root, text="Hello world!")
    label.pack()
    
    style = tkinter.ttk.Style()
    widget_style = label.winfo_class()
    print("Widget Style:", widget_style)
    
    font_name = style.lookup(widget_style, 'font')
    print("Font Name:", font_name)
    
    font = tkinter.font.nametofont(font_name)
    for key, value in font.config().items():
        print(f"{key:>10}: {repr(value)}")
    
    root.mainloop()
    

    Which prints on my end:

    Widget Style: TLabel
    Font Name: TkDefaultFont
        family: 'Segoe UI'
          size: 9
        weight: 'normal'
         slant: 'roman'
     underline: 0
    overstrike: 0
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search