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
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 withtkinter.ttk.Style
class (tkinter.ttk.Style) , see code below :You can do that by using
tkinter.ttk.Style()
:Which prints on my end: