Making a copy of tkinter.font.Font object without changing its family

How do I make a copy of a tkinter.font.Font object without changing it’s family option?

Below is the script I had used and it’s output. I was surprised to discover that it’s .copy method changed the font’s family.

Test Script:

import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkFont

class App(ttk.Frame):

    def __init__(self, parent):
        ttk.Frame.__init__(self, parent)
        self.parent = parent
        self._defaultFont=tkFont.Font(family='Times',size='11',weight='normal')        

        self.setFont()


    def setFont(self):
        """Customise Font styles""" 
        Font = self._defaultFont.copy()
        FontBold = self._defaultFont.copy()
        FontBold.config(weight='bold')
        print('_defaultFont.configure = ', self._defaultFont.configure())
        print('Font.configure         = ', Font.configure())
        print('Font.actual            = ', Font.actual())
        print('FontBold.configure     = ', FontBold.configure())
        print('FontBold.actual        = ', FontBold.actual())


if __name__ == '__main__':
    root = tk.Tk()
    root.title('Test')
    root.geometry('200x100')
    app = App(root)
    app.pack(expand=1, fill='both')
    root.mainloop()

Output:

_defaultFont.configure =  {'slant': 'roman', 'overstrike': 0, 'underline': 0, 'family': 'Times', 'size': 11, 'weight': 'normal'}
Font.configure         =  {'slant': 'roman', 'overstrike': 0, 'underline': 0, 'family': 'Nimbus Roman No9 L', 'size': 11, 'weight': 'normal'}
Font.actual            =  {'slant': 'roman', 'overstrike': 0, 'underline': 0, 'family': 'Nimbus Roman No9 L', 'size': 11, 'weight': 'normal'}
FontBold.configure     =  {'slant': 'roman', 'overstrike': 0, 'underline': 0, 'family': 'Nimbus Roman No9 L', 'size': 11, 'weight': 'bold'}
FontBold.actual        =  {'slant': 'roman', 'overstrike': 0, 'underline': 0, 'family': 'Nimbus Roman No9 L', 'size': 11, 'weight': 'bold'}

Answer

The issue described in my question occurred when I had used an invalid font family, i.e. a font that does not exist in tkinter.font.families(), to create a tkinter.font.Font() object. So there is a need to ensure that a tkinter.font.families() font is used with the .copy() method.

To demonstrate this, I had investigated the outcome from using fonts such as 'Times' and 'Helvetica', which does not reside in my Ubuntu 16.04.3 system, and from using fonts such as 'Ubuntu' and 'URW Gothic L', which resides in my system. The output from my investigations are shown below. They show that the .copy() method of class tkinter.font.Font will exactly duplicate the font’s attributes only if the font used are reported by tkinter.font.families(). The revised script that I had used to investigate my issue is provided below.

If a font in tkFont.families() is not used:

_defaultFont.configure =  {'family': 'Times', 'size': 11, 'underline': 0, 'slant': 'roman', 'overstrike': 0, 'weight': 'normal'}
_defaultFont.actual    =  {'family': 'Nimbus Roman No9 L', 'size': 11, 'underline': 0, 'slant': 'roman', 'overstrike': 0, 'weight': 'normal'}
Font.configure         =  {'family': 'Nimbus Roman No9 L', 'size': 11, 'underline': 0, 'slant': 'roman', 'overstrike': 0, 'weight': 'normal'}
Font.actual            =  {'family': 'Nimbus Roman No9 L', 'size': 11, 'underline': 0, 'slant': 'roman', 'overstrike': 0, 'weight': 'normal'}
FontBold.configure     =  {'family': 'Nimbus Roman No9 L', 'size': 11, 'underline': 0, 'slant': 'roman', 'overstrike': 0, 'weight': 'bold'}
FontBold.actual        =  {'family': 'Nimbus Roman No9 L', 'size': 11, 'underline': 0, 'slant': 'roman', 'overstrike': 0, 'weight': 'bold'}

_defaultFont.configure =  {'family': 'Helvetica', 'underline': 0, 'size': 11, 'weight': 'normal', 'slant': 'roman', 'overstrike': 0}
_defaultFont.actual    =  {'family': 'Nimbus Sans L', 'underline': 0, 'size': 11, 'weight': 'normal', 'slant': 'roman', 'overstrike': 0}
Font.configure         =  {'family': 'Nimbus Sans L', 'underline': 0, 'size': 11, 'weight': 'normal', 'slant': 'roman', 'overstrike': 0}
Font.actual            =  {'family': 'Nimbus Sans L', 'underline': 0, 'size': 11, 'weight': 'normal', 'slant': 'roman', 'overstrike': 0}
FontBold.configure     =  {'family': 'Nimbus Sans L', 'underline': 0, 'size': 11, 'weight': 'bold', 'slant': 'roman', 'overstrike': 0}
FontBold.actual        =  {'family': 'Nimbus Sans L', 'underline': 0, 'size': 11, 'weight': 'bold', 'slant': 'roman', 'overstrike': 0}

If a font in tkFont.families() is used:

Ubuntu in tkFont.families()
_defaultFont.configure =  {'family': 'Ubuntu', 'weight': 'normal', 'slant': 'roman', 'size': 11, 'overstrike': 0, 'underline': 0}
_defaultFont.actual    =  {'family': 'Ubuntu', 'weight': 'normal', 'slant': 'roman', 'size': 11, 'overstrike': 0, 'underline': 0}
Font.configure         =  {'family': 'Ubuntu', 'weight': 'normal', 'slant': 'roman', 'size': 11, 'overstrike': 0, 'underline': 0}
Font.actual            =  {'family': 'Ubuntu', 'weight': 'normal', 'slant': 'roman', 'size': 11, 'overstrike': 0, 'underline': 0}
FontBold.configure     =  {'family': 'Ubuntu', 'weight': 'bold', 'slant': 'roman', 'size': 11, 'overstrike': 0, 'underline': 0}
FontBold.actual        =  {'family': 'Ubuntu', 'weight': 'bold', 'slant': 'roman', 'size': 11, 'overstrike': 0, 'underline': 0}

URW Gothic L in tkFont.families()
_defaultFont.configure =  {'slant': 'roman', 'family': 'URW Gothic L', 'underline': 0, 'size': 11, 'overstrike': 0, 'weight': 'normal'}
_defaultFont.actual    =  {'slant': 'roman', 'family': 'URW Gothic L', 'underline': 0, 'size': 11, 'overstrike': 0, 'weight': 'normal'}
Font.configure         =  {'slant': 'roman', 'family': 'URW Gothic L', 'underline': 0, 'size': 11, 'overstrike': 0, 'weight': 'normal'}
Font.actual            =  {'slant': 'roman', 'family': 'URW Gothic L', 'underline': 0, 'size': 11, 'overstrike': 0, 'weight': 'normal'}
FontBold.configure     =  {'slant': 'roman', 'family': 'URW Gothic L', 'underline': 0, 'size': 11, 'overstrike': 0, 'weight': 'bold'}
FontBold.actual        =  {'slant': 'roman', 'family': 'URW Gothic L', 'underline': 0, 'size': 11, 'overstrike': 0, 'weight': 'bold'}

Revised Test Script:

import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkFont

class App(ttk.Frame):

    def __init__(self, parent):
        ttk.Frame.__init__(self, parent)
        self.parent = parent
        fontFamilies = tkFont.families()
        print('tkFont.families() = ', fontFamilies)

        testfont = 'URW Gothic L' #change string to the font you want to use
        for font in fontFamilies:
            if testfont == font:
                print('n{} in tkFont.families()'.format(testfont))
                break

        self._defaultFont=tkFont.Font(family=testfont, size='11',
                                      weight='normal')        
        self.setFont()


    def setFont(self):
        """Customise Font styles""" 
    self.Font = self._defaultFont.copy()
    self.FontBold = self._defaultFont.copy()
    self.FontBold.config(weight='bold')
    print('n_defaultFont.configure = ', self._defaultFont.configure())
    print('_defaultFont.actual    = ', self._defaultFont.actual())
    print('Font.configure         = ', self.Font.configure())
    print('Font.actual            = ', self.Font.actual())
    print('FontBold.configure     = ', self.FontBold.configure())
    print('FontBold.actual        = ', self.FontBold.actual())


if __name__ == '__main__':
    root = tk.Tk()
    root.title('Test')
    root.geometry('200x100')
    app = App(root)
    app.pack(expand=1, fill='both')
    root.mainloop()

Leave a Reply

Your email address will not be published. Required fields are marked *