How to forget buttons created by a loop? - python-3.x

I need to create buttons with a for loop, here its an example of what i need to do:
But the problem is that when I press the different buttons, its prints the correct number, but it only forgets the last button created (in this case the button "4").
How can i do to forget all the buttons at once by only pressing one of them?
Its important the creation of the buttons by the loop
import tkinter as tk
root=tk.Tk()
def Eliminate(Number):
def Forget(number):
button.pack_forget()
print(number)
for i in range(Number):
button= tk.Button(root,text=i,command=lambda number=i:Forget(number))
button.pack()
Eliminate(5)
root.mainloop() ```

You need to pass in the button widget itself, not the number that created it. To do that you simply need to issue the command argument in a new line, like this:
import tkinter as tk
def Forget(btn):
btn.pack_forget()
def Eliminate(Number):
for i in range(Number):
button= tk.Button(root,text=i)
button.config(command=lambda button=button:Forget(button))
button.pack()
root=tk.Tk()
Eliminate(5)
root.mainloop()
However as the command is only calling the pack_forget method, it's much easier to forget making your own callback function and just provide pack_forget:
import tkinter as tk
def Eliminate(Number):
for i in range(Number):
button= tk.Button(root,text=i)
button.config(command=button.pack_forget)
button.pack()
root=tk.Tk()
Eliminate(5)
root.mainloop()

Related

multiple commands for a tkinter button

I have been trying to execute 2 commands in 1 button. I have read that using lambda can solve the problem. But my situation is a little different. On button press, one command is to destroy the existing GUI, and the second command, I want is to open another SERVER GUI. Below is my existing button functionality.
exit_button = Button(topFrame, text='Quit', command=window.destroy)
How can I open another GUI using same button?
Thank you for any help.
Edit:-
I have now created the below function:
def close_func():
os.kill(os.getpid(), signal.SIGINT)
GUI_Interface()
window.destroy()
server_socket.close()
GUI_Interface is a function that I need to call after closing the existing .py file. If I put GUI_Interface as the first command for close_func(), then it really does not go back to the 2nd step and never closes the existing.py file.
And if I place GUI_Interface in the end, it just closes the existing one and nevr opens the function of the new .py file
Three options at least:
using or (with lambda if arguments):
from tkinter import Tk, Button
root = Tk()
Button(root, text='Press', command=lambda: print('hello') or root.destroy() or print('hi')).pack()
root.mainloop()
important to use or because and didn't work (don't know why or how this works at all)
or use function definitions:
from tkinter import Tk, Button
def func():
print('hello')
root.destroy()
print('hi')
root = Tk()
Button(root, text='Press', command=func).pack()
root.mainloop()
or lists with lambda:
from tkinter import Tk, Button
def func():
print('hello')
root.destroy()
print('hi')
root = Tk()
Button(root, text='Press', command=lambda: [print('hello'), root.destroy()]).pack()
root.mainloop()

hiding tkinter window using withdraw vs wm_withdraw

What is the difference between withdraw and wm_withdraw?
import time
import tkinter as tk
def hide():
root.withdraw()
time.sleep(2)
root.deiconify()
root = tk.Tk()
tk.Button(root, text = 'hide', command = hide).pack()
root.mainloop()
When the 'hide' button is clicked, the window is hidden. It disappears from the panel (taskbar), and is not visible in the task view (simultaneous view of all open windows) for 2 seconds.
import time
import tkinter as tk
def hide():
root.wm_withdraw()
time.sleep(2)
root.deiconify()
root = tk.Tk()
tk.Button(root, text = 'hide', command = hide).pack()
root.mainloop()
Same code, but wm_withdraw instead of withdraw. Again, clicking the 'hide' button makes the both the taskbar entry and the window itself invisible for 2 seconds.
Is there any difference at all between these two? Which one should I use? Further, should I use deiconify or wm_deiconify? All four combinations (withdraw, deiconify; wm_withdraw, deiconify; withdraw, wm_deiconify; wm_withdraw, wm_deiconify) seem to do the exact same thing. Is there any application where they will do different things?
There's no difference between them - they both (withdraw and deiconify) just shortucts for wm_ counterparts.
The same applies to all functions, that interact with Window manager under Wm class.
There is no difference between withdraw and wm_withdraw. I can not specify why this was done, but here is the source of tkinter in which we have line withdraw = wm_withdraw (which makes it clear that both calls end up at the same method):
def wm_withdraw(self):
"""Withdraw this widget from the screen such that it is unmapped
and forgotten by the window manager. Re-draw it with wm_deiconify."""
return self.tk.call('wm', 'withdraw', self._w)
withdraw = wm_withdraw

Python 3 Tkinter button command not working (very specific scenario)

I am using these calendar modules found in this post for my program, with some slight modifications to the imports to make it work for the latest python version.
I'll just show the snippets of my code that I feel does matter to this problem.
So I have this pop up window that I made that I use for alerts:
#class for pop-up windows for alerts, errors etc.
class PopUpAlert():
def __init__(self, alert='Alert!'):
self.root = tk.Tk()
tk.Label(self.root,
text=alert,
font="Verdana 15",
fg='red',
padx=10,
pady=5).pack(side=tk.TOP)
self.root.bind('<Return>', (lambda event: self.ok()))
tk.Button(self.root,
text='ok',
pady=10,
command=self.ok).pack(side=tk.TOP)
def ok(self):
print('ok clicked')
self.root.destroy()
The function ok was made just for me to test if the function is even being called. This window works completely fine in my code, except when I try to implement with the calendar, where the "ok" button of my PopUpAlert (which is supposed to destroy the window) stops working:
class CalendarDialog(tkSimpleDialog.Dialog):
"""Dialog box that displays a calendar and returns the selected date"""
def body(self, master):
self.calendar = ttkcalendar.Calendar(master)
self.calendar.pack()
def apply(self):
self.result = self.calendar.selection
def validate(self):
if self.calendar.selection == None:
PopUpAlert(alert='Please select a date or click cancel!')
return False
return True
The calendar has an "ok" button that is used to confirm selection of the date and close the calendar window. What I was trying to do is make it such that the user cannot click "ok" to close the window if he/she has not picked a date. For that, I used the function validate which is pre-defined in the class tkSimpleDialog.Dialog which my CalendarDialog inherits from. I overwrote the function in my CalendarDialog class to call up PopUpAlert, then returned False to the parent function ok (which is called when the "Ok" button is pressed on the calendar window):
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
(The whole thing can be found in the tkSimpleDialog file that's linked in the other SO page that I linked above.)
After commenting out lines one by one I found that the "ok" button on my PopUpAlert only didn't work when self.root.destroy() isn't called on the calendar. Why? How do I fix this?
I already tried changing my PopUpAlert to be a Toplevel window, which also didn't work.
It would be a lot nicer of you to provide a mcve instead of asking us to make it.
The problem is that a dialog by default disables clicks to other windows, including windows it spawns. To fix this you need to use a Toplevel instead of Tk (as mentioned) AND add this line of code to the end of PopUpAlert.__init__:
self.root.grab_set()
It would be a lot neater if you subclassed Toplevel rather than that weird wrapper. Here's a mcve:
try:
import Tkinter as tk
import tkSimpleDialog as sd
except:
import tkinter as tk
from tkinter import simpledialog as sd
#class for pop-up windows for alerts, errors etc.
class PopUpAlert(tk.Toplevel):
def __init__(self, master, alert='Alert!', **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
tk.Label(self,
text=alert,
font="Verdana 15",
fg='red',
padx=10,
pady=5).pack(side=tk.TOP)
self.bind('<Return>', self.ok)
tk.Button(self,
text='ok',
pady=10,
command=self.ok).pack(side=tk.TOP)
self.grab_set() # this window only gets commands
def ok(self, *args):
print('ok clicked')
self.destroy()
class CalendarDialog(sd.Dialog):
"""Dialog box that displays a calendar and returns the selected date"""
def body(self, master):
self.calendar = tk.Label(master, text="Whatever you do, don't click 'OK'!")
self.calendar.pack()
def validate(self):
PopUpAlert(self, alert='Please select a date or click cancel!')
def display():
CalendarDialog(root)
root = tk.Tk()
tk.Button(root, text='data data data', command=display).pack()
root.mainloop()
Note I also got rid of that useless lambda, which happens to be a pet peeve of mine. lambda is great in some cases, but it's very rarely needed.

Enter button doesn't trigger

i am trying to open a google search window using python and the enter button trigger doesn't work.
so if you press the enter button it should open the window but it doesn't.
i am also using tkinter for gui.
help me :( thanks
(i am using win 10)
import tkinter as tk
def keyup():
opener()
def opener():
import webbrowser
text = name.get().strip()
query=str(text)
webbrowser.open("www.google.com/search?rlz=1C1CHZL_koUS766US766&ei=rDxSWtPFOeKt0gK4_YioDg&q="+query)
def clear():
name.delete(0, 'end')
root = tk.Tk()
ler=tk.Label(root, text = "what do you want to search?",font=("Helvetica", 40))
ler.pack()
name = tk.Entry(root,width=100)
name.pack()
widget=tk.Button(root, text = 'search', command =opener,width=30,height=3)
widget.pack()
widget.bind('<Enter>', opener)
wider=tk.Button(root, text = 'clear', command =clear,width=10,height=3)
wider.pack()
root.mainloop()
how do i fix this?
You have few mistakes
for key "Enter" is event <Return>. You can also assign <Return> to root and it works even when button is not selected/focused.
root.bind('<Return>', opener)
or assign to Entry
name.bind('<Return>', opener)
and "Enter" will run opener only when Entry is focused.
command= executes function without arguments but bind() executes function with one argument so you have to define function with argument which has default value and then it will work with both.
def opener(event=None):
use http:// in url because you can use file:// to open local file. On Linux link without http:// is treated as local file.
Smaller mistakes: put all import at top to make code more readable, Entry returns string so you don't need str()
EDIT: added name.bind('<Return>', opener)
import tkinter as tk
import webbrowser
def opener(event=None):
text = name.get().strip()
webbrowser.open("http://www.google.com/search?q="+text)
def clear():
name.delete(0, 'end')
root = tk.Tk()
ler=tk.Label(root, text="what do you want to search?", font=("Helvetica", 40))
ler.pack()
name = tk.Entry(root,width=100)
name.pack()
name.bind('<Return>', opener) # added
widget=tk.Button(root, text='search', command=opener, width=30, height=3)
widget.pack()
widget.bind('<Return>', opener)
wider = tk.Button(root, text='clear', command=clear, width=10, height=3)
wider.pack()
root.mainloop()
With Tk, the event binding <Enter is used to describe the cursor moving over the space occupied by the widget (entering the space). The binding you wish to use is <Return>, which is mapped to the return (enter) key on the keyboard.
I think it should be <Return> instead of <Enter>. Also, the indentations seem incorrect.

Tkinter Assign Different Functions To Each Ranged Button

the problem is its only works for one global function.. how to assign each button for a different function
from tkinter import*
class mainWindow:
def __init__(self,master):
mainFrame=Frame(master)
mainFrame.pack()
self.cv=Canvas(scrollregion=(0,0,200,1200),width=200,height=1200,bg='green')
self.scb=Scrollbar(command=self.cv.yview)
self.cv.pack(side=LEFT)
self.scb.pack(side=RIGHT,fill=Y)
self.cv.configure(yscrollcommand=self.scb.set)
#frame on canvas to pack title frame
self.cvFrame=Frame(self.cv)
self.cvFrame_window=self.cv.create_window(5,5,anchor=NW,window=self.cvFrame)
self.tFrame=Frame(self.cvFrame)
self.tFrame.pack(in_=self.cvFrame)
self.t=['Site Preparation','WBLFF','Frame','Roof Construction','Roof Finishes',]
self.tr=[0,1,2,3,4,5]
#range button created inside one frame
for i in range(5):
self.tButton=Button(self.tFrame,text=self.t[i],bg='purple',fg='white',width=20,relief=GROOVE)
self.tButton.grid(in_=self.tFrame,row=self.tr[i])
for i in range(0,1):
self.tButton.bind('<Button-1>',self.subT1)
#open up new subtitle under main title
def subT1(self,event):
self.s=['Site Preparation','Site Clearence','Earth Works']
self.sc=['dark violet','yellow','yellow']
self.sf=['white','black','black']
self.sr=[0,1,2]
self.sFrame=Frame(self.tFrame)
self.sFrame.grid(in_=self.tFrame,row=0)
for x in range(3):
self.sBtn=Button(self.sFrame,text=self.s[x],bg=self.sc[x],fg=self.sf[x],width=20,relief=GROOVE)
self.sBtn.grid(in_=self.sFrame,row=self.sr[x])
self.sBtn.bind('<Button-1>',self.destF)
#detroy frame back to main title
def destF(self,event):
self.sFrame.destroy()
#root window
root=Tk()
root.title('Built Up Rates')
root.geometry('220x600+0+0')
A=mainWindow(root)
root.mainloop()
Open Up The Program
Open up the subtitle by clicking the button
the problem is its only works for one global function.. how to assign each button for a different function
Firstly: you can use Button(command=function_name)
in place of bind('< Button-1 >',function_name)
Create list of functions
functions = [self.subT1, self.other_function]
for i in range(2):
Button(command=functions[i])
def subT1(self): # without `event` or with `event=None`
from tkinter import*
class mainWindow:
def __init__(self,master):
mainFrame=Frame(master)
mainFrame.pack()
self.cv=Canvas(scrollregion=(0,0,200,1200),width=200,height=1200,bg='green')
self.scb=Scrollbar(command=self.cv.yview)
self.cv.pack(side=LEFT)
self.scb.pack(side=RIGHT,fill=Y)
self.cv.configure(yscrollcommand=self.scb.set)
#Frame to Canvas
self.cvFrame=Frame(master)
self.cvFrame_window=self.cv.create_window(5,5,anchor=NW,window=self.cvFrame)
self.t=['Site Preparation','WBLFF','Frame','Roof Construction','Roof Finishes',]
self.tr=[0,1,2,3,4,5]
#range button created inside one frame
for i in range(5):
self.tButton=Button(self.cvFrame,text=self.t[i],bg='purple',fg='white',width=20,relief=GROOVE)
self.tButton.grid(in_=self.cvFrame,row=self.tr[i])
if i in range(0,1):
self.tButton.bind('<Button-1>',self.subT1)
if i in range(1,2):
self.tButton.bind('<Button-1>',self.subT2)
def subT1(self,event):
self.s=['Site Preparation','Site Clearence','Earth Works']
self.sc=['dark violet','yellow','yellow']
self.sf=['white','black','black']
self.sFrame=Frame(self.cvFrame)
self.sFrame.grid(in_=self.cvFrame,row=0)
for x in range(3):
self.sBtn=Button(self.sFrame,text=self.s[x],bg=self.sc[x],fg=self.sf[x],width=20,relief=GROOVE)
self.sBtn.grid(in_=self.sFrame,row=x)
self.sBtn.bind('<Button-1>',self.destF)
def subT2(self,event):
self.s=['WBLFF','Pile Cap','Column Stump','Ground Beam','Ground Slab']
self.sc=['dark violet','yellow','yellow','yellow','yellow']
self.sf=['white','black','black','black','black']
self.sFrame=Frame(self.cvFrame)
self.sFrame.grid(in_=self.cvFrame,row=1)
for x in range(5):
self.sBtn=Button(self.sFrame,text=self.s[x],bg=self.sc[x],fg=self.sf[x],width=20,relief=GROOVE)
self.sBtn.grid(in_=self.sFrame,row=x)
self.sBtn.bind('<Button-1>',self.destF)
#detroy frame back to main title
def destF(self,event):
self.sFrame.destroy()
#root window
root=Tk()
root.title('Built Up Rates')
root.geometry('220x600+0+0')
A=mainWindow(root)
root.mainloop()
got it :) by using 'if' statements... works well in ranged object and assign the parameters is there any other way to make it much more simpler like inheritance or global definition perhaps?

Resources