tkinter reading checkbutton values added through a for loop - python

I am making a menu test for the employees at my restaurant. The plan is for menu items to loop through "Loop items here" at which time they select the correct checkbuttons (ingredients) and then click the "submit and continue button". When they click the submit button I first need to read the on and off values of the checkbuttons to determine which items they have selected, then compare those to the correct answers that I defined in a dictionary of lists, then clear all checkbuttons and whether the answer is wrong or right the program will continue on and I will eventually have a results screen but right now I am stuck on how to read the checkbutton on and off values. I am simply trying to print the selected veggies right now and cant figure it out.
I think it has to do with the fact they are in different methods and also the fact that they were added in a loop? I am not sure exactly but I know my code is trying to read the wrong thing and any help would be greeeeatly appreciated!
Sorry for the lengthy question I just thought it would be beneficial to give you as much info as possible to understand what I am trying to do..
from tkinter import *
class GUI(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.grid()
self.parent.title("Wahoos Menu Test")
self.create_buttons()
global count
count = -1
def create_buttons(self):
for r in range(20):
for c in range(14):
Label(self, text='',
borderwidth=0).grid(row=r,column=c)
B = Button(self, text ="Begin Exam", relief=RIDGE, fg="black", command= self.on_button_press).grid(row=19, column=7)
L = Label(self, text="What comes in the following", fg="blue").grid(row=6, column=0)
self.veg = ['Lettuce', 'Cabbage', 'Cheese', 'Ahee Rice', 'Brown Rice', 'Banzai Veg', 'Red Cabbage', 'Black Beans', 'Cajun White Beans']
self.vegboxes = []
self.opt = []
c = 1
for ve in self.veg:
c +=1
self.v = IntVar()
self.vegboxes.append(self.v)
vo = Checkbutton(self, text=ve, variable=self.v, onvalue=1, offvalue=0).grid(row=c, column=11, sticky=W)
def on_button_press(self):
global count
count = count + 1
menuItems = {'nft': ['cabbage', 'cheese', 'corn', 'nf', 'salsa'],
'nckt': ['lettuce', 'cheese', 'corn', 'nck', 'salsa']}
menu = ['blackened fish taco', 'wahoos chicken salad']
if count == len(menu):
C = Button(self, text =" Your Done! ", relief=RIDGE, fg="black").grid(row=19, column=7)
else:
m = Label(self, text=menu[count], fg="black").grid(row=7, column=0)
C = Button(self, text ="Submit and Continue", relief=RIDGE, fg="black", command= self.read_checks).grid(row=19, column=7)
def read_checks(self):
for v in self.veg:
if self.v == 1:
print(self.veg[v])
def main():
root = Tk()
app = GUI(root)
root.mainloop()
if __name__ == '__main__':
main()

You could create a dictionary and have each key be the Checkbutton's label,
and have the value be the state "Control Variable".
Then you would check the state with the Control Variable's get() method as shown in the example below.
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.buttonDic = {
'Brown Rice':0,
'Banzai Veg':0,
'Red Cabbage':0,
'Black Beans':0
}
for key in self.buttonDic:
self.buttonDic[key] = tk.IntVar()
aCheckButton = tk.Checkbutton(self, text=key,
variable=self.buttonDic[key])
aCheckButton.grid(sticky='w')
submitButton = tk.Button(self, text="Submit",
command=self.query_checkbuttons)
submitButton.grid()
def query_checkbuttons(self):
for key, value in self.buttonDic.items():
state = value.get()
if state != 0:
print(key)
self.buttonDic[key].set(0)
gui = GUI()
gui.mainloop()
This approach allows you to create and analyze the Checkbuttons with one dictionary.
Note the use of items() in for key, value in self.buttonDic.items():
which is needed to prevent:
ValueError: too many values to unpack
More information on the Checkbutton widget and it's variable can be found: here
I'm going to include my first attempt which was based on
the Checkbutton widget's onvalue and offvalue,
in case it helps someone understand Control Variables a bit more.
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.bRiceV = tk.StringVar()
bRice = tk.Checkbutton(self, text="Brown Rice",variable=self.bRiceV,
onvalue="Brown Rice", offvalue="Off")
bRice.pack()
self.bVegV = tk.StringVar()
bVeg = tk.Checkbutton(self, text="Banzai Veg",variable=self.bVegV,
onvalue="Banzai Veg", offvalue="Off")
bVeg.pack()
self.varList = [self.bRiceV, self.bVegV]
submitButton = tk.Button(self, text="Submit",
command=self.query_checkbuttons)
submitButton.pack()
def query_checkbuttons(self):
for var in self.varList:
value = var.get()
if value != 'Off':
print(value)
var.set('Off')
gui = GUI()
gui.mainloop()

Related

Retrieve input from Entry widget stored as an instance variable and use it outside of the class

I have difficulties trying to get the input from Entry widget stored as a instance variable, so I can use it as input outside this class:
class CreateGUI:
def __init__(self, master):
self.master = master
self.master.geometry("275x325")
self.master.columnconfigure(0, weight=1)
self.master.columnconfigure(1, weight=2)
self.checkbutton_var1 = IntVar()
self.checkbutton_var2 = IntVar()
self.path = ''
self.type = []
def add_labels(self):
Label(self.master, text="Provide path to file:").grid(column=0, row=0, padx=10, pady=10, sticky="N")
def add_entries(self):
user_input = Entry(self.master)
user_input.grid(column=0, row=1, padx=5, pady=5, ipadx=60)
return user_input
def add_buttons(self, user_input):
checkbutton1 = Checkbutton(self.master, text="test1", variable=self.checkbutton_var1, onvalue=1,offvalue=0,height=2,width=10)
checkbutton1.grid(column=1, row=0)
checkbutton2 = Checkbutton(self.master, text="test2", variable=self.checkbutton_var2, onvalue=1, offvalue=0,height=2, width=10)
checkbutton2.grid(column=1, row=1)
button = Button(self.master, text="push", bg="pink", bd=100, fg="white",
command=lambda: self.retrieve_input(user_input.get(), self.checkbutton_var1.get(), self.checkbutton_var2.get()))
button.grid(column=0, row=3, padx=20, pady=20, sticky="NEWS")
def retrieve_input(self, p, *args):
self.path = p
#print(self.path)
for el in args:
self.type.append(el)
#print(self.type)
def main():
tk = Tk()
app = CreateGUI(tk)
app.add_labels()
user_input = app.add_entries()
app.add_buttons(user_input)
print(app.type)
print(app.path)
tk.mainloop()
When I start the program, write the input and press the button, it does not print anything. There are empty brackets printed the moment the program is initiated. The prints inside the retrieve_input are printing exactly what I need, but I need this inputs outside of the class, because they will be an input to another class.
I tried everything related to this problem, but it is not working and I would really appriciate any kind of help. Thanks!
You are getting the input for the Entry widget right before anyone can have a chance to type in it. As a result, user_input.get() will return an empty string. One thing you can do is make some sort of trigger for calling add_buttons() that the user activates when they are done filling out user_input. Further tweaking after that should make it work.
Please tell me if you have any more trouble.

Adding button (with variables) by pressing button - tkinter

I'm making a point of sale system and trying to implement a button, that when pressed a new button appears but also, a window which asks the user to input an Item
def newButton ():
w = Toplevel()
w.title("New Item") #creates new window when button is pressed
w.geometry("200x200")
itemNameLabel = Label(w, font=("arial", 15), text="What is the item called?")
itemNameLabel.grid(row=0, padx=5)
itemName = Entry(w, width=18, borderwidth=5)
itemName.grid(row=1, padx=5)
newItemName = itemName.get
itemPriceLabel = Label(w, font=("arial", 15), text="What is the item's price?")
itemPriceLabel.grid(row=4, padx=5)
itemPrice = Entry(w, width=18, borderwidth=5)
itemPrice.grid(row=5, padx=5)
def item6_Button():
global item6_qty
item6_price = itemPrice.get
item6_text = newItemName
item6_qty += 1
item6_text = (item6_text + " "+str(item6_price) +" "+ str(item6_qty)) #concatonates text & variable
item6.config(text=item6_text) #updates label text - doesn't add multiple
item6.pack()
item6_Button = Button(itemFrame, text=newItemName, width=10, height=5, command=item6_Button)
item6_Button.grid(row=7, column=1, padx=5)
item6 = Label(receiptFrame)
w.mainloop()
newButton= Button(itemFrame, text="Add New Button", width=20, height=5, command=newButton) #creates button for new window
newButton.place(x=480, y=600)
newButton = Label(itemFrame)
*item6_qty and item6_price are declared near the beginning of the program
This is what I have so far and although the window appears, I don't think the variables are actually set, on top of the new button appearing in the item frame. I'm not entirely sure how to go about this - do I need to use .insert for the variables?
This is the standard code I have which creates the normal button
#Item1 Button + Function
def item1_Button():
global item1_qty #making qty variable global so it can used
item1_text = ("Chips")
item1_qty += 1 #increments qty variable by one everytime button is clicked
item1_text = (item1_text + " "+str(item1_price) +" "+ str(item1_qty)) #concatonates text & variable
item1.config(text=item1_text) #updates label text - doesn't add multiple
item1.pack() #places label within the frame
item1_Button = Button(itemFrame, text="Chips", width=10, height=5, command=item1_Button)
#creates button + links to function
item1_Button.grid(row=4, column=1, padx=5) #positions button
item1 = Label(receiptFrame)#creates label for button
I'm not sure if I've provided enough code of what I've done to give a better picture of what I'm trying to achieve but I know large chunks of code aren't very favoured
here is an example of what You could do (does this help?):
from tkinter import Tk, Button, Entry, Toplevel
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.geometry('100x150')
self.btn = Button(self, text='Create New!', command=self.ask)
self.btn.pack()
def ask(self):
ask_window = InputWindow(self)
ask_window.focus_force()
def create(self, text):
button = Button(self, text=text)
button.pack()
class InputWindow(Toplevel):
def __init__(self, parent):
Toplevel.__init__(self, parent)
self.parent = parent
self.bind('<FocusOut>', self.destroy_)
self.user_input = Entry(self)
self.user_input.pack()
self.submit_btn = Button(self, text='Submit!', command=self.retrieve)
self.submit_btn.pack()
def retrieve(self):
text = self.user_input.get()
self.parent.create(text)
self.destroy()
def destroy_(self, event):
if isinstance(event.widget, Toplevel):
self.destroy()
root = MainWindow()
root.mainloop()

How to make a tkinter button open only one Toplevel?

I need a tkinter button to open only one Toplevel
but I tried using a counter that changes to 2 when one Toplevel is made and a if loop so it checks if the counter is 1, (it will make a Toplevel window if its 1)
but when I run the program I can make many windows by clicking the button multiple times
I think the solution of using a counter doesn't work
def menu_window(self): # this is in a class
self.frame4 = tk.Frame(self.master, padx=30, pady=30)
self.frame4.grid()
Counter = 1 ### COUNTER
button2 = tk.Button(self.frame4, text="Review your Quiz", command=lambda: PreviewQuiz(self.master, self.frame4,
Counter))
button2.grid(row=3, column=2, padx=40, pady=15, ipadx=28)
button3 = tk.Button(self.frame4, text=" Start your Quiz ")
button3.grid(row=4, column=2, padx=40, pady=5, ipadx=30)
class PreviewQuiz:
def __init__(self, master, frame4, Counter):
if Counter == 1: # CHECK IF COUNTER IS 1
self.master = master
self.review_q = tk.Toplevel(self.master)
self.frame5 = tk.Frame(self.master, padx=50, pady=20)
self.frame5.grid()
self.Counter = 2 # SET COUNTER TO 2
Just disable the button when the user clicks on it and enable it only when the top-level is closed.
Here is an example taken from the code you provided in your newest post:
import tkinter as tk
class Run:
def __init__(self, master):
self.master = master
self.button = tk.Button(master, text="TopLevel", command=self.make_new)
self.button.pack()
def make_new(self):
self.button['state'] = 'disabled'
new = tk.Toplevel(self.master)
lbl = tk.Label(new, text='only one topLevel')
lbl.pack()
new.protocol("WM_DELETE_WINDOW", lambda : self.button.configure(state='normal') or new.destroy()) # or make a method to change the state
master1 = tk.Tk()
i = Run(master1)
master1.mainloop()
Your code does not run, which makes it difficult to debug. There are however a few things:
When you assingn a value to counter in menu_window() you end the value with a ".", which makes it a float, not an integer.
You call PreviewQuiz with the counter argument, but when you update the counter you do it to an instance variable of PreviewQuiz, not of menu_window() class so next time you call PreviewQuiz you will still use 1.0.

Remove multiple label from tkinter

I'm trying to make a simple tkinter script that creates 10 numbers in the GUI after clicking a button. The problem is removing the numbers from the GUI. I can only remove one number. The numbers is beeing created via a loop.
I've printed out the name of the .pack() variable "self.label_a2" and the names are: .!label2, .!label3 and so on. I have not figured out how I can use the ".!label2" names to remove the lables. Nor How to remove more than just one lable.
Here is the code:
from tkinter import *
root = Tk()
class the_GUI:
def __init__(self, master):
self.master = master
master.title("Numbers")
self.start_prim_button = Button(master, text="Get numbers", command=self.main_prim, width=30)
self.start_prim_button.pack()
self.start_secu_button = Button(master, text="Remove numbers", command=self.main_secu, width=30)
self.start_secu_button.pack()
def main_prim(self):
self.label_a1 = Label(text='Numbers:', bg = "orange", width=30)
self.label_a1.pack()
numbers(self)
def main_secu(self):
remove_numbers(self)
def numbers(self):
print('Printing numbers:')
# Loop to create numbers
for i in range(10):
print(i)
self.label_a2 = Label(text=i, bg = "light green", width=30)
self.label_a2.pack()
print(self.label_a2)
def remove_numbers(self):
print('Removing numbers:')
try:
for i in range(10):
self.label_a2.destroy()
root.update_idletasks()
print('Removing', i)
except:
print('Generate numbers first')
my_gui = the_GUI(root)
root.mainloop()
When running the script it's only lable whit number 9 that gets removed. How to make all of the labes go away? Help please.
BR BaconFlip
You can add all the label instances to a list and then iterate through it while destroying them, your final code should look something like this
from tkinter import *
root = Tk()
class the_GUI:
def __init__(self, master):
self.master = master
master.title("Numbers")
self.start_prim_button = Button(master, text="Get numbers", command=self.main_prim, width=30)
self.start_prim_button.pack()
self.start_secu_button = Button(master, text="Remove numbers", command=self.main_secu, width=30)
self.start_secu_button.pack()
self.label_list=[]
def main_prim(self):
self.label_a1 = Label(text='Numbers:', bg = "orange", width=30)
self.label_a1.pack()
numbers(self)
def main_secu(self):
remove_numbers(self)
def numbers(self):
print('Printing numbers:')
# Loop to create numbers
for i in range(10):
print(i)
self.label_a2 = Label(text=i, bg = "light green", width=30)
self.label_a2.pack()
self.label_list.append(self.label_a2)
print(self.label_a2)
def remove_numbers(self):
print('Removing numbers:')
try:
for i in range(10):
self.label_list[i].destroy()
root.update_idletasks()
print('Removing', i)
self.label_a1.destroy()
self.label_list=[]
except:
print('Generate numbers first')
my_gui = the_GUI(root)
root.mainloop()
Another way of doing it is by using root.winfo_children() to get the children of the root widget and checking if the child is an instance of the label using isinstance(). This might be suitable for you if you don't want to store every label in lists.
def remove_numbers(self):
print('Removing numbers:')
try:
for i in self.master.winfo_children():
if isinstance(i, Label):
i.destroy()
root.update_idletasks()
print('Removing', i)
except:
print('Generate numbers first')

Creating elements by loop Tkinter

I'm looking for a way to create elements dynamically in Tkinter. For example, say the user enters 5, I'd like a loop to create 5 radio buttons and entries next to them.
Here's a simple example to get you started:
import Tkinter as tk
class ButtonBlock(object):
def __init__(self, master):
self.master = master
self.button = []
self.button_val = tk.IntVar()
entry = tk.Entry()
entry.grid(row=0, column=0)
entry.bind('<Return>', self.onEnter)
def onEnter(self, event):
entry = event.widget
num = int(entry.get())
for button in self.button:
button.destroy()
for i in range(1, num+1):
self.button.append(tk.Radiobutton(
self.master, text=str(i), variable=self.button_val, value=i,
command=self.onSelect))
self.button[-1].grid(sticky='WENS', row=i, column=0, padx=1, pady=1)
def onSelect(self):
print(self.button_val.get())
if __name__ == '__main__':
root = tk.Tk()
ButtonBlock(root)
root.mainloop()
There's nothing special about widgets. You create them in a loop the same way you would create any other object:
for i in range(5):
r = tk.Radiobutton(...)
r.pack(...) # or .grid(...)
# if you need to reference these buttons later,
# save them in a list
self.buttons.append(r)

Categories

Resources