Change font size without messing with Tkinter button size

16,366

Solution 1

The width of the button is defined in units of character width. In your case the button is defined to be 17 characters wide. So changing the character width by (ie changing the font size) changes the width of the button. AFAIK, the only way around that is to put the button into a Frame, because a Frame can define it's size in pixels. Here's a new kind of Button that does exactly that:

import Tkinter as tk

class Warspyking(tk.Frame):
    '''A button that has it's width and height set in pixels'''
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master)
        self.rowconfigure(0, minsize=kwargs.pop('height', None))
        self.columnconfigure(0, minsize=kwargs.pop('width', None))
        self.btn = tk.Button(self, **kwargs)
        self.btn.grid(row=0, column=0, sticky="nsew")
        self.config = self.btn.config

#example usage:
MyWindow = tk.Tk()
MyWindow.geometry("500x550")

from itertools import cycle
fonts = cycle((('Helvetica', '11'),('Helvetica', '15'),('Helvetica', '20')))
def chg():
    button.config(font=next(fonts))

button = Warspyking(MyWindow,text="Click me!",width=200,height=100 ,font=next(fonts), command=chg)
button.grid(row=1, column=1)

MyWindow.mainloop()

EDIT: Based on what I learned from Bryan Oakley, here's a much neater implementation:

class Warspyking(tk.Button):
    def __init__(self, master=None, **kwargs):
        self.img = tk.PhotoImage()
        tk.Button.__init__(self, master, image=self.img, compound='center', **kwargs)

I should also add that I very much agree with Bryan: Using this is probably a sign that you are doing something wrong. You should let tkinter handle sizing.

Solution 2

Typically, when you give a button a width, that width is measured in characters (ie: width=1 means the width of one average sized character). However, if the button has an image then the width specifies a size in pixels.  

A button can contain both an image and text, so one strategy is to put a 1x1 pixel as an image so that you can specify the button size in pixels. When you do that and you change the font size, the button will not grow since it was given an absolute size.

Here is an example that illustrates the technique. Run the code, then click on "bigger" or "smaller" to see that the text changes size but the button does not.

import Tkinter as tk
import tkFont

def bigger():
    size = font.cget("size")
    font.configure(size=size+2)

def smaller():
    size = font.cget("size")
    size = max(2, size-2)
    font.configure(size=size)

root = tk.Tk()
font = tkFont.Font(family="Helvetica", size=12)

toolbar = tk.Frame(root)
container = tk.Frame(root)

toolbar.pack(side="top", fill="x")
container.pack(side="top", fill="both", expand=True)

bigger = tk.Button(toolbar, text="Bigger", command=bigger)
smaller = tk.Button(toolbar, text="Smaller", command=smaller)

bigger.pack(side="left")
smaller.pack(side="left")

pixel = tk.PhotoImage(width=1, height=1)
for row in range(3):
    container.grid_rowconfigure(row, weight=1)
    for column in range(3):
        container.grid_columnconfigure(column, weight=1)
        button = tk.Button(container, font=font, text="x",
            image=pixel, compound="center", width=20, height=20)
        button.grid(row=row, column=column)

root.mainloop()

All of that being said, there is almost never a time when this is a good idea. If the user wants a larger font, the whole UI should adapt. Tkinter is really good at making that happen, to the point where it all mostly works by default.

Solution 3

I found solution of this problem. I was trying to solve a similar problem: I want to put image on label. I set the image size equal to label size. When I have been trying to put it with command label.config(image=img) the label size grow. The image have the size I set to it, so it didn't cover label completely. I was using grid manager. All size were not entered in advanced but calculated by Tkinter. I was using grid_columnconfigure and grid_rowconfigure. The solution I found is to put this label with image (or button in Your case) to LabelFrame and set grid_propagate to False. Code example:

MyWindow = tk.Tk()
MyWindow.geometry("500x550")

#create LabelFrame (200x200)
label = tk.LabelFrame(MyWindow, width=200, height=200)

#grid manager to set label localization
labelk.grid(row=0, column=0)

#label row and column configure: first argument is col or row id
label.grid_rowconfigure(0, weight=1)
label.grid_columnconfigure(0, weight=1)

#cancel propagation
label.grid_propagate(False)

#Create button and set it localization. You can change it font without changing size of button, but if You set too big not whole will be visible
button = t.Button(label, text="Hello!", font=('Helvetica', '20'))

#Use sticky to button took up the whole label area
button.grid(row=0, column=0, sticky='nesw')

MyWindow.mainloop()

Result for font size 40 and 20:

Image for font size 40 Image for font size 20

Example for creating button with dynamic size by grid manager:

MyWindow = tk.Tk()
MyWindow.geometry("500x550")

#Divide frame on 3x3 regions
for col in range(3):
    MyWindow.grid_columnconfigure(col, weight=1)
for row in range(3):
    MyWindow.grid_rowconfigure(row, weight=1)

label = tk.LabelFrame(MyWindow)

#Put label in the middle
label.grid(row=1, column=1, sticky='nesw')
label.grid_propagate(False)
label.grid_rowconfigure(0, weight=1)
label.grid_columnconfigure(0, weight=1)
button = tk.Button(label, text="Hello!", font=('Helvetica', '30'))
button.grid(row=0, column=0, sticky='nesw')
MyWindow.mainloop()

It is late answer, but maybe it will help someone.

Share:
16,366
warspyking
Author by

warspyking

Hi, I really enjoy programming on the site www.roblox.com although there are many 12- kids on there, it's a great place to develop games. I'm also trying to get used to JavaScript. I know a bit of Batch. I just joined this site in order to get some help, but maybe I'll eventually be good enough to help others too.

Updated on June 04, 2022

Comments

  • warspyking
    warspyking almost 2 years

    I am having trouble changing the font size of a button in Tkinter, when I attempt to do it the button also expands/contracts based on the size of the text. Is there a way I can alter the text size with the button's size anchored in place?

    I ran across this while designing a tic-tac-toe application, however to save you the trouble, here is a very minimal example of the problem in practice:

    import Tkinter as tk
    
    MyWindow = tk.Tk()
    MyWindow.geometry("500x550")
    
    
    button = tk.Button(MyWindow,text="Hello!",width=17,height=10,font=('Helvetica', '20'))
    button.grid(row=1, column=1)
    
    MyWindow.mainloop()
    

    The most important part here is font=('Helvetica', '15') or more specifically, the number 15. If you change that number and run this again, not only will the text be bigger/smaller, but so will the button! How do I get around this?

    It's probably a really simple problem. I've just gotten started with Tkinter. Thanks in advance for any help I receive!

  • warspyking
    warspyking about 7 years
    Your class declaration and stuff is scaring me. I'm only starting out in both python and tkinter haha.
  • Novel
    Novel about 7 years
    GUIs rely heavily on classes, as do many other things in python. Jump in.
  • Xiang
    Xiang over 3 years
    This is so brilliant and easy to be understood! As a mark, this answer is on python 2.x, in order to test on python 3.x, please edit Tkinter as tkinter in the first line, and import tkFont to from tkinter import font in the second line, and tkFont.Font( to font.Font( in the following, then you are all set!
  • sodmzs1
    sodmzs1 about 3 years
    stackoverflow.com/q/67241484/14715170 Can you please help me for this ?