Updating Label text after OptionMenu selection changes - python-3.x

My objective is to update the contents of the label price, every time that a new item in option menu w is selected. This is my code so far, but it is returning errors that I am not sure how to fix.
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
Label(master, text="Ore:").grid(row=0)
Label(master, text="Price:").grid(row=1)
self.price = Label(master, text="0.00").grid(row=1, column=1)
variable = StringVar(master)
variable.set("Select an ore") # default value
def displayPrice(self):
self.price = orePrice[self.w.get()]
self.w = OptionMenu(master, variable, *orePrice, command=displayPrice).grid(row=0, column=1)
# here is the application variable
self.contents = StringVar()
# set it to some value
self.contents.set("this is a variable")
# tell the entry widget to watch this variable
#self.w.bind('<Button-1>', )
You can assume that:
orePrice = {'Gold': 300, 'Silver': 50, 'Bronze': 10} # etc... you can add more if you feel like it.
I'm a newbie at Python GUI, hence the messy and/or badly written code.

I ammended your code. Now whenever you change ore type, the price field is updated:
from tkinter import *
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
Label(master, text="Ore:").grid(row=0)
Label(master, text="Price:").grid(row=1)
self.priceVar = StringVar()
self.priceVar.set("0.00")
self.price = Label(master, textvariable=self.priceVar).grid(row=1, column=1)
self.orePrice = {'Gold': 300, 'Silver': 50, 'Bronze': 10}
variable = StringVar(master)
variable.set("Select an ore") # default value
self.w = OptionMenu(master, variable, *self.orePrice, command=self.displayPrice).grid(row=0, column=1)
# here is the application variable
self.contents = StringVar()
# set it to some value
self.contents.set("this is a variable")
# tell the entry widget to watch this variable
#self.w.bind('<Button-1>', )
def displayPrice(self, value):
self.priceVar.set(self.orePrice[value])
root = Tk()
app = App(root)
root.mainloop()

Related

How to get the values of all the OptionMenu widgets within a frame inside a canvas in Tkinter

I'm writing a minimalist image tagging app that will list out all the image files in a specific location alongside a dropdown menu to select the options for tagging the image. Once the images are tagged, I need to save the changes to a JSON file and I've got a button for that. How can we read all the options selected so that it can be written into a file?
Following is the code so far:
from tkinter import N, RIGHT, Button, OptionMenu, Scrollbar, StringVar, Tk, Canvas, Frame, Label
class App:
def __init__(self):
self.root = Tk()
self.tags = ['Apples', 'Oranges', 'Berries']
self.GetRows()
self.SaveButton()
self.root.mainloop()
def GetRows(self):
self.canvas = Canvas(self.root)
self.scroll_y = Scrollbar(self.root, orient="vertical", command=self.canvas.yview)
self.frame = Frame(self.canvas)
lst = [f"A01{str(i)}.JPG" for i in range(100)]
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# put the frame in the canvas
self.canvas.create_window(0, 0, anchor='nw', window=self.frame)
# make sure everything is displayed before configuring the scrollregion
self.canvas.update_idletasks()
self.canvas.configure(scrollregion=self.canvas.bbox('all'),
yscrollcommand=self.scroll_y.set)
self.canvas.pack(fill='both', expand=True, side='left')
self.scroll_y.pack(fill='y', side='right')
def SaveState(self):
pass
def SaveButton(self):
self.save_button = Button(self.root, text="Save Changes", padx=50, pady=10, command=self.SaveState)
self.save_button.pack(side=RIGHT)
if __name__ == '__main__':
App()
The SaveState method is what will be used to write the selections so far into a file.
Thanks in advance!
In order to make OptionMenu results available try modifying your code so that
all StringVars are accessible outside of GetRows.
def GetRows(self):
...
# Define label as a list
self.label = []
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# Save StringVar reference
self.label.append(label)
...
def SaveState(self):
self.data = dict()
# Retrieve results into dictionary
for i, a in enumerate(self.label):
self.data[f"A_{i}"] = a.get()
print(self.data)
Then use json.dump(self.data, a_file) to save it

Tkinter - label doesn't update

This morning I started to work on a little app so I can understand better TkInter.
I didn't get too far because I can't make the label to update after I press a button.
It seems like after I create an instance of the Label object I can't work on it anymore because of the mainloop() I guess? I tried to use .update() and other ways to make this work but I can't figure it out. If I add () to file_explorer method, the label updates but I can't open the file explorer anymore and it also starts the file explorer without pressing the button so it's pointless. Found something here on StackOverflow but still nothing.
from tkinter import *
from tkinter import filedialog as fd
import os
# Main_window
App = Tk()
App.geometry("300x300")
App.resizable(0, 0)
filename = "empty"
class Btn:
def __init__(self, master, pos_x, pos_y, label):
frame = Frame(master)
frame.pack()
self.Button = Button(master, text=label, command=self.file_explorer)
self.Button.place(x=pos_x, y=pos_y)
def file_explorer(self):
global filename
filename = fd.askopenfilename(filetypes=(('text files', '*.txt'), ('All files', '*.*')))
filename = os.path.basename(filename)
class FileLabel:
def __init__(self, master, pos_x, pos_y):
global filename
frame = Frame(master)
frame.pack()
self.label1 = Label(master, text=filename)
self.label1.place(x=pos_x, y=pos_y)
e = Btn(App, 10, 10, "Browse file")
f = FileLabel(App, 90, 12)
App.mainloop()
Updating filename will not update the label automatically. However, you can use StringVar instead of normal string and textvariable option of Label to achieve the goal:
...
filename = StringVar(value="empty")
class Btn:
def __init__(self, master, pos_x, pos_y, label):
frame = Frame(master)
frame.pack()
self.Button = Button(master, text=label, command=self.file_explorer)
self.Button.place(x=pos_x, y=pos_y)
def file_explorer(self):
fname = fd.askopenfilename(filetypes=(('text files', '*.txt'), ('All files', '*.*')))
# update filename
filename.set(os.path.basename(fname))
class FileLabel:
def __init__(self, master, pos_x, pos_y):
frame = Frame(master)
frame.pack()
self.label1 = Label(master, textvariable=filename) # used textvariable instead
self.label1.place(x=pos_x, y=pos_y)
...

In application addition of new tkinter modules and the ability to call on the entries from them

The following is in reference to a python3 tkinter GUI as an interface for a mySQL database.
I'd like to know if it's possible to add additional labels and entries inside the application via a button.
The below is my code:
class Application(tk.Frame):
def __init__(self, master=None,x=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
self.xy=x
def create_widgets(self):
self.label1=tk.Label(self, text="Athlete")
self.label1.grid(row=2)
self.Athlete=tk.Entry(self)
self.Athlete.grid(row=2, column=1)
self.button1=tk.Button(self,text='Commit')
self.button1['command']=self.Commit
self.button1.grid(row=3,column=0,columnspan=2)
self.label2=tk.Label(self,text="Program")
self.label2.grid(row=4,column=5)
self.Exercise=tk.Label(self,text="Exercise")
self.Exercise.grid(row=5,column=2)
self.Weight=tk.Label(self,text="Weight")
self.Weight.grid(row=5,column=3)
self.Sets=tk.Label(self,text="Sets")
self.Sets.grid(row=5,column=4)
self.Reps=tk.Label(self,text="Reps")
self.Reps.grid(row=5,column=5)
self.label4=tk.Label(self, text="Exercise 1")
self.label4.grid(row=7,column=1)
self.entry1=tk.Entry(self)
self.entry1.grid(row=7,column=2)
self.Weight1=tk.Entry(self)
self.Weight1.grid(row=7,column=3)
self.Sets1=tk.Entry(self)
self.Sets1.grid(row=7,column=4)
self.Reps1=tk.Entry(self)
self.Reps1.grid(row=7,column=5)
def Commit(self):
Weight=int(self.Weight1.get())
Sets=int(self.Sets1.get())
Reps=int(self.Reps1.get())
Volume=(Weight*Sets*Reps)
print(Weight, Sets, Reps, Volume)
root=tk.Tk()
app=Application(master=root)
app.mainloop()
What I'd like to have is another button that will populate the next row with identical labels and entries allowing the user to customize how many exercises they are entering.
Following on from that, I'll then need a way to call these new entries for the .get() function.
What isn't ideal is: a) hard coding the quantity of entry fields b) having only one entry field
The idea is that the user can enter as many exercises as they want, use the "commit" button and it will sent it to a mySQL database.
Any other additional feedback is appreciated.
Thank you
In my example I create all common GUI elements in the methodcreate_top().
To keep track of the exercise number = grid row I created the variable self.exercise_number. Then I create each row of entrys in create_widgets().
Since I don't need to access these widgets after commit is pressed I re-use the references to each widget.
Each row it processed by Commit() when I press the commit button.
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None, x=None):
super().__init__()
self.master = master
self.pack(padx=10, pady=10) # Added padding
self.exercise_number = 0 # Keeping track of grid row
self.create_top()
self.create_widgets()
def create_top(self):
# Create common GUI elements
tk.Label(self, text="Athlete").grid(row=2)
self.Athlete = tk.Entry(self)
self.Athlete.grid(row=2, column=1)
self.button1 = tk.Button(self, text='Commit')
self.button1['command'] = self.Commit
self.button1.grid(row=3, column=0, columnspan=2)
tk.Label(self,text="Program").grid(row=4, column=5)
tk.Label(self,text="Exercise").grid(row=5, column=2)
tk.Label(self,text="Weight").grid(row=5, column=3)
tk.Label(self,text="Sets").grid(row=5, column=4)
tk.Label(self,text="Reps").grid(row=5, column=5)
def create_widgets(self):
# Create new line of input fields
self.exercise_number += 1
row = 6 + self.exercise_number # Calculate where to grid entrys
self.label4 = tk.Label(self, text=f"Exercise {self.exercise_number}")
self.label4.grid(row=row, column=1)
self.Exercise = tk.Entry(self)
self.Exercise.grid(row=row, column=2)
self.Weight = tk.Entry(self)
self.Weight.grid(row=row, column=3)
self.Sets = tk.Entry(self)
self.Sets.grid(row=row, column=4)
self.Reps = tk.Entry(self)
self.Reps.grid(row=row, column=5)
def Commit(self):
Weight = int(self.Weight.get())
Sets = int(self.Sets.get())
Reps = int(self.Reps.get())
Volume = (Weight*Sets*Reps)
print(Weight, Sets, Reps, Volume)
self.create_widgets() # Add new row
root = tk.Tk()
app = Application(master=root)
app.mainloop()

How can I print the values of a Entry() in tkinter?

Im just simply trying to print the values of the entry fields and it tells me its not defined. I been looking online for a long while now. The lastest thing that I tried was adding self as a parameter of add_student but that didn't work.
This is the code
from tkinter import *
class window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.widgets()
def add_student(self):
print(f"Student's name (first, last): {self.fname_textbox}, {self.lname_textbox}")
def widgets(self):
self.master.title("Student Test Score & Grade")
self.master.minsize(200,200)
""" Labels """
# First name
self.fname_label = Label(root, text='First Name: ')
self.fname_label.grid(row=0, column=0, padx=5, pady=5)
# Last Name
self.lname_label = Label(root, text='Last name: ')
self.lname_label.grid(row=1, column=0, padx=5, pady=5)
""" Entry boxes """
# First Name
self.fname_textbox = Entry(root, width=30)
self.fname_textbox.grid(row=0, column=1, padx=5)
# Last name
self.lname_textbox = Entry(root, width=30)
self.lname_textbox.grid(row=1, column=1, padx=5)
""" Buttons """
# Add Button
self.add_btn = Button(root, text="Add Student", command=self.add_student).grid(row=4, column=2, padx=2, pady=2)
if __name__=="__main__":
root = Tk()
root.resizable(width=False, height=False)
app = window(root)
root.mainloop()
It prints this
Student's name (first, last): .!entry, .!entry2
Instead of: Student's name (first, last): John, Doe
What does this mean?
You need to get the value of the entries in order to use them in your print statement
You do this by putting:
firstname = self.fname_textbox.get()
lastname = self.1fname_textbox.get()
before you print and using these values in your print statement.
Ah, Try something like this:
def __init__(self, master):
self.nameEntry = Entry(master)
self.contents = StringVar()
self.nameEntry["textvariable"]=self.contents
self.nameEntry.pack()
self.nameEntry.grid(row=0,column=1)
self.buttonSave = Button(master,text="Save",command=self.save).grid(row=9,columnspan=2, sticky=W+E+N+S)
def save(self):
print self.nameEntry.get()

Setting label value from OptionMenu in tkinter

I would like to set a label value to the value of the current optionmenu value. If the latter changes I want the former to change too. My issue is that this gui elements are defined in separate classes (and I want them to be like that), but I do not know how to connect them together. Without classes I know I can use the OptionMenu's command method to set the value of the Label. But putting them into Frame containers I am stuck.
Here is a simplistic and functioning code what I want to resolve:
from tkinter import *
root = Tk()
opt=['Jan', 'Feb', 'March']
class MyOptMenu(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack()
self.var = StringVar(self)
self.var.set(opt[0])
self.om = OptionMenu(self, self.var, *opt)
self.om.pack(side=TOP)
self.var.trace('w', self.getValue)
def getValue(self, *args):
return(self.var.get())
class MyLabel(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack()
self.labstring = StringVar(self)
self.lab = Label(self, textvariable = self.labstring, bg='white')
self.lab.pack(side=TOP)
self.labstring.set('hello')
a = MyOptMenu(root)
b = MyLabel(root)
root.mainloop()
Could you give me some help how to proceed. Many thanks.
According to #j_4321's suggestion, here I post the solution that resolved my issue. I provide explanation in comments in between code lines.
from tkinter import *
root = Tk()
opt=['Jan', 'Feb', 'March']
var = StringVar(root) # initialization of a common StringVar for both OptionMenu and Label widgets
class MyOptMenu(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack()
var.set(opt[0]) # give an initial value to the StringVar that will be displayed first on the OptionMenu
self.om = OptionMenu(self, var, *opt)
self.om.pack(side=TOP)
var.trace('w', self.getValue) # continuously trace the value of the selected items in the OptionMenu and update the var variable, using the function self.getValue
def getValue(self, *args):
return(var.get()) # return the current value of OptionMenu
class MyLabel(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack()
self.lab = Label(self, textvariable = var, bg='white') # use the same StringVar variable (var) as the OptionMenu. This allows changing the Label text instantaneously to the selected value of OptionMenu
self.lab.pack(side=TOP)
a = MyOptMenu(root)
b = MyLabel(root)
root.mainloop()

Resources