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 objectroot = tkinter.Tk()# Create label widgetlabel = tkinter.Label( root, text="Hello world!" )label.pack()# Get font name (key) from the widgetfont_name = label['font']print( " font_name:", repr(font_name) )# Get Font instance and print attributesfont = tkinter.font.nametofont( font_name )for key, value in font.config().items(): print( f"{key:>10}: {repr(value)}" )# Display the widgetroot.mainloop() The above program prints out the font details as follows:
font_name: 'TkDefaultFont' family: 'sans-serif' size: 10 weight: 'normal' slant: 'roman' underline: 0overstrike: 0However, if I change it to use a tkinter.ttk.Label instead of a tkinter.Label...
import tkinter.fontimport tkinter.ttk[...]# Create label widgetlabel = 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 existIf 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 themestyle = 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 existNote 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.