Radio Button and Checkbox don't update variable value - python

I have a GUI that uses a class that calls a function that provides a popup window.
The popup box has a button that gets a directory from the user using askdirectory() from tkinter.filedialog, a pair of radio buttons(RadioG, RadioY), and checkbox(state_checkbox) that I want to update the two different variables(driveletter and statevalue) . I've set the drive letter to "G:" and that value is held correctly, but doesn't update if I select the "Y:" Radio button. I have the IntVar.set(1), but that checkbox does not populate as checked and checking/unchecking it keeps the value at 1 and does not update it to 0 when unchecked.
Any advice on what I'm missing to keep my values from updating is appreciated.
relevant section of code below:
class preferences(Preferences):
def save_preferences(self):
# Used to reset the folder location desired by the user
self.prefdialog = tk.Tk()
self.prefdialog.title('Set Preferences')
self.driveletter = tk.StringVar()
self.driveletter.set("G:")
self.statevalue = tk.IntVar()
self.statevalue.set(1)
self.fpath = '\\Desktop'
self.RadioG = tk.Radiobutton(self.prefdialog, text="G:", variable=self.driveletter,
value="G:", command=lambda: print(self.driveletter.get()))
self.RadioG.grid(row=3,column=0,sticky=tk.W,pady=4)
self.RadioY = tk.Radiobutton(self.prefdialog, text="Y:", variable=self.driveletter,
value="Y:", command=lambda: print(self.driveletter.get()))
self.RadioY.grid(row=3,column=1,sticky=tk.W,pady=4)
self.state_checkbox = tk.Checkbutton(
self.prefdialog, text="Check to use state folders",
variable=self.statevalue, command=lambda: print(self.statevalue.get()))
self.state_checkbox.grid(row=4,column=0,sticky=tk.W,pady=4)
self.prefdialog.mainloop()
For posterity: Looks like my issue was using Tk() twice. Per Bryan since I'm trying to create a popup window, I should be calling Toplevel(), not a second instance of Tk(). I also definitely called mainloop() twice, once for my main window and once for this popup window.

Related

Only opening a Window only when it already is not open in Tkinter

I created a button in tkinter with its command being to create anew window. But I don't want it to create a new window every time I click the create a new window button. I want to tell the program to open the new window only if it is not already open. If somewhere in the background the window is already open and the user presses the create a new window button I want it to move that window on the first layer of the user's screen (bringing it upfront from all the other open windows).
Here is my code --
def main():
main_bg = "Gray"
main_fg = "White"
import tkinter as tk
employees = []
window = tk.Tk()
window.configure(bg=main_bg)
window.title("Business app")
window.geometry("1100x650")
def open_new_window():
random_window = tk.Toplevel(window)
random_window.configure(bg=main_bg)
random_window.geometry("600x600")
random_button = tk.Button(window, text="Do Something", font="Times 32", bg=main_bg, fg=main_fg, command=open_new_window)
random_button.pack()
window.mainloop()
if __name__ == "__main__":
main()
I have searched websites like Geeksforgeeks and this website but I could not find the solution I was looking for. Please help.
NOTE -- PLEASE DO NOT CLOSE THIS QUESTION AND POINT ME TOWARDS ANOTHER FORUM BECAUSE I HAVE CHECKED OTHERS IN THIS WEBSITE AS I MENTIONED ABOVE
Added grab_set() will prevent open new window again.
def open_new_window():
random_window = tk.Toplevel(window)
#random_window.configure(bg=main_bg)
random_window.geometry("600x600")
random_window.grab_set()
Result:
When you click Do Something will prevent open new window. Unless close the topelevel window and then open new window again
You can save a reference to the widget, and then check the reference to see if it's set. If it is set, you can then call winfo_exists to check whether the reference points to an existing window.
random_window = None
...
def open_new_window():
global random_window
if random_window and random_window.winfo_exists():
# the window exists, so do nothing
return
random_window = tk.Toplevel(window)
random_window.configure(bg=main_bg)
random_window.geometry("600x600")

Tkinter pack_forget() and destroy() don't work

I have a problem with these two functions that don't work as expected.
I want to use a selection form drop-down menĂ¹ and based on the selection two different set of checkbox are displayed in the root window. To do so, I use this code:
from tkinter import *
import tkinter as tk
def evaluation(form):
form.pack_forget()
form.destroy()
form=Frame(root)
GotPlatform=platform.get()
for elem in PlatformOption[GotPlatform]:
OptionButtonSel[elem]=Checkbutton(form, text=elem)
OptionButtonSel[elem].pack()
form.pack(fill="both", expand = 1)
PlatformOption={'Option1':["option1-1","option1-2"],'Option2':["option2-1","option2-2"]}
OptionButtonSel={}
root = tk.Tk()
f1=Frame(root)
menuBar = tk.Menu(root)
menu1 = tk.Menu(root)
submenu = tk.Menu(root)
platform = StringVar()
submenu.add_radiobutton(label="Option1", value="Option1", variable=platform,command=lambda:evaluation(f1))
submenu.add_radiobutton(label="Option2", value="Option2", variable=platform,command=lambda:evaluation(f1))
menuBar.add_cascade(label="Options", menu=menu1)
menu1.add_cascade(label="Select option", menu=submenu)
root.config(menu=menuBar)
root.mainloop()
The code works but whenever I change the options fron drop-down menĂ¹, the checkboxes option are stacked and aren't overwritten as expected.
This puzzles me since I have used this other code and it works as expected:
from tkinter import Tk, Frame, Menu, Label
def show_frame1():
forget_all()
f1.pack(fill="both", expand = 1)
def show_frame2():
forget_all()
f2.pack(fill="both", expand = 1)
def forget_all():
f1.pack_forget()
f2.pack_forget()
root = Tk()
f1 = Frame(root)
f2 = Frame(root)
Label(f1, text="MENU SELECTED 1").pack()
Label(f2, text="MENU SELECTED 2").pack()
menubar=Menu(root)
subMenu=Menu(menubar, tearoff = 0)
menubar.add_cascade(label = 'MENU', menu=subMenu)
subMenu.add_command(label = 'SUBMENU1', command = show_frame1)
subMenu.add_command(label = 'SUBMENU2', command = show_frame2)
root.config(menu = menubar)
root.mainloop()
Aside from addind destroy(), the usage of frame and pack_forget() seems identical to me.
I use Python 3.10.1, IDE Spyder last version, Windows 8.
The root of the problem is that you create a form that never appears on the screen. You create it, but never call pack on it. You then pass this form to the function every time the button is clicked. In other words, you keep passing the original form, not the new one that is recreated.
The best solution is for your main code to call pack, place, or grid on the form, and then in the function you can delete the children of the form without deleting the form itself.
So, add the following shortly after creating f1:
f1.pack(fill="both", expand=True)
Next, modify evaluation to destroy the children rather than the form itself:
def evaluation(form):
for child in form.winfo_children():
child.destroy()
GotPlatform=platform.get()
for elem in PlatformOption[GotPlatform]:
OptionButtonSel[elem]=Checkbutton(form, text=elem)
OptionButtonSel[elem].pack()
It gives the effect that it doesn't work because the widget is invisible to the screen. This topic however is further explained below
pack_forget() not working
Just to prove my theory I did some other research about the topic and
https://www.geeksforgeeks.org/python-forget_pack-and-forget_grid-method-in-tkinter/
explains the concept of pack_forget() perfectly

Text to appear as checkbox is clicked in Python Tkinter

I am coding a GUI in Python 2.7 and I am making checkboxes. I want to know how to make a text appear right beside the checkbox when it is checked and unchecked. For eg. When I check the checkbox the text beside the checkbox should be 'enable' and when I uncheck the checkbox the text should be 'disable'.
You can assign same StringVar to textvariable and variable options of Checkbutton and set onvalue='enable' and offvalue='disable'. Then whenever the state of the checkbutton changes, the text changes:
import tkinter as tk
root = tk.Tk()
var = tk.StringVar(value='disable')
tk.Checkbutton(root, textvariable=var, variable=var, onvalue='enable', offvalue='disable').pack()
root.mainloop()
There's nothing particularly difficult about this. Checkbuttons can call a command when toggled. You can change the text inside the command using the configure method of the widget.
Here's a simple example:
import tkinter as tk
def toggle(widget):
variable = widget.cget("variable")
value = int(widget.getvar(variable))
label = "enable" if value else "disable"
widget.configure(text=label)
root = tk.Tk()
for i in range(10):
cb = tk.Checkbutton(root, text="disable")
cb.configure(command=lambda widget=cb: toggle(widget))
cb.pack(side="top")
root.mainloop()

How to refresh tkinter entry value? I get a 1-click lag

I built an interface where the user fills a hierarchical form. Past values are displayed in a ttk.Treeview.
I allow the user to edit previous values by clicking on the tree. The value gets filled on the form where it can be edited and overwriten.
The problem: the value I insert on the Entry widget is only displayed the next time the user clicks it, so that it is always 1 click lagging. Please run my sample code to get a better understanding. It gets confusing because if the user clicks a value and then another, it will display the previously clicked value.
It must have something to do with the event handling routine in tkinter, but I could not find and answer.
How can I get rid of this lag?
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
def cb_clique(event):
item = tree.selection()[0]
entry1.delete(0, "end")
entry1.insert(0, item)
entry1 = tk.Entry(root, width=15)
entry1.grid(row=1,column=1)
tree = ttk.Treeview(root)
tree.bind("<Button-1>", cb_clique)
tree["columns"]=("valor")
tree.column("valor", width=200 )
tree.heading("valor", text="Valor")
tree.grid(row=3, column = 1, columnspan = 4)
tree.insert("", "end", iid = "Will display position",text = "Click me",
values=("a","b"))
tree.insert("", "end", iid = "Use position to get info",
text = "Click me", values=("a","b"))
root.mainloop()
Looks like the <Button-1> event triggers before the window notices that the selection has changed, so selection() returns the thing that was selected before your click. Try changing the event binding to <<TreeViewSelect>>.
tree.bind("<<TreeviewSelect>>", cb_clique)

Python tkinter, how to disable background buttons from being clicked

When opening a new tkinter window, I only want the user to be able to click buttons on the new window. They should not be able to click on buttons from other windows that are part of the application. How would I accomplish this?
Here is a snip of my code:
def exportEFS(self):
self.exportGUI = Toplevel()
Button(self.exportGUI, text='Backup', command=self.backup).pack(padx=100,pady=5)
Button(self.exportGUI, text='Restore', command=self.restore).pack(padx=100,pady=5)
def backup(self):
self.backupWindow = Toplevel()
message = "Enter a name for your Backup."
Label(self.backupWindow, text=message).pack()
self.entry = Entry(self.backupWindow,text="enter your choice")
self.entry.pack(side=TOP,padx=10,pady=12)
self.button = Button(self.backupWindow, text="Backup",command=self.backupCallBack)
self.button.pack(side=BOTTOM,padx=10,pady=10)
In this snip, once the backupWindow is opened, the exportGUI remains open, but the user should not be able to click "Backup" or "Restore" while the backupWindow is opened.
Thanks!
You will want to call grab_set on the TopLevel window so that all keyboard and mouse events are sent to that.
def exportEFS(self):
self.exportGUI = Toplevel()
Button(self.exportGUI, text='Backup', command=self.backup).pack(padx=100,pady=5)
Button(self.exportGUI, text='Restore', command=self.restore).pack(padx=100,pady=5)
def backup(self):
self.backupWindow = Toplevel()
self.backupWindow.grab_set()
message = "Enter a name for your Backup."
Label(self.backupWindow, text=message).pack()
self.entry = Entry(self.backupWindow,text="enter your choice")
self.entry.pack(side=TOP,padx=10,pady=12)
self.button = Button(self.backupWindow, text="Backup",command=self.backupCallBack)
self.button.pack(side=BOTTOM,padx=10,pady=10)
What you can do is set the state to disabled. As so:
self.button.config(state="disabled")
And to enable it, you just use:
self.button.config(state="normal")
However, you must assign your buttons to variables first, like this:
self.backup=Button(self.exportGUI, text='Backup', command=self.backup)
self.backup.pack(padx=100,pady=5)
self.restore=Button(self.exportGUI, text='Restore', command=self.restore)
self.restore.pack(padx=100,pady=5)
so you would disable these using:
self.backup.config(state="disabled")
self.restore.config(state="disabled")
and re-enable using:
self.backup.config(state="normal")
self.restore.config(state="normal")
Please note however, that while the button is disabled, nothing can be changed to that button, both through the code, or through the user using it. So that means if you wanted to change the text of that button, you would have to change the state of the button to "normal" before changing it (if it already isn't in that state, which by default, all widgets are in that state when first created).
Cheers :)

Categories

Resources