Tkinter buttons resize, when longer text is displayed on them - python

I am making a program that can launch files and programs like a Stream Deck. After selecting the file I want to assign to a button, the button resizes due to the filename being wider than the placeholder text "Add".
I couldn't find any solutions to this problem anywhere.
I am desperate on finding the solution as this is pretty much the final thing i need to fix to make the program pre-Alpha.
Thank you in advace.
How to reproduce this issue:
import tkinter
from tkinter import *
root = Tk()
button1 = Button(root, text="Add", padx=10, pady=10)
button2 = Button(root, text="More Text", padx=10, pady=10)
button1.grid(row=0, column=0)
button2.grid(row=1, column=0)
root.mainloop()

A widget appears in its natural size, if not defined otherwise. The natural size is usally the smallest amount of pixels needed to display the widget in width and height. You can change that appearance by defining a width for your Button, even better have a constant if you use the same value for a couple of Buttons. Example:
import tkinter as tk
#from tkinter import *
#dont't use wildcard imports
#only import your modul once
BUTTONWIDTH = 10 #Constant
root = tk.Tk()
button1 = tk.Button(root, text="Add", width=BUTTONWIDTH) #use width
button2 = tk.Button(root, text="More Text", width=BUTTONWIDTH)
button1.grid(row=0, column=0)
button2.grid(row=1, column=0)
root.mainloop()

Related

How do I change the background colour and foreground colour using a button in Tkinter?

So what I am trying to do is create a theme picker for my application
For example, the user could click on a the Green/Black button and it would change every widgets background to Black and it would change every widgets foreground to Green. Then they could click the Red/White button and it would do the same thing but change every widgets background to White and every widgets foreground to Red.
# Imports the tkinter library.
from tkinter import *
from tkmacosx import Button
# Pillow is a improved verison of PIL, stands for Python Image Library. Allows you to import image types like .jpg and .png
from PIL import ImageTk,Image
# Imports messagebox module from tkinter library.
from tkinter import messagebox
# Declare global variables
global selectedBackground
global selectedForeground
selectedBackground="white"
selectedForeground="red"
# Tk() helps to display the root window and manages all the other components of the tkinter application and assigns it to root.
root = Tk()
root.eval("tk::PlaceWindow . center")
root.configure(bg=selectedBackground)
cipherButton = Button(root, text=" Cipher ", padx=40, pady=20, command=openCipher, borderwidth=0, fg="#22fd35", bg="black", highlightbackground="#22fd35").grid(row=1, column=0)
decipherButton = Button(root, text="Decipher", padx=40, pady=20, command=openDecipher, borderwidth=0, fg="#22fd35", bg="black", highlightbackground="#22fd35").grid(row=1, column=1)
spacer1 = Label(root, text=" ", padx=10, pady=1, background="black").grid(row=4, column=1)
quitButton = Button(root, text="Exit d3cryptt", padx=10, pady=5, command=root.quit, borderwidth=0, fg="#22fd35", bg="black", highlightbackground="#22fd35").grid(row=5, column=0, columnspan=2)
spacer2 = Label(root, text=" ", padx=10, pady=1, background=selectedBackground).grid(row=6, column=1)
changecolour = Button(root, text="change colour", padx=1, pady=5, background="black", command=changeColour).grid(row=7, column=0)
#Enter the event main loop
root.mainloop()
I have tried using a function. Something like this.
def changeColour():
selectedBackground="black"
selectedForeground="#22fd35"
changecolour = Button(root, text="change colour", padx=1, pady=5, background="black", command=changeColour).grid(row=7, column=0)
But that doesn't work. I think a function is the right way to go, but I may be wrong.
I will also need this 'theme' to carry on to any other windows that are created from this window. I think I can just do that by using lambda in the command section of the widget.
Widget.config(bg=color) is what your looking for.
Here is a small example of a theme-changing app:
from tkinter import *
from tkmacosx import Button
root = Tk()
def changethemetoblack():
root.config(bg="#000000")
def changethemetowhite():
root.config(bg="#ffffff")
def changethemetored():
root.config(bg="#ff0000")
themeblackbutton = Button(root, text="Change Theme To Black", command=changethemetoblack, bg="#000000", fg="#ffffff")
themewhitebutton = Button(root, text="Change Theme To White", command=changethemetowhite)
themeredbutton = Button(root, text="Change Theme To Red", command=changethemetored, bg="#ff0000", fg="#ffffff")
themeblackbutton.pack()
themewhitebutton.pack()
themeredbutton.pack()
root.mainloop()
I'll try to change it so that it applies for your code.
However, your provided script does not seem to be a working one. I assume this is because it is only a snippet of the real one. I'm not pushing for your entire code, but edit it so we can run it. Ex. openCipher method is causing errors as we have not defined it.

Is there a way to refresh a tkinter frame in which widgets have been reconfigured?

I'd like to allow the user to configure the appearance of the app (background color, font, font size and color, button color, etc). I've got all of the default settings saved into a config file, which the program interprets and saves into variables within a class for easy access between functions. Now, I can save the changes the user made into the config file and those changes will be reflected when the user closes and reopens the app, but I'd like the changes to be instantaneous, so I tried something like this:
import tkinter as tk
class SetColor:
def __init__(self, color):
self.color = 'green'
current = SetColor('green')
root = tk.Tk()
lbl_color = tk.Label(root, text='Choose button color')
lbl_color.grid(row=0, column=0, pady=5, padx=2)
btn_red = tk.Button(root, text='Red', bg=current.color, command=lambda:update_color('red'))
btn_red.grid(row=0, column=1, pady=5, padx=2)
btn_green = tk.Button(root, text='Green', bg=current.color, command=lambda:update_color('green'))
btn_green.grid(row=0, column=2, pady=5, padx=2)
btn_blue = tk.Button(root, text='Blue', bg=current.color, command=lambda:update_color('blue'))
btn_blue.grid(row=0, column=3, pady=5, padx=2)
def update_color(color):
current.color = color
#THIS is where I'm wondering if there's a way to refresh without individually updating each widget as I've done below
btn_red.config(bg=current.color)
btn_green.config(bg=current.color)
btn_blue.config(bg=current.color)
root.mainloop()
This does work, but in my actual app there are a loooooot more widgets that would need updating than in this^ example. So I have a feeling I'm missing something or going about this in the wrong way. Any help greatly appreciated :)
Your best bet is to store the buttons in a list and loop over that list. This way you can seperate different buttons. But if you are sure you want to change the color of every singlle button you can do: for widget in root.winfo_children():
if isinstance(widget, tk.Button):
widget.config(bg=current.color)
#Maarten's answer is perfect for the tkinter button.
There is another option using ttk.Button can be used in such scenario
create the button object with a custom style
btn_green = ttk.Button(root, text='Green', style="color.TButton", command=lambda: update_color('green'))
create style object
style = ttk.Style()
style.theme_use("default")
set the style
style.configure('color.TButton', background=current.color)
# Activate is when you mouse over the button.
style.map('color.TButton', background=[('active', current.color)])
Full example:
import tkinter as tk
from tkinter import ttk
class SetColor:
def __init__(self, color):
self.color = 'green'
def update_color(color):
current.color = color
# Let's set the style
# naming that style variable as color.TButton
# NOTE: .TButton is important, you can add any other pretix though
style.configure('color.TButton', background=current.color)
# Activate is when you mouse over the button.
style.map('color.TButton', background=[('active', current.color)])
current = SetColor('green')
root = tk.Tk()
# Create style Object
style = ttk.Style()
# Setting theme to default (built in themes can be found https://wiki.tcl-lang.org/page/List+of+ttk+Themes)
style.theme_use("default")
lbl_color = ttk.Label(root, text='Choose button color')
lbl_color.grid(row=0, column=0, pady=5, padx=2)
btn_red = ttk.Button(root, text='Red', style="color.TButton", command=lambda: update_color('red'))
btn_red.grid(row=0, column=1, pady=5, padx=2)
btn_green = ttk.Button(root, text='Green', style="color.TButton", command=lambda: update_color('green'))
btn_green.grid(row=0, column=2, pady=5, padx=2)
btn_blue = ttk.Button(root, text='Blue', style="color.TButton", command=lambda: update_color('blue'))
btn_blue.grid(row=0, column=3, pady=5, padx=2)
update_color(current.color)
root.mainloop()
There are a lot more options with ttk style to play around.
Have a look at
Python ttk Style
ttk Themes
I did this and works perfectly. I hope it works for you.
def groups1(): # This function is to place the widgets for Groups.
# Clean widgets immediately after you call the button inside
# the button function.
for widget in frameleft.winfo_children():
widget.destroy()
groups = tkinter.ttk.Label(frameleft, text='Grupos', font=
('URW Gothic', 20))
groups.place(x=20, y=30)

How to switch screens using tkinter?

I want to switch the screen using the tkinter module.
Is it possible to switch screens without frame?
This is the code I did.
#frame1.py
from tkinter import *
root=Tk()
root.title('page1')
def goto_p2():
root.destroy()
import frame2
Button(root, text="goto page1", command=goto_p2).pack()
mainloop()
.
#frame2.py
from tkinter import *
root=Tk()
root.title('page1')
def goto_p1():
root.destroy()
import frame1
Button(root, text="goto page2", command=goto_p1).pack()
mainloop()
Here you go, example code how it can be done.
import tkinter as tk
from tkinter import Button,Label
def clear_frame():
for widget in window.winfo_children():
widget.destroy()
def screen_two():
clear_frame()
button_2 = Button(window, text="Go to screen one", command=lambda: screen_one())
button_2.pack(pady=10)
label_2 = Label(window, text="Label on window two")
label_2.pack(pady=10)
def screen_one():
clear_frame()
button_1 = Button(window, text="Go to screen two", command=lambda: screen_two())
button_1.pack(pady=10)
label_1 = Label(window, text="Label on window one")
label_1.pack(pady=10)
window = tk.Tk()
window.title("Test")
window.geometry('250x100')
screen_one()
window.mainloop()
[EDITED]
Function clear_frame() will destroy every widget in frame ( basicly it will clear screen). Besides clear_frame(), there are two more functions, screen_two() and screen_one(). Both of those functions are doing the same thing, calling clear_frame() function, and displaying one button and one label. (Only difference is text inside label and text inside button).
When code runs, it will create window with geometry of 250x100. It will call screen_one() function and run mainloop().

Python - Tkinter button size changes when chaging the size of its font

I want to change the fontsize in a Tkinter button. But the button size changes along with it.
I even tried to limit the height and width of the button but nothing seems to work,
This is the code:
from tkinter import *
root = Tk()
root.geometry("500x300")
root.resizable(False, False)
button = Button(root, text="Ihsan", bg="Black", fg="white",
activeforeground="white", activebackground="grey", width=15, height=3,
font=("ariel", "43"))
button.place(x=350, y=20)
root.mainloop()
I get a window with a huge button. Please help
Width and height of the button in letters (if displaying text) or pixels (if displaying an image).
Here's one way to go, by using size of image to limit size of button when font size changed. Not sure if it is best one.
import tkinter as tk
from PIL import Image, ImageTk
def new_font():
global setting
setting = 1 - setting
font = f"ariel {32 if setting else 16}"
button.configure(font=font)
setting = 0
root = tk.Tk()
im = Image.new("RGB", (200, 200))
photo = ImageTk.PhotoImage(im)
root.geometry("300x300")
root.resizable(False, False)
button = tk.Button(
root,
text="Ihsan",
bg="Black",
fg="white",
activeforeground="white",
activebackground="grey",
width=200,
height=200,
font=("ariel", "16"),
image=photo,
compound='center')
button.place(x=30, y=20)
size = tk.Button(root, text="Size", command=new_font)
size.place(x=30, y=250)
root.mainloop()
The width and height is what is giving you the large button. Height in this case means 3 fontsize high.
Default is that it only enclose the text.
Margins you provide with pad, either in the widget itself, or when you pack it.
Using place is a bit uncommon and has its specific usecases, it does not look like you need it. The pack geometry manager can be used instead.
from tkinter import *
root = Tk()
root.geometry("500x300")
root.resizable(False, False)
button = Button(root, text="Ihsan", bg="Black", fg="white",
activeforeground="white", activebackground="grey",# width=15, height=3,
font=("ariel", "43"))
#button.place(x=350, y=20)
button.pack(side=RIGHT)
root.mainloop()

Python GUI using Tkinter - widget positioning is not proper

I am creating a GUI wherein I have a TextBox, and a few Buttons.
The problem is, the layout seems to be scrambled. And when I increase the row number so that I could space out well, it doesn't make any difference. I am not sure where I have made a mistake.
Code:
from Tkinter import *
from tkFileDialog import *
gui = Tk() #create an object
gui.title("xyz")
gui.geometry("900x300")
GuiLabel1 = Label(gui,text="hi everyone!!!!!!")
GuiLabel1.grid(row=0, column=0)
GuiLabel2 = Label(gui,text="File")
GuiLabel2.grid(row=1, column=0)
bar=Entry(gui)
bar.grid(row=1, column=1)
button1= Button(gui, text="Browse")
button1.grid(row=1, column=2)
button2= Button(gui, text="Process")
button2.grid(row=2, column=2)
button3= Button(gui, text="ABC")
button3.grid(row=3, column=0)
button4= Button(gui, text="ABC")
button4.grid(row=3, column=1)
button5= Button(gui, text="ABC")
button5.grid(row=3, column=2)
button6= Button(gui, text="ABC")
button6.grid(row=3, column=3)
button7= Button(gui, text="ABC")
button7.grid(row=3, column=4)
gui.mainloop()
See the below image for the screenshot of the GUI:
The widgets are appearing pretty much exactly as you have told them to appear. The problem is, you are relying on a lot of defaults for positioning which is probably why they don't appear on the screen the way you think they should.
When you use grid, you should almost always include the sticky option to define how a widget will fill the cell it has been placed in. You often need to augment that with padding. Finally, you should generally give at least one row and one column a non-zero weight.
Also, remember that you're creating a grid. If you have a very wide item in the grid, that will cause the entire column to be wide. If you place smaller items in subsequent rows they will by default be centered in that column.
You can use the "width=" parameter to evenly space widgets see http://effbot.org/tkinterbook/button.htm and row/columnconfigure to show empty rows/columns
from Tkinter import *
gui = Tk() #create an object
gui.title("xyz")
gui.geometry("900x300")
GuiLabel1 = Label(gui,text="hi everyone!!!!!!")
GuiLabel1.grid(row=0, column=0)
GuiLabel2 = Label(gui,text="File")
GuiLabel2.grid(row=3, column=0)
bar=Entry(gui)
bar.grid(row=1, column=1)
button1= Button(gui, text="Browse", width=test_width)
button1.grid(row=1, column=2)
button2= Button(gui, text="Process", width=test_width)
button2.grid(row=2, column=2)
test_width=20
for ctr in range(4):
button= Button(gui, text="ABC"+str(ctr+1), width=test_width)
button.grid(row=3, column=ctr)
## empty column between
gui.columnconfigure(4, minsize=50)
button7= Button(gui, text="ABC")
button7.grid(row=3, column=5)
gui.mainloop()
I Think you may be looking for a mixture of
pack()
grid()
and also the element Frame which allows you to 'group' elements in it that count as one element.
I can not fix your code but I suggest you look into those and have a try.

Categories