Can you make a button create itself? - python

So I want to make a button, that when I click on it, it gets deleted and another one appears, and this can be done an infinite amount of times
a=0
button1 = [1,2,3,4,5,6]
def ButtonClick():
global a
#Destroys the first button
button1[a].destroy()
a+a+1
#Making new button, that should do the same as the old button
button1[a] = tk.Button(text='hello',command=ButtonClick)
canvas1.create_window(250+a*50, 140, window=button1[a])
button1[a] = tk.Button(text='hello',command=ButtonClick)
canvas1.create_window(100, 140, window=button1[a])
As you can see the new button is also using command=ButtonClick so when I press on the created button it should do the same as the old one, but it doesn't and I am not sure why because when I change the command on the new button it says error so it is somehow refereeing to the def ButtonClick. But nothing happens when I press the new button. Can anyone help me out?

In order to destroy and rebuild a button, you will have to make it global. Here is a working example of a button that can be destroyed by its own function, only to be created again:
def ButtonClick():
global root
root.b.destroy()
root.b = Button(root,text="Hello",command=ButtonClick)
root.c.create_window(250+root.button1[root.bnumber]*50,140,window=root.b)
if root.bnumber<len(root.button1)-1:root.bnumber+=1
global root
root = Tk()
root.geometry('800x600')
root.resizable(0, 0)
root.c = Canvas(root,bg="#ffffff",width=800,height=600)
root.c.grid(row=0,column=0,sticky=W+S+E+N)
root.b = Button(root,text="Hello",command=ButtonClick)
root.c.create_window(100,140,window=root.b)
root.button1 = [1,2,3,4,5]
root.bnumber = 0
I made your button1 list and the current index (a) attributes of the tkinter widget, so it will be accessible as long as a function has access to the widget.
For this example, however, it would be easier to just move the window within the tkinter Canvas instead of removing and re-creating it.

Related

How to solve Tkinter Button Binding Causing errors on Mac?

I'm running a python (3.10.2) Tkinter GUI application on Mac OS X (12.1). The code is simply just binding the event to a Combobox to create a new page. This all works fine, but sometimes when you click on the back button, it gives me the error code "Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)". I've tried multiple different combinations with no success. I've tried using entry boxes from Tkinter rather than comboboxes from ttk. This still led to the same error. I then tired using a submit button instead of a combobox widget. I never experienced the error once, but in another one of my projects I wanted to bind the enter key and have a submit button which still gave me the same error. This allowed me to determine it had something to do with event binding. Next, I changed my python version (I changed from Python 3.10.0 to Python 3.9, then I upgraded to Python 3.10.2, both of these had no impact whatsoever). After this, I played around and realized that the button command itself wasn't even executing. The button was being clicked, and this killed the code for some reason. I'm really confused about the entire situation. What makes even less sense is that sometimes it occurs and sometimes it doesn't. Some runs allow it to be successful, while others don't. The other thing that I saw with my other project was that if you ran a different command that deleted the page and created a new one, the key bind would work. All of this confused me even more than I already was.
# Importing tkinter and ttk
from tkinter import *
from tkinter import ttk
# Creating the original page
def HomePage():
# Creating new tkinter window with the size of the screen as the height and width
# I made its name HomePage.root in order to make it accessible to other functions without the hassle of global and local
# I also made height and width like this in order to access them in the next function
HomePage.root = Tk()
height = HomePage.root.winfo_screenheight()
width = HomePage.root.winfo_screenwidth()
HomePage.root.geometry("%dx%d" % (width, height))
# Creating combobox and binding it to NewPage function
combo = ttk.Combobox(HomePage.root)
combo.pack()
combo.bind("<Return>",NewPage)
# Running Mainloop
HomePage.root.mainloop()
# NewPage function to go into new page
# Back command
def Back(control, func):
print("yay I entered the command")
control.destroy()
func()
def NewPage(e):
# Destroying old page and creating new page
HomePage.root.destroy()
NewPage.window = Tk()
height = NewPage.window.winfo_screenheight()
width = NewPage.window.winfo_screenwidth()
NewPage.window.geometry("%dx%d" % (width, height))
# Creating button
back = Button(NewPage.window, text="back", command=lambda: Back(NewPage.window, HomePage))
back.pack(side=BOTTOM)
# Running Mainloop
NewPage.window.mainloop()
# Running HomePage function
HomePage()
I worked with my code changing and modifying it until I had an idea. Rather than setting the button command, why not bind the button with a mouse click? I tried this, and it worked!
Here is my code:
# Importing tkinter and ttk
from tkinter import *
from tkinter import ttk
# Creating the original page
def HomePage():
# Creating new tkinter window with the size of the screen as the height and width
# I made its name HomePage.root in order to make it accessible to other functions without the hassle of global and local
# I also made height and width like this in order to access them in the next function
HomePage.root = Tk()
height = HomePage.root.winfo_screenheight()
width = HomePage.root.winfo_screenwidth()
HomePage.root.geometry("%dx%d" % (width, height))
# Creating combobox and binding it to NewPage function
combo = ttk.Combobox(HomePage.root)
combo.pack()
combo.bind("<Return>",NewPage)
# Running Mainloop
HomePage.root.mainloop()
# Back command
def Back(e,control, func):
print("yay I entered the command")
control.destroy()
func()
# NewPage function to go into new page
def NewPage(e):
# Destroying old page and creating new page
HomePage.root.destroy()
NewPage.window = Tk()
height = NewPage.window.winfo_screenheight()
width = NewPage.window.winfo_screenwidth()
NewPage.window.geometry("%dx%d" % (width, height))
# Creating button
back = Button(NewPage.window, text="back")
back.pack(side=BOTTOM)
back.bind("<Button 1>", lambda event, control=NewPage.window, func=HomePage: Back(event, control, func))
# Running Mainloop
NewPage.window.mainloop()
# Running HomePage function
HomePage()

Tkinter scale gets stuck each time a function is called

I have a problem where the tkinter Scale widget seems to get stuck whenever I run a seemingly big function.
This is the code:
from tkinter import Tk, Button, Frame, Scale
root = Tk()
slider = Scale(root, orient='horizontal')
slider.pack()
frame = Frame(root)
frame.pack()
num = 0
def buttons():
for widget in frame.winfo_children():
widget.destroy()
for i in range(50):
Button(frame, text='Button' + str(i)).pack()
def basic():
global num
slider.set(num)
num += 1
print(num)
if num <= 100:
slider.after(100, basic)
if __name__ == '__main__':
buttons()
basic()
root.bind('<space>', lambda x: buttons())
root.mainloop()
What I want my program to do is update the slider normally even when I press 'Space' (meaning calling the buttons() function)
If you watch closely each time you press Space the slider will get stuck a little.
Since I'm using the slider for an Mp3 player in order to show time elapsed, this loss of time is extremely important for example for audio files of 10 or so seconds since the slider falls behind a lot making it seem as if it's working wrong \
I'd also like to point out that destroying the buttons and then repacking them is necessary for me.
I suspect that this happens because the program has to go over the buttons() function something that takes time since it's creating 50 buttons. Or am I mistaken?
Can I avoid that lag?
PS: As I mentioned in my comment:
I normally have a button that renames a (button) which is a song and in order for them to alphabetically ordered after renaming i need to recall the function that draws them. If I only configure tha name of the button (and not redraw them), it will stay in place and not move down or up depending on its name, while on the actual directory the order will change leading to inappropriate behavior such as playing the same song
Here are some images for better understanding:
Thanks in advance!
Look at this code:
import tkinter as tk
def config_buttons():
# Get the `text` of the first button
starting_value = int(buttons[0].cget("text")) + 1
# Iterate over all of the buttons
for i, button in enumerate(buttons, start=starting_value):
# Change the button's `text` and `command` atributes
button.config(text=i, command=lambda i=i:print("Clicked %i"%i))
root = tk.Tk()
buttons = []
add_button = tk.Button(root, text="+1 on all buttons", command=config_buttons)
add_button.pack()
for i in range(50):
button = tk.Button(root, text=i, command=lambda i=i:print("Clicked %i"%i))
button.pack()
buttons.append(button)
root.mainloop()
When the add_button buttons is pressed, I iterate over all of the buttons and change their text and command attributes. As I am not creating new buttons, the function runs very fast.
You can implement something similar in your code. Basically, avoid creating new buttons and just update the ones you already have on the screen.

Switching between windows in tkinter

First I have open main window then through that I have another window called order .Now I have to make a button that get to the main window again
def back():
wn.destroy()
import purchase
Button(text="Back",width='30',height='5',command=back,fg='black',bg='green',bd='5',font=(40),command=back).place(x='100',y='600')
You need to make the button start a function to open the window.
Ex.
def OpenNewWindow:
newWindow = TopLevel(master)
B1 = tk.Button(text="example", command = OpenNewWindow
B1.pack()
Treat it like a normal window: geometry, title, etc.
but remember one thing:
KEEP THE TOPLEVEL IN THE MAIN LOOP

Drag and Drop button tkinter python

I'm new using tkinter on python and I would like to develop a program that can Drag and Drop a button pressing other one... I will try to explain : I have button 'A' that will create a new button 'B' and I want to Drag the New button to another place
Any help
Thanks
The tkinter.dnd module, as suggested by j_4321 in comments.
Here is some sample code using that library to do what you have said:
from tkinter import *
from tkinter.dnd import Tester as DragWindow, Icon as Dragable
# Make a root window and hide it, since we don't need it.
root = Tk()
root.withdraw()
# Make the actual main window, which can have dragable objects on.
main = DragWindow(root)
def make_btn():
"""Make a new test button."""
# The functional part of the main window is the canvas.
Dragable('B').attach(main.canvas)
# Make a button and bind it to our button creating function.
Button(main.top, text='A', command=make_btn).pack()
# Start the mainloop.
mainloop()

Buttons to remove themselves in Tkinter

I'm sorry if this has been asked already, but I haven't been able to find it. I'm also just starting to learn programming so feedback is appreciated. :)
My end goal is to create an 8 by "x" grid of buttons that change their own color when pushed. I want to use this to make a grid I can upload to the POV toy I've built. This code creates a column of 8 buttons each with a callback passing itself as an argument. The idea being the callback function can do things to the button like change it's color, or delete it.
import Tkinter
def unpack(i):
buttons[i].pack_forget()
print i
top = Tkinter.Tk() buttons = [] for i in range(0, 8):
buttons.append(Tkinter.Button(top, text='Hello', command=lambda: unpack(i)))
for button in buttons:
button.pack()
top.mainloop()
When I do this I get a windows with column of 8 buttons, and when I click on one one gets deleted. When I click on a second nothing happens. In my command prompt I get the number 7 printed no matter which button I press. I suspect the problem is in the for loop that creates the buttons, but I have no idea how to fix it.
Thanks!
Pass the button object to the callback function instead of the index, because the index is change after the item deletion in the list.
import Tkinter
top = Tkinter.Tk()
for i in range(0, 8):
btn = Tkinter.Button(top, text='Hello')
btn['command'] = lambda b=btn: b.pack_forget()
btn.pack()
top.mainloop()
NOTE: To prevent late binding problem, I used default parameter in the above code.

Categories