Skip to content

Commit 266cf83

Browse files
[3.15] gh-156961: Fix tkinter.font.Font for a font name returned as a Tcl object (GH-157028)
Tk can return a font name as a Tcl object, for example from ttk.Style().lookup("TButton", "font"), Menu.entrycget("font"), ttk.Entry.cget("font"), or the default value in the result of configure(). Such an object does not compare equal to a string, so it was not recognized as the name of an existing named font. Keep it as is, so that it is passed back to Tk, and only convert it where it is compared with a string. (cherry picked from commit 62cbd34)
1 parent e325fae commit 266cf83

3 files changed

Lines changed: 44 additions & 3 deletions

File tree

‎Lib/test/test_tkinter/test_font.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ def setUpClass(cls):
2020
except tkinter.TclError:
2121
cls.font = font.Font(root=cls.root, name=fontname, exists=False)
2222

23+
def tcl_font_object(self, desc):
24+
# Return a font name or description as a Tcl object representing a
25+
# font, as Tk returns for example from ttk.Style().lookup().
26+
tk = self.root.tk
27+
tk.call('set', '_font', desc)
28+
tk.eval('font measure $_font x') # convert the Tcl object to a font
29+
obj = tk.call('set', '_font')
30+
tk.call('unset', '_font')
31+
return obj
32+
2333
def test_configure(self):
2434
self.assertEqual(self.font.config, self.font.configure)
2535
options = self.font.configure()
@@ -73,6 +83,27 @@ def test_create_from_description(self):
7383
f = font.Font(root=self.root, font=desc)
7484
self.assertGreater(int(f.cget('size')), 0) # pixels -> points
7585

86+
def test_tcl_object(self):
87+
# Tk can return a font as a Tcl object (gh-156961).
88+
if not self.wantobjects:
89+
self.skipTest('Tcl objects are converted to strings')
90+
obj = self.tcl_font_object(fontname)
91+
self.assertEqual(obj.typename, 'font')
92+
93+
# It can be used as the name of an existing named font.
94+
for f in (font.Font(root=self.root, name=obj, exists=True),
95+
font.nametofont(obj, root=self.root)):
96+
# The Tcl object is kept as is, so that it is passed back to Tk.
97+
self.assertIs(f.name, obj)
98+
self.assertEqual(str(f), fontname)
99+
self.assertEqual(f.actual(), self.font.actual())
100+
self.assertEqual(f, self.font)
101+
self.assertEqual(self.font, f)
102+
# Referring to a non-existent named font still fails.
103+
self.assertRaisesRegex(tkinter.TclError, 'named font nosuchfont',
104+
font.Font, root=self.root, exists=True,
105+
name=self.tcl_font_object('nosuchfont'))
106+
76107
def test_copy(self):
77108
# size=-20 (pixels): copy() copies the configured options, so the
78109
# size is preserved rather than resolved (gh-143990).

‎Lib/tkinter/font.py‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ def __init__(self, root=None, font=None, name=None, exists=False,
9191
if exists:
9292
self.delete_font = False
9393
# confirm font exists
94-
if self.name not in tk.splitlist(tk.call("font", "names")):
94+
name = getattr(name, 'string', name) # can be a Tcl object
95+
if name not in tk.splitlist(tk.call("font", "names")):
9596
raise tkinter._tkinter.TclError(
9697
"named font %s does not already exist" % (self.name,))
9798
# if font config info supplied, apply it
@@ -106,7 +107,7 @@ def __init__(self, root=None, font=None, name=None, exists=False,
106107
self._call = tk.call
107108

108109
def __str__(self):
109-
return self.name
110+
return str(self.name)
110111

111112
def __repr__(self):
112113
return f"<{self.__class__.__module__}.{self.__class__.__qualname__}" \
@@ -115,7 +116,13 @@ def __repr__(self):
115116
def __eq__(self, other):
116117
if not isinstance(other, Font):
117118
return NotImplemented
118-
return self.name == other.name and self._tk == other._tk
119+
name = self.name
120+
other_name = other.name
121+
if type(name) is not type(other_name):
122+
# A Tcl object does not compare equal to a string.
123+
name = getattr(name, 'string', name)
124+
other_name = getattr(other_name, 'string', other_name)
125+
return name == other_name and self._tk == other._tk
119126

120127
def __getitem__(self, key):
121128
return self.cget(key)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`tkinter.font.nametofont` and the :class:`tkinter.font.Font`
2+
constructor for a font name returned by Tk as a Tcl object,
3+
for example by :meth:`ttk.Style.lookup() <tkinter.ttk.Style.lookup>`.

0 commit comments

Comments
 (0)