Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions Lib/test/test_tkinter/test_font.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,44 @@ def test_create(self):
self.assertRaises(tkinter.TclError, font.Font, root=self.root,
name='testfont', font=('Times', 10))

def test_create_from_named_font(self):
# gh-143990: a font created from a named font copies its configured
# options, preserving a size specified in pixels (a negative size).
sizetype = int if self.wantobjects else str
named = font.Font(root=self.root, name='my named font', # name with spaces
family='Times', size=-20, weight='bold')
# The source is the name of a named font or a Font representing one.
for source in ['my named font', named]:
with self.subTest(source=source):
f = font.Font(root=self.root, font=source)
self.assertEqual(f.cget('size'), sizetype(-20))
self.assertEqual(f.actual('family'), named.actual('family'))
self.assertEqual(f.actual('weight'), 'bold')
# Explicit options still override the copied settings.
f = font.Font(root=self.root, font=named, size=30)
self.assertEqual(f.cget('size'), sizetype(30))

def test_create_from_description(self):
# gh-143990: a font created from a font description is resolved via
# "font actual", so a size in pixels (negative) becomes a size in points.
descriptions = [
('Times', -20), # tuple
('Times', -20, 'bold'), # tuple with a style
'Times -20', # string
'Times -20 bold', # string with a style
'{Times New Roman} -20', # string, family with spaces
# a Font wrapping a description, as a tuple and as a string
font.Font(root=self.root, font=('Times', -20), exists=True),
font.Font(root=self.root, font='Times -20', exists=True),
]
for desc in descriptions:
with self.subTest(font=desc):
f = font.Font(root=self.root, font=desc)
# resolved as if the description were wrapped by exists=True
wrapped = font.Font(root=self.root, font=desc, exists=True)
self.assertEqual(f.actual(), wrapped.actual())
self.assertGreater(int(f.cget('size')), 0) # pixels -> points

def test_existing(self):
sizetype = int if self.wantobjects else str

Expand Down Expand Up @@ -109,16 +147,19 @@ def test_existing(self):
self.assertRaises(TypeError, font.Font, root=self.root, exists=True)

def test_copy(self):
f = font.Font(root=self.root, family='Times', size=10, weight='bold')
# size=-20 (pixels): copy() copies the configured options, so the
# size is preserved rather than resolved (gh-143990).
f = font.Font(root=self.root, family='Times', size=-20, weight='bold')
copied = f.copy()
self.assertIsInstance(copied, font.Font)
self.assertIsNot(copied, f)
self.assertNotEqual(copied.name, f.name)
self.assertEqual(copied.actual(), f.actual())
# The copy is independent of the original.
sizetype = int if self.wantobjects else str
self.assertEqual(copied.cget('size'), sizetype(-20))
# The copy is independent of the original.
copied.configure(size=20)
self.assertEqual(f.cget('size'), sizetype(10))
self.assertEqual(f.cget('size'), sizetype(-20))
self.assertEqual(copied.cget('size'), sizetype(20))
self.assertRaises(TypeError, f.copy, 'x')

Expand Down
13 changes: 10 additions & 3 deletions Lib/tkinter/font.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,15 @@ def __init__(self, root=None, font=None, name=None, exists=False,
self.name = font
else:
if font:
# start from the actual settings of the given font
font = tk.splitlist(tk.call("font", "actual", font))
# start from the settings of the given font
try:
# a named font: copy its options, preserving the size,
# which can be negative (specified in pixels)
font = tk.splitlist(tk.call("font", "configure", font))
except tkinter.TclError:
# a font description: resolve it ("font configure" only
# accepts a font name); this loses a size in pixels
font = tk.splitlist(tk.call("font", "actual", font))
if options:
# explicit options override the corresponding settings
settings = self._mkdict(font)
Expand Down Expand Up @@ -146,7 +153,7 @@ def __del__(self):

def copy(self):
"Return a distinct copy of the current font"
return Font(self._tk, **self.actual())
return Font(self._tk, self.name)

def actual(self, option=None, displayof=None):
"Return actual font attributes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
A :class:`tkinter.font.Font` created from a named font,
including by :meth:`~tkinter.font.Font.copy`,
now copies its configured options rather than the options resolved by Tcl's ``font actual``,
preserving a size specified in pixels (a negative size).
Loading