Using if statements in Tkinter won't output on click - python

I am trying to make my first GUI app in python, and am very new to this. I want to make something for my wife (teacher) such that she can enter the number of "Growth Points" a student earns on a Standardized Test and spits out the number of "Gotchas" (in-class currency as incentives) the student receives.
The rules are: 3 gotchas for the first 6 growth points, then 5 gotchas for each subsequent growth point.
I was following a Guide from Geeksforgeeks to make the app, and can get the button to change text of a label already created but not output the Gotchas earned by the student... This is purely just to learn how to do it, so things like the Menu option is not necessary, just included as I learned.
Here is the code I have tried:
# Import Module
from tkinter import *
# create root window
root = Tk()
# root window title and dimension
root.title("Welcome to my first App")
# Set geometry (widthxheight)
root.geometry('450x300')
# all widgets will be here
# Determine the number of Growth Points
lbl = Label(root, text="How many Growth Points did the Student Earn?")
lbl.grid()
# Gather the number of Growth Points from the User
growth_points = Entry(root, width=10)
growth_points.grid(column=1, row=0)
menu = Menu(root)
item = Menu(menu)
item.add_command(label='New')
menu.add_cascade(label='File', menu=item)
root.config(menu=menu)
# Function for when the button is clicked
def clicked():
lbl.configure(text = "Clicked!") # Just to check whether the button does something
growth_points = int(growth_points)
if growth_points <= 6:
lbl_test = Label(root, text="Under 6") # Test to see if the if statement works
lbl_test.grid(column=0,row=2) # Output for the if statement (Doesn't work?)
num_gotcha = growth_points*3 # Gives the number of Gotchas for the first 6 growth points
num_gotcha = str(num_gotcha) # Converts into a String for Printing
gp_lbl = Label(root, text=num_gotcha) # Labels and inserts after clicking button
gp_lbl.grid(column=0,row=3)
elif growth_points > 6:
over_gotcha = growth_points - 6 # Gets the amount of Growth Points over the first 6
num_gotcha = over_gotcha * 5 + 18 # Finds the Gotchas for the GP's over 6, then adds the GPs for the first 6
num_gotcha = str(num_gotcha)
gp_lbl2 = Label(root, text="Student gets" + " " + num_gotcha + " " + "Gotchas") # Another way of trying to display the value
gp_lbl2.grid(column=0, row=3)
# Adding a button to begin the program
btn = Button(root, text = "Enter" , command=clicked)
btn.grid(column=2, row=0)
# Execute Tkinter
root.mainloop()
I can get the button to change the text, so it seems like the button works but it won't go through the If statements.. How should this work? Thanks for any help!

Didn't meddle with the "Gothcas" logic. Just added global reference to entry widget and renamed the variable for entered growth points:
# Function for when the button is clicked
def clicked():
global growth_points
lbl.configure(text="Clicked!") # Just to check whether the button does something
growth_points_number = int(growth_points.get())
if growth_points_number <= 6:
lbl_test = Label(root, text="Under 6") # Test to see if the if statement works
lbl_test.grid(column=0, row=2) # Output for the if statement (Doesn't work?)
num_gotcha = growth_points_number * 3 # Gives the number of Gotchas for the first 6 growth points
num_gotcha = str(num_gotcha) # Converts into a String for Printing
gp_lbl = Label(root, text=num_gotcha) # Labels and inserts after clicking button
gp_lbl.grid(column=0, row=3)
elif growth_points_number > 6:
over_gotcha = growth_points_number - 6 # Gets the amount of Growth Points over the first 6
num_gotcha = over_gotcha * 5 + 18 # Finds the Gotchas for the GP's over 6, then adds the GPs for the first 6
num_gotcha = str(num_gotcha)
gp_lbl2 = Label(root,
text="Student gets" + " " + num_gotcha + " " + "Gotchas") # Another way of trying to display the value
gp_lbl2.grid(column=0, row=3)
That should work.

Related

Tkinter - Creating multiple check boxes using a loop

I am trying to create a program that allows the user to select any number of check boxes and hit a button to return a random result from those check boxes. Since I am basing my list off the roster of Smash bros ultimate, I am trying to avoid creating 70+ variables just to place check boxes. However, I am unable to figure out how to iterate this. The various values set for rows are just placeholders until I can figure this out. I would also like to have a reset button at the top that allows the user to automatically uncheck every box. This code is what I have so far. Any help would be greatly appreciated.
#!/usr/bin/python3
from tkinter import *
window = Tk()
#window name and header
window.title("Custom Random SSBU")
lbl = Label(window, text="Select the fighters you would like to include:")
lbl.grid(column=1, row=0)
f = [] #check boxes
ft = open("Fighters.txt").readlines() #list of all the character names
fv=[0]*78 #list for tracking what boxes are checked
ff=[] #list to place final character strings
def reset():
for i in fv:
fv[i]=0
rst = Button(window, text="Reset", command=reset)
rst.grid(column=0, row=3)
for y in range (0,77):
f[y] = Checkbutton(window, text = ft[y], variable = fv[y])
f[y].grid(column=0, row=4+y)
def done():
for j in fv:
if fv[j] == 1:
ff.append(fv[j])
result = random.choice(ff)
r=Label(window, text=result)
d = Button(window, text="Done", command=done)
d.grid(column=0, row = 80)
window.mainloop()
Unfortunately I'm afraid you are going to have to create variables for each checkbox.
tkinter has special purpose Variable Classes for holding different types of values, and if you specify an instance of one as the variable= option when you create widgets like Checkbutton, it will automatically set or reset its value whenever the user changes it, so all your program has to do is check its current value by calling its get() method.
Here's an example of the modifications to your code needed to create them in a loop (and use them in the done() callback function):
import random
from tkinter import *
window = Tk()
#window name and header
window.title("Custom Random SSBU")
lbl = Label(window, text="Select the fighters you would like to include:")
lbl.grid(column=1, row=0)
with open("Fighters.txt") as fighters:
ft = fighters.read().splitlines() # List of all the character names.
fv = [BooleanVar(value=False) for _ in ft] # List to track which boxes are checked.
ff = [] # List to place final character strings.
def reset():
for var in fv:
var.set(False)
rst = Button(window, text="Reset", command=reset)
rst.grid(column=0, row=3)
for i, (name, var) in enumerate(zip(ft, fv)):
chk_btn = Checkbutton(window, text=name, variable=var)
chk_btn.grid(column=0, row=i+4, sticky=W)
def done():
global ff
ff = [name for name, var in zip(ft, fv) if var.get()] # List of checked names.
# Randomly select one of them.
choice.configure(text=random.choice(ff) if ff else "None")
d = Button(window, text="Done", command=done)
d.grid(column=0, row=len(ft)+4)
choice = Label(window, text="None")
choice.grid(column=1, row=3)
window.mainloop()
I wasn't sure where you wanted the Label containing the result to go, so I just put it to the right of the Reset button.
variable = fv[y]
This looks up the value of fv[y] - i.e, the integer 0 - at the time the Checkbutton is created, and uses that for the variable argument.
You need to use an instance of one of the value-tracking classes provided by TKinter, instead. In this case we want BooleanVar since we are tracking a boolean state. We can still create these in a list ahead of time:
text = open("Fighters.txt").readlines()
# Let's not hard-code the number of lines - we'll find it out automatically,
# and just make one for each line.
trackers = [BooleanVar() for line in text]
# And we'll iterate over those pair-wise to make the buttons:
buttons = [
Checkbutton(window, text = line, variable = tracker)
for line, tracker in zip(text, trackers)
]
(but we can not do, for example trackers = [BooleanVar()] * len(text), because that gives us the same tracker 78 times, and thus every checkbox will share that tracker; we need to track each separately.)
When you click the checkbox, TKinter will automatically update the internal state of the corresponding BooleanVar(), which we can check using its .get() method. Also, when we set up our options for random.choice, we want to choose the corresponding text for the button, not the tracker. We can do this with the zip trick again.
So we want something more like:
result_label = Label(window) # create it ahead of time
def done():
result_label.text = random.choice(
label
for label, tracker in zip(text, trackers)
if tracker.get()
)

overlapping statuses using tkinter module

I am trying to work on GUI using tkinter module. I created label with random greetings generator. However,they are overlapping with previous generated labels.This is the code:
import tkinter
import random
window = tkinter.Tk()
# to rename the title of the window
window.title("GUI")
window.geometry("500x500")
#defining Functions
def search_greetings():
phrases = ["Hallo ", "Hoi ", "Greetings "]
name = str(entry1.get())
text = ".Please enter your search term below."
return phrases[random.randint(0, 2)] + name + text
def search_display():
greeting = search_greetings()
# This creates the text field
greeting_display = tkinter.Label(window,text = search_greetings())
greeting_display.grid(row=6,column=1)
search_box = tkinter.Entry()
search_box.grid(row=7)
# pack is used to show the object in the window
label = tkinter.Label(window, text = "Hello World! Welcome to my app")
label.grid(row = 0)
# creating 2 text labels and input labels
tkinter.Label(window, text = "Username").grid(row = 2) # this is placed in 1 0
# 'Entry' is used to display the input-field
entry1 = tkinter.Entry()
entry1.grid(row = 2, column = 1) # this is placed in 1 1
tkinter.Label(window, text = "Password").grid(row = 3) # this is placed in 2 0
tkinter.Entry().grid(row = 3, column = 1) # this is placed in 2 1
# 'Checkbutton' is used to create the check buttons
tkinter.Checkbutton(window, text = "Keep Me Logged In").grid(columnspan = 2) # 'columnspan' tells to take the width of 2 columns
# you can also use 'rowspan' in the similar manner
# Submit button
button = tkinter.Button(text = "Submit",command = search_display).grid(row = 5)
window.mainloop()
It is returning the labels like below:
Greetings 1234.Please enter your search term below.
G Hallo ashita.Please enter your search term below.v.
G Hallo ashita.Please enter your search term below..v.
Please check the error in the code.
Seems like you are making a new Label every time. You can edit a Label's text like so:
mylabel = tkinter.Label(root, text="First!")
mylabel["text"] = "Second!"
This will display "Second!" (after being packed). You can even change the text after the Label is packed.

Connecting button to run function to add labels

I just finished reading a Python book called Practical Programming 2nd ed. and thought I'd challenge myself to apply what I learned to make my first small program for work (convenience store) to create a list of chips I need to order using tkinter as the interface.
I would input how many of each brand of chips I have in stock, and after pressing the 'Order' button, it would return the number of each brand of chips needed to order.
Here is the code I've got so far:
import tkinter as tk
# Initiate tkinter
window = tk.Tk()
# Create a dictionary of brand of chips with a value of how many chips should be in stock
chips = {'Miss Vickies Original': 4, 'Miss Vikies Salt & Vinegar': 4}
# Start counting rows
row_num = 0
for brand in chips:
# Create label describing the brand of chips
label = tk.Label(window, text=brand)
label.grid(row=row_num, column=1)
# Create an entry box to input number of chips in stock
entry = tk.Entry(window)
entry.grid(row=row_num, column=2)
# Add one; To start at next row
row_num += 1
# Return number of chips to order
def order(entry_data, stock_required):
# Subtract how much is in stock from how much should be in stock
order_quantity = int(stock_required) - int(entry_data)
# Create label to the left of each chip with how many should be ordered
order_label = tk.Label(window, text="'Order: ', order_quantity")
order_label.pack(side='left')
# Order button
button = tk.Button(window, text='Order', command=lambda: order(entry.get(), chips[1]))
button.pack(side='bottom')
window.mainloop()
I think I've confused myself with my own code lol. I'm having great difficulty figuring out how to make the button(placed at the bottom) trigger the calculation to occur and show the order_label to the left of each chips label.
A problem I see with my code that I'm not sure how to fix is the label would only be called once, as the function doesn't contain a loop. What's the recommended way to solve this?
I've checked: Tkinter: Link an Entry Widget to a Button to a Function.
But not a related problem, as I have the lambda: order function already in.
Here is how it should look like:
This is my first program, so any constructive criticism is encouraged!
I added the functionality needed to make your code work as you intended; You will need to keep track of the current level of stock to make this something useful though.
import tkinter as tk
def generate_order_quantities():
for brand, datafields in chips_entry_fields.items():
entry, quantity_to_order, label = datafields
recommended_stock_level = chips_stock_levels[brand]
try:
quantity_sold = int(entry.get())
except ValueError:
quantity_sold = 0
quantity_to_order.set(min(quantity_sold, recommended_stock_level))
if __name__ == '__main__':
# Initiate tkinter
window = tk.Tk()
# Create a dictionary of brand of chips_stock_levels with a value of how many chips_stock_levels should be in stock
chips_stock_levels = {'Miss Vickies Original': 4, 'Miss Vikies Salt & Vinegar': 4}
chips_quantities_sold = {'Miss Vickies Original': 0, 'Miss Vikies Salt & Vinegar': 0}
chips_entry_fields = {}
# Start counting rows
row_num = 0
for row, brand in enumerate(chips_stock_levels):
# Create label describing the brand of chips_stock_levels
label = tk.Label(window, text=brand)
label.grid(row=row, column=1)
# Create an entry box to input number of chips_stock_levels in stock
entry = tk.Entry(window)
chips_entry_fields[brand] = [entry]
entry.grid(row=row, column=2)
# Create label to the left of each chip with how many should be ordered
quantity_to_order = tk.IntVar()
chips_entry_fields[brand].append(quantity_to_order)
# quantity_to_order.set()
order_label = tk.Label(window, textvariable=quantity_to_order)
chips_entry_fields[brand].append(order_label)
order_label.grid(row=row, column=3)
# Order button
button = tk.Button(window, text='Order', command=generate_order_quantities)
button.grid(row=3, column=3)
window.mainloop()

How to Check to See Which Listbox Item Was Selected in TkInter

I'm trying to figure out how to see if the user picks certain currencies (EUR'S and CAD'S right now) from a Listbox, but when I try to configure the callback of the buttons, it does nothing. Here's what I'm trying:
if select_option == 1:
global btn_convert
btn_convert.config(command=euro_callback)
elif select_option == 2:
global btn_convert
btn_convert.config(command=cad_callback)
But this doesn't work. This is my list:
select_option = tk.Listbox(window, height=2, selectmode="SINGLE")
select_option.insert(1, "EURO")
select_option.insert(2, "CAD")
Am I doing it wrong? Can't I ask if the user selected the option "EURO" or "CAD" directly in the script?
And all I found when I searched is stuff about regular list, or finding the results of many separate lists (but what I'm trying to figure out is how to get that result and apply a command to a button). When I tried most of these, it just does nothing.
Here's the full code:
# Imports the tkinter module, which is vital for the GUI
import tkinter as tk
# Imports the html module from the lxml module. We'll be getting our exchange rates from https://www.bloomberg.com
from lxml import html
import requests
# Makes the window
window = tk.Tk()
# Titles the window
window.title("Currency Converter")
# Makes the window 275 x 200 pixels
window.geometry("275x200")
# Makes the background crimson
window.configure(bg="#900C3F")
# window.wm_iconbitmap("penny.ico")
# Gets the information from Bloomberg Markets
page_euro = requests.get('https://www.bloomberg.com/quote/EURUSD:CUR')
page_cad = requests.get('https://www.bloomberg.com/quote/USDCAD:CUR')
tree_euro = html.fromstring(page_euro.content)
tree_cad = html.fromstring(page_cad.content)
'''
When the "Convert" button is pressed, it'll get the value from the text
entry where you put in your value you want to convert to (EUROS or CAD
(Canadian Dollars)), and it'll ask itself; "Is the value all numbers? Or does
it have characters too?". If it doesn't have characters, it'll run as normal.
If it DOES have characters, it'll inform you that, like; "Whoops! You can
only put numbers there!"
'''
def euro_callback():
usd_amount = ent_convert.get()
if str(usd_amount).isdigit():
usd_amount = float(ent_convert.get())
# <div class="price">1.****</div>
euro_exchange = tree_euro.xpath('//div[#class="price"]/text()')
euro_exchange = float(str(euro_exchange[0]))
euro_amount = usd_amount / euro_exchange
lbl_amount.config(text="Euro Amount: %.2f€" % euro_amount)
else:
lbl_amount.config(text="Whoops! You can only put numbers there!")
def cad_callback():
cad_amount = ent_convert.get()
if str(cad_amount).isdigit():
usd_amount = float(ent_convert.get())
# <div class="price">1.2652</div>
cad_exchange = tree.xpath('//div[#class="price"]/text()')
cad_exchange = float(str(cad_exchange[0]))
cad_amount = usd_amount / cad_exchange
lbl_amount.config(text="Canadian Dollar amount: %.2f$" % cad_amount)
else:
lbl_amount.config(text="Whoops! You can only put numbers there!")
btn_convert.config(command=callback)
def callback():
selection = select_option.curselection()[0]
if selection == 1:
# euro_callback()
elif selection == 2:
# cad_callback()
# The list of available currencies to convert to
# lbl_usd = tk.Label(window, text="Enter the USD ($) here:", bg="#900C3F", fg="#FFFFFF")
select_option = tk.Listbox(window, height=2, selectmode="SINGLE")
select_option.insert(1, "EURO")
select_option.insert(2, "CAD")
ent_convert = tk.Entry(window)
# A blank label, followed by a button, which has the usd_callback() command
lbl = tk.Label(window, text=" ", bg="#900C3f")
btn_convert = tk.Button(window, text="Convert", command=callback)
# A blank label, followed by a label that outputs the EURO amount you would get
lbl2 = tk.Label(window, text=" ", bg="#900C3f")
lbl_amount = tk.Label(window, bg="#900C3F", fg="#FFFFFF")
# Packs (adds) all the labels, entries and buttons into the window
select_option.pack()
# lbl_usd.pack()
ent_convert.pack()
lbl.pack()
btn_convert.pack()
lbl2.pack()
lbl_amount.pack()
# Loops the window, and starts the program
window.mainloop()
Any help would be appreciated. Thank you!!
Edit:
I figured it out! This is the answer I was looking for:
def callback():
selection = select_option.curselection()[0]
if selection == 0:
# euro_callback()
btn_convert.config(command=euro_callback)
elif selection == 1:
# cad_callback()
btn_convert.config(command=cad_callback)
# The list of available currencies to convert to
select_option = tk.Listbox(window, height=2, selectmode="SINGLE")
select_option.insert(0, "EURO")
select_option.insert(1, "CAD")
Thanks to Novel and Nelson for helping me!!
The problem is that the callback is configured once (ideally), when the program boots. The best solution is to use a callback that checks the listbox and can sort out what to do.
For example:
btn_convert.config(command=callback)
#...
def callback():
selection = select_option.curselection()[0]
if selection == 1:
euro_callback()
elif selection == 2:
cad_callback()

Trying to put drop down list on a code using Tkinter

I'm tring to put some drop down list on a graphic interface I'm building.
I've found the following code for a drop down list, but I'm not able to adapt it to my code.
from Tkinter import *
def print_it(event):
print var.get()
root = Tk()
var = StringVar()
var.set("a")
OptionMenu(root, var, "a","b","c", command=print_it).pack()
root.mainloop()
This is my code, it's quite simple what I've done so far. A menu shows up, it asks for how many (n) components does the users want to enter, and it shows n options to entry. The code above shows 'blank' entrys after you put the desired number of components. I want to replace those three blank entrys with three drop down list.
It's marked when I want to put those dropdown lists.
from Tkinter import *
import Image
import ImageTk
import tkFileDialog
class Planificador:
def __init__(self,master):
master.title("Planificador")
self.frameOne = Frame(master)
self.frameOne.grid(row=0,column=0)
# Logo
self.imgLabel = Label(self.frameOne, image = None)
self.imgLabel.grid(row=0,column=0)
self.img = ImageTk.PhotoImage(file = "logo.png")
self.imgLabel["image"] = self.img
self.botones()
def botones(self):
self.piezastext = Label(self.frameOne, text = " number of components ", justify="center")
self.piezastext.grid(row=1, column=0)
self.entrypiezas = Entry(self.frameOne,width=5)
self.entrypiezas.grid(row=2, column=0)
self.aceptarnumpiezas = Button(self.frameOne,text="Aceptar", command=self.aceptar_piezas,width=8)
self.aceptarnumpiezas.grid(row=6, column=0)
def aceptar_piezas(self):
num_piezas = self.entrypiezas.get()
print num_piezas
self.piezastext.grid_remove()
self.entrypiezas.grid_remove()
self.aceptarnumpiezas.grid_remove()
n = 1;
while n <= int(num_piezas):
self.textopieza = Label(self.frameOne, text = "Pieza", justify="left")
self.textopieza.grid(row=n, column=0)
// INSTEAD THESE 'n' BLANK ENTRYS, I WANT TO PUT 'n' DROP DOWN LISTS
self.entrypiezas = Entry(self.frameOne,width=5)
self.entrypiezas.grid(row=n, column=1)
self.aceptarpiezas = Button(self.frameOne,text="Aceptar",width=8)
self.aceptarpiezas.grid(row=int(num_piezas)+1, column=0)
n += 1
# Main
if __name__ == "__main__":
# create interfacE
root = Tk()
movieApp = Planificador(root)
root.mainloop()
So I want to know how can I put that drop down list on a given frame, frameOnein my case, instead of a full window. Thanks in advance.
I modified your aceptar_piezas function to do what I think you want:
def aceptar_piezas(self):
num_piezas = self.entrypiezas.get()
print num_piezas
self.piezastext.grid_remove()
self.entrypiezas.grid_remove()
self.aceptarnumpiezas.grid_remove()
# Create a list of tuples to hold the dynamically created Optionmenus
# The first item in the tuple is the menu, the second is its variable
self.optionmenus = list()
n = 1
while n <= int(num_piezas):
self.textopieza = Label(self.frameOne, text = "Pieza", justify="left")
self.textopieza.grid(row=n, column=0)
# Variable for the Optionmenu
var = StringVar()
# The menu
menu = OptionMenu(self.frameOne, var, "a","b","c")
menu.grid(row=n, column=1)
# Set the variable to "a" as default
var.set("a")
# Add the menu to the list of Optionmenus
self.optionmenus.append((menu, var))
n += 1
def clicked():
"""This function was made just to demonstrate. It is hooked up to the button"""
for optionmenu in self.optionmenus:
print optionmenu[1].get()
print self.optionmenus
# This button doesn't need to be in the while loop
self.aceptarpiezas = Button(self.frameOne, text="Aceptar", command=clicked, width=8)
self.aceptarpiezas.grid(row=int(num_piezas)+1, column=0)
The tuples in the list are in the order that the Optionmenus were created. So, the first tuple contains the data for the first Optionmenu, the second for the second, and so forth.

Categories

Resources