Is there a way to toggle/hide/show tkinter buttons - python

I am making blackjack in tkinter and instead of placing buttons over the existing buttons i want to toggle them when, say a new game.

There are many ways of doing this. One option (I think he simplest one) is to get your buttons in a frame that you can pack and unpack using pack and packing_forget. In this case you need another frame where your button frame is the only packed widget, so the buttons will appear in the same place when you pack them again. You can also resize the frame so things on it will become invisible when it becomes really small. Another option is to use a canvas where your buttons are canvas objects. You can them move or hide them as you want.

In addition to #Flavio Moraes answer you can use grid_remove() method to save a widget before remove it (if you don't need it anymore you can also destroy() the widget:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry("300x80")
root.title('Toogle button')
def crea():
""" restore button """
btn.grid(column=0, row=0, sticky='nsew')
def remo():
"""remove button"""
btn.grid_remove()
def btn_off():
btn.after(1500, remo)
btn.after(3000, crea)
btn = ttk.Button(root, text='Hide', command=btn_off)
btn.grid(column=0, row=0, rowspan=2, sticky='nsew')
root.columnconfigure(0, weight=3)
root.mainloop()

Related

Python: How to destroy buttons with the same name (tkinter)

The loop in the program created a number of buttons with the same name. How to destroy them?
For example:
for i in range(5):
global btn
btn=Button(text=name,command=startfile_)
btn.place(x=5,y=5)
def destroy_it():
btn.destroy()#Its destroying only 1
destroy_btn(text=name,command=destroy_it)
It strongly depends on your use case. By the way, I suggest you to ensure your code is reproducible as-is, so that we can adapt our answers to your specific case.
Here some ways to address your problem.
1. save all buttons in a list and iterate over the list to delete them
pros: very easy to use and understand
cons: you need to pass your list along the code to work on it
import tkinter as tk
root = tk.Tk()
buttons = []
for i in range(5):
btn = tk.Button(root, text=f'test{i}', command=None) # TODO fill with your command
btn.pack()
buttons.append(btn)
def destroy_it(buttons):
# You must know the list of buttons to destroy
for btn in buttons.copy():
btn.destroy()
buttons.remove(btn) # also delete buttons from the list
tk.Button(root, text="Destroy all buttons", command=lambda: destroy_it(buttons)).pack()
root.mainloop()
2. destroy all widgets that satisfy a specific rule (i.e. Buttons with a specific text)
pros: you have a large flexibility on the widgets you are going to delete
cons: you may accidentally delete widgets, you must correctly deal with it
import tkinter as tk
root = tk.Tk()
for i in range(5):
btn = tk.Button(root, text=f'test{i}', command=None) # TODO fill with your command
btn.pack()
def destroy_it():
# Iterate over any widget and destroy it if it is a button with text "test....."
for child in root.winfo_children():
if isinstance(child, tk.Button) and child['text'].startswith('test'):
child.destroy()
tk.Button(root, text="Destroy all buttons", command=destroy_it).pack()
root.mainloop()
3. Give your buttons a name and address them by name
pros: easy to include in the code and to understand
cons: you must remember the names you used and you may get KeyError
import tkinter as tk
root = tk.Tk()
for i in range(5):
btn = tk.Button(root, name=f'btn{i}', text=f'test{i}', command=None) # TODO fill with your command
btn.pack()
def destroy_it():
# Get each button by its name
for i in range(5):
btn = root.nametowidget(f'.btn{i}')
btn.destroy()
tk.Button(root, text="Destroy all buttons", command=destroy_it).pack()
root.mainloop()
There are probably many other ways to achieve it, such as associating an "autodestroy" method to each button that is triggered by command, or include your buttons in a Frame and destroy the frame at once... But you may start from the examples above

How to delete buttons on Tkinter?

I really need to be able to delete a button onscreen into a label. All I need to do is remove the button, and put the label in place of it. However, I do not know how to remove buttons.
I am running Windows 10, Python 3.9.2.
Are you looking for something like this?:
import tkinter as tk
def remove_button():
global label
# Get the grid parameters passed in button when it was created
button_grid_info = button.grid_info()
button.grid_forget()
label = tk.Label(button_grid_info["in"], text="This is a Label widget")
# Put the label exactly where the button was:
label.grid(**button_grid_info)
root = tk.Tk()
button = tk.Button(root, text="Click me", command=remove_button)
button.grid(row=1, column=1)
root.mainloop()
grid_forget removes the widget without destroying it. If you used <button>.pack, use pack_forget. If you used <button>.place, use place_forget.

Tkinter: Combining a Scrollbar with a Canvas

I know this isn't the first time a question like this is asked, but even after like 2 hours of browsing the Internet I can't get it to work:
So I'm trying to create a Tkinter-Frame, that contains several Buttons (As Example I took 30). But Because I don't have enough space in my program, I need to add an Scrollbar next to it, so that one can scroll through the Buttons.
The Problems I had where, that the inner "moving part" of the bar was as big as the whole scrollbar and couldn't be moved, which I kinda solved by using scollregion=(0,0,1000,1000), but even then the moving of the bar had no effect on the canvas whatsoever.
Here Is the corresponding code that I extracted out of my program:
import Tkinter as tk
root = tk.Tk()
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=50)
root.columnconfigure(1, weight=1)
root.minsize(300,400)
root.maxsize(300,400)
#Buttons
buttonFrame = tk.Canvas(root, bg='#bbb')
buttonFrame.grid(row=0, column=0, sticky=tk.N+tk.E+tk.S+tk.W)
buttonFrame.columnconfigure(0, weight=1)
scroll = tk.Scrollbar(root, command=buttonFrame.yview)
scroll.grid(row=0, column=1, sticky=tk.N+tk.E+tk.S+tk.W)
buttonFrame.configure(yscrollcommand=scroll.set)
for i in range(30):
tk.Button(buttonFrame, text=str(i+1)).grid(row=i, column=0, sticky=tk.N+tk.E+tk.S+tk.W)
root.mainloop()
As you (hopefully) see, the slider can't even be moved nor does it change anything on the canvas, even if I squeeze a scrollregion=(bla) somewhere in there.
2 Questions:
a.) What do I need to add (or remove), so that I can scroll through the list of Buttons
b.) Does the fix from a. still work when I make the Scrollbar a child of the buttonFrame instead of the root?
To add widgets to a Canvas you have to use the create_window method, not grid(). Then you have to update the canvas before setting the scrollregion.
for i in range(30):
btn = tk.Button(buttonFrame, text=str(i+1))
buttonFrame.create_window((100,i*50), window=btn)
root.update()
buttonFrame.config(scrollregion=buttonFrame.bbox("all"))
If you try that I suspect it's not what you were looking for, since the create_window method requires absolute positioning (you can't use grid or pack). That's why most people put a Frame in the Canvas, and add their widgets to that instead. Many people have abstracted this faux Frame that is actually a Frame in a Canvas in another Frame, including me.

How to place a tk button on the top of a frame?

I am trying to create a program that has a list of functions on the left. I would like to have the menu start at the top and expand downwards as more buttons are added. However no matter what I do I always get the button in the middle of its widget:
I am trying to create the button as follows:
button = Button ( button_frame,
font = ('Open sans','10','bold'),
text='Button',
bg='#444444',
fg='#cccccc',
relief=FLAT,
borderwidth=0,
highlightthickness = 1,
highlightbackground="#222222",
command=quit)
button.grid(row=0, column=0, sticky=N)
The simplest solution in this case is to use pack. Pack is specifically designed to arrange widgets either vertically or horizontally.
button.pack(side="top", fill="x")
If you insist on using grid, what you need to do make sure that the grid row below the last button has a non-zero weight, and all of the other rows should have a weight of zero. That will cause tkinter to always allocate any extra space below the last button.

Python - is there any way to expand the resolution ratio for existing Tkinter Tk() and Toplevel() widget? (zooming-in)

I just made an app using python and tkinter widgets.
There are Labels, Frames, Buttons, etc in the Tk and Toplevel widgets.
However, it includes thousands of codes and its really annoying to resize every widgets when I support multiple resolutions.
Is there any way to expand the resolution ratio for existing Tkinter Tk() and Toplevel() widget and their child widgets? (zooming-in)
If not, what would be the best approach to support multiple resolutions of a python app with the same ratio?
Any help would be much appreciated, sorry for bad English.
Yes, this is possible however it depends on the geometry manager you have used in your program.
For the .pack() method (which is arguably the simplest geometry method for "intelligent" GUI designs) you can use a range of attributes on when you declare .pack() on the widget. These attributes include (but are not limited to) fill, expand, anchor, padx, pady, etc.
The below shows an example of a set of three buttons which will automatically expand to fit the window if it changes or is initialised to a different size than was used during development.
from tkinter import *
root = Tk()
btn1 = Button(root, text="btn1")
btn2 = Button(root, text="btn2")
btn3 = Button(root, text="btn3")
btn1.pack(fill="both", expand=True)
btn2.pack(fill="both", expand=True)
btn3.pack(fill="both", expand=True)
root.mainloop()
For the .grid() method you will need to make use of the functions Grid.columnconfigure() and Grid.rowconfigure. Both of these have the attribute weight which determines which rows and columns should be given priority for assignment of extra space if more becomes available in the window. Setting all rows and columns to have a weight of 1 means they will all be given space equally. You will also need to use the sticky attribute when declaring .grid() on the widgets.
The below shows an example of a set of three buttons which will automatically expand to fit the window if it changes or is initialised to a different size than was used during development.
from tkinter import *
root = Tk()
for column in range(3):
Grid.columnconfigure(root, column, weight=1)
for row in range(1):
Grid.rowconfigure(root, row, weight=1)
btn1 = Button(root, text="btn1")
btn2 = Button(root, text="btn2")
btn3 = Button(root, text="btn3")
btn1.grid(column=0, row=0, sticky=N+S+E+W)
btn2.grid(column=1, row=0, sticky=N+S+E+W)
btn3.grid(column=2, row=0, sticky=N+S+E+W)
root.mainloop()
Using .place() would be a lot more difficult, you would need to have a function setup which would trigger on every window resize event which would calculate the size that the buttons need to expand to.
This would look something like the below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.button = Button(self.root, text="Button")
self.button.place(relx=0.5, rely=0.5, anchor="center")
self.root.bind("<Configure>", self.resize)
def resize(self, *args):
self.button.configure(width=self.root.winfo_width(), height=self.root.winfo_height())
root = Tk()
App(root)
root.mainloop()
Subjectively speaking, .pack() tends to be easier, however this all comes down to how much effort you're willing to put in to implement this with your current program.
Can't comment so I add a short tip to the detailed Ethan answer. You can design most of the GUIs in tkinter with either pack, grid or a combination of both (by placing frames on a window with one of them, and using either grid or pack inside of each frame, to "place" the widgets). You can tune the configurations for proper location and size when the window resizes. Keep placer use for special cases (like overlaying some widget on the top of others)

Categories

Resources