How do I get the label position of entry widgets to update properly when changing the font size?

Viewed 647

I am creating a tkinter ttk GUI and I made some radio buttons that select the GUI size. This changes, among other things, the global font size for the whole application.

I made a quick script to demonstrate it.

    import tkinter as tk
    from tkinter import ttk
    import tkinter.font

    UI_SIZES = ["Tiny", "Small", "Normal", "Large", "Huge"]

    TINY_FONT_SIZE = 8
    SMALL_FONT_SIZE = 12
    NORMAL_FONT_SIZE = 16
    LARGE_FONT_SIZE = 18
    HUGE_FONT_SIZE = 22

    root = tk.Tk()

    ui_size = tk.StringVar(root, "Normal")
    entry_text = tk.StringVar(root, "Entry Text")


    def text_size_callback(*_args):
        """change the program font size when the font_size variable changes"""
        font = tkinter.font.nametofont("TkDefaultFont")
        selected_size = ui_size.get()
        if selected_size == "Tiny":
            font.configure(size=TINY_FONT_SIZE)
        elif selected_size == "Small":
            font.configure(size=SMALL_FONT_SIZE)
        elif selected_size == "Normal":
            font.configure(size=NORMAL_FONT_SIZE)
        elif selected_size == "Large":
            font.configure(size=LARGE_FONT_SIZE)
        elif selected_size == "Huge":
            font.configure(size=HUGE_FONT_SIZE)
        root.option_add("*Font", font)


    ui_size.trace('w', text_size_callback)
    ui_size.set("Normal")

    ui_radio_group = ttk.Frame(root)
    ui_size_radios = []
    for sizeval in UI_SIZES:
        ui_size_radios.append(ttk.Radiobutton(
            ui_radio_group,
            text=sizeval,
            variable=ui_size,
            value=sizeval
        ))
    text_entry = ttk.Entry(ui_radio_group, textvariable=entry_text)

    i = 0
    for sizeradio in ui_size_radios:
        sizeradio.grid(row=i, column=0, sticky=tk.W)
        i += 1
    text_entry.grid(row=2, column=1)
    ui_radio_group.pack()

    root.mainloop()

This works well, except for the labels inside entries (and comboboxes). The text itself resizes but the label does not, until the text is edited. This makes the text entries look weird.

Here it is on startup

normal text setting, nothing weird

Then messed up after clicking tiny

text is tiny, but sizing is off

Then fixed after I hit backspace

sizing is fixed after editing text

How can I work around this? Or, how can I do this more correctly so it works without workarounds? I'm on Windows 10, using Python 3.6.3 64-bit, if it helps.

EDIT: I made an example for you all to poke with

1 Answers

After some tests, it seems to happen only with ttk.Entry, not tk.Entry. There are at least two workarounds:

  1. to insert then delete text in the entry after the font size changed
  2. to reconfigure the font of the entry after the font size changed

Here is an example:

import tkinter as tk
from tkinter import ttk
import tkinter.font as tkfont

def change_fontsize():
    font.configure(size=30)

def change_fontsize_1():
    font.configure(size=20)
    # workaround 1
    entry.insert(0, ' ')
    entry.delete(0)

def change_fontsize_2():
    font.configure(size=5)
    # workaround 2
    entry.configure(font=font)


root = tk.Tk()

font = tkfont.Font(root)
entry = ttk.Entry(root, font=font)
entry.insert(0, 'Entry Text')
entry.pack()

ttk.Button(root, text='Change size, no workaround', command=change_fontsize).pack()
ttk.Button(root, text='Change size, workaround 1', command=change_fontsize_1).pack()
ttk.Button(root, text='Change size, workaround 2', command=change_fontsize_2).pack()

root.mainloop()
Related