Automatically updating a value through a OptionMenu tkinter object - python

I would like to write a tkinter app that will automatically update a value based on the current state of the OptionMenu object. Here's what I have so far
from tkinter import *
root = Tk()
def show():
myLabel=Label(root,text=clicked.get()).pack()
clicked=StringVar()
clicked.set("1")
drop = OptionMenu(root,clicked,"1","2","3")
drop.pack()
myButton = Button(root,text="show selection",command=show)
root.mainloop()
In this version, the text can only be updated by clicking a button. How can I make the text update automatically, without this "middle man"?

You can simply assign clicked to the textvariable of the Label, then whenever an option is selected, the label will be updated:
import tkinter as tk
root = tk.Tk()
clicked = tk.StringVar(value="1")
drop = tk.OptionMenu(root, clicked, "1", "2", "3")
drop.pack()
tk.Label(root, textvariable=clicked).pack()
root.mainloop()

After changing some things, i got it working.
It is better to use the config() function to change item's attributes, and another important thing is to not pack() the objects (the Label, in this case) in the same line that the variable declaration.
Like so, you'll be able to change the text. Here is your code updated!
from tkinter import *
def show():
myLabel.config(text = clicked.get())
root = Tk()
clicked=StringVar( value="1")
myLabel=Label(root, text="click the button at the bottom to see this label text changed")
myLabel.pack()
drop = OptionMenu(root, clicked, "1","2","3")
drop.pack()
myButton = Button(root, text="show selection", command=show)
myButton.pack()
root.mainloop()

Related

How to create a simpledialog-like window with a combo with tkinter?

simpledialog or filedialog are widgets very convenient to use.
I would like to do the same :
modal window which pops up on screen like these simpledialogs
combo box inside
and when I select a value in combo, return this value without needing a button
Something like:
def askComboValue():
root = Tk() #how to pops up this window?
label = ttk.Label(root, text = "select your value")
label.pack()
box_value = ''
combo = ttk.Combobox(root, textvariable=box_value, values=['bla', 'bli', 'blo'])
combo.current(0)
combo.pack()
combo.bind("<<ComboboxSelected>>", returnValue) #how to catch this value?
root.grab_set_global() #is it the right way to make it modal?
root.mainloop()
return box_value #how to return this value?
Does anyone know how to deal with it?
Thanks for your help
If the function is called when there is already a tkinter window, then better use Toplevel() instead of Tk(). Also box_value should be instance of StringVar() instead. grab_set() is used instead of grab_set_global() as well.
Below is an example based on your code:
import tkinter as tk
from tkinter import ttk
def askComboValue(*values):
top = tk.Toplevel() # use Toplevel() instead of Tk()
tk.Label(top, text='Select your value').pack()
box_value = tk.StringVar()
combo = ttk.Combobox(top, textvariable=box_value, values=values)
combo.pack()
combo.bind('<<ComboboxSelected>>', lambda _: top.destroy())
top.grab_set()
top.wait_window(top) # wait for itself destroyed, so like a modal dialog
return box_value.get()
def test():
result = askComboValue('bla', 'bli', 'blo')
print(result)
root = tk.Tk()
tk.Button(root, text='Test', command=test).pack()
root.mainloop()

radiobutton get does not work inside a function

I would like to have a window with a single button (with text "Click"). When I click the button a new window should open. And in that second window I would like to have 2 radiobuttons and a button (with text "Print"). Clicking that button ("Print") should print the current value of the variable my_variable, which indicates which Radiobutton is choosen.
The first code is missing the first window, but it prints out the correct values. The second code, where I added the first window (with the button "Click" opening the second window) prints always the default value of the variable my_variable. What should I change to get the current value every time a press the button "Print"?
I use tkinter with Python 3.7.
Working code:
import tkinter as tk
def function():
"""That function print the current value of my_variable, depending on which radiobutton was choosen."""
value = my_variable.get()
print(value)
window = tk.Tk()
window.title("Title")
window.geometry('200x200')
my_variable = tk.IntVar(value=0)
rbtn1 = tk.Radiobutton(window, text='one', value=1, variable=my_variable)
rbtn1.grid(row=0, column=0)
rbtn2 = tk.Radiobutton(window, text='two', value=2, variable=my_variable)
rbtn2.grid(row=1, column=0)
button = tk.Button(window, text="Print", command=function)
button.grid(row=2, column=0)
window.mainloop()
Not working code:
import tkinter as tk
def on_click():
def function():
"""That function shoud print the current value of my_variable, depending on which radiobutton was choosen. But it prints the default value instead."""
value = my_variable.get()
print(value)
window = tk.Tk()
window.title("Title")
window.geometry('200x200')
my_variable = tk.IntVar(value=0)
rbtn1 = tk.Radiobutton(window, text='one', value=1, variable=my_variable)
rbtn1.grid(row=0, column=0)
rbtn2 = tk.Radiobutton(window, text='two', value=2, variable=my_variable)
rbtn2.grid(row=1, column=0)
button = tk.Button(window, text="Print", command=function)
button.grid(row=2, column=0)
window_main = tk.Tk()
window_main.title("Title main")
window_main.geometry('400x400')
button = tk.Button(window_main, text="Click", command=lambda: on_click())
button.grid(row=0, column=0)
window_main.mainloop()
The problem is you're calling tk.Tk() twice. When you want to create another window, use tk.Toplevel() instead.
To avoid needing to do that, just change the one line indicated below:
import tkinter as tk
def on_click():
def function():
"""That function should print the current value of my_variable, depending on which radiobutton was chosen. But it prints the default value instead."""
value = my_variable.get()
print(value)
window = tk.Toplevel() ##### CHANGED.
window.title("Title")
window.geometry('200x200')
my_variable = tk.IntVar(value=0)
rbtn1 = tk.Radiobutton(window, text='one', value=1, variable=my_variable)
rbtn1.grid(row=0, column=0)
rbtn2 = tk.Radiobutton(window, text='two', value=2, variable=my_variable)
rbtn2.grid(row=1, column=0)
button = tk.Button(window, text="Print", command=function)
button.grid(row=2, column=0)
window_main = tk.Tk()
window_main.title("Title main")
window_main.geometry('400x400')
button = tk.Button(window_main, text="Click", command=lambda: on_click())
button.grid(row=0, column=0)
window_main.mainloop()
If you want to understand why you should avoid calling Tk() more than once, see the answer to Why are multiple instances of Tk discouraged?
See:
When you click a button in the main window, it calls on_click.
Inside on_click, you every time reassign the variable value: my_variable = tk.IntVar(value=0).
This is why the value of my_variable is not preserved between clicks of the button in the main window.
You need to keep my_variable outside on_click, and initialize it once, not on every click.
A nice way to do that is to make on_click accept it as a parameter: def on_click(my_variable), and command=lambda: on_click(my_variable).
A quick and pretty dirty way is to use global; it's acceptable in a throwaway script, but will quickly become ugly and hard to reason about.

Get checkbox to create/delete window when checked/unchecked

I want a checkbox that when check, creates a scrolled text widget and when unchecked, removes the widget.
Currently it creates the widget only once I've checked the box and then unchecked it, and then when checked again, it does nothing and when unchecked again, it creates a second widget below.
I've tried different ways of coding it but I can't figure out what I'm doing wrong.
# Creates Normal Checkbutton
chk_state = BooleanVar()
chk_state.set(False) # set check state
chk = Checkbutton(window, text='Normal Entries', var=chk_state)
chk.place(x=0, y=0)
#Checks Checkbutton State
def chk_checked(event):
txt = scrolledtext.ScrolledText(window, height=15, width=35)
if chk_state.get():
txt.insert(END, 'Paste Normal Entries Here...')
txt.pack(anchor='nw', padx=50, pady=50)
elif txt.winfo_exists():
txt.pack_forget()
else:
pass
#Event when checkbox checked
chk.bind('<Button-1>', chk_checked)
You can try as this
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
def chk_checked():
global txt
if chk_state.get():
txt = ScrolledText(window, height=15, width=35)
txt.insert(tk.END, 'Paste Normal Entries Here...')
txt.pack(anchor='nw', padx=50, pady=50)
else:
txt.pack_forget()
window = tk.Tk()
chk_state = tk.BooleanVar()
chk_state.set(False) # set check state
chk = tk.Checkbutton(window, text='Normal Entries', var=chk_state, command=chk_checked)
chk.place(x=0, y=0)
txt = None
window.mainloop()
This isn't the best way for do that, maybe you can create a class, i think that would be better.
The problem with your code is that each time that you click the CheckButton the function chk_checked(event) creates a new ScrolledText and after works on it instead working on the ScrolledText that was created previously. You have to declare a global variable (instead of a local variable) in wich you store the ScrolledText that you want to use and work only with it

How can I pass an argument to a function from a drop down in tkinter GUI

I have a function
def hist(x):
plotly.offline.plot( {
'data' : [{
'type' : 'histogram',
'x' : data[x],
}],
"layout": Layout(title=x)
})
hist("price")#function call
in place of price i want the user to select other options in drop-down of tkinter gui and it should get updated in function call how should i proceed.
and also how to set a background image for a tkinter window which fits all the window irrespective of the dimensions of the window.
This can be done using an OptionMenu widget from tkinter.
Essentially all we need to do is initialise the OptionMenu widget and then call it's StringVar variable.
from tkinter import *
root = Tk()
def command():
print(var.get())
var = StringVar(root)
var.set("Price")
option = OptionMenu(root, var, "Price", "Foo", "Bar")
option.pack()
button = Button(root, text="Ok", command=command)
button.pack()
root.mainloop()
The above will create an OptionMenu and a Button and print the value of the OptionMenu whenever the Button is pressed.
Once you understand the base concepts at play here you can start getting "fancy" with how your inputting information.
We can set up a trace on the StringVar variable and use it to detect when the OptionMenu is updated, meaning we get an automatic response in the program without the user having to press a button after selecting something from the drop down.
from tkinter import *
root = Tk()
def command(*args):
print(var.get())
var = StringVar(root)
var.set("Price")
option = OptionMenu(root, var, "Price", "Foo", "Bar")
option.pack()
var.trace("w", command)
root.mainloop()

Updating a label from an entry field on button push with tkinter in Python 3.5.2

I am trying to create a window with a line label, an entry field, a current value label, and an "Update Value" button.
Here is an example:
This is what I have so far. I can get the entered value to print to console, but I can't seem to work out how to get an entered value and change the currentValue Label to reflect that value by pressing the button:
from tkinter import*
main=Tk()
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
#This kills the program
def exitProgram():
exit()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry=Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue)
currentValue.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValues=Button(text='Set Values',width=30,command=setValues)
setValues.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton=Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
There are a couple of problems with your code.
Firstly, you are overwriting the setValues function with the setValues Button widget, and similarly, you are overwriting the currentValue StringVar with the currentValue Label.
To set a StringVar, you use its .set method.
Don't use plain exit in a script, that's only meant to be used in an interactive interpreter session, the proper exit function is sys.exit. However, in a Tkinter program you can just call the .destroy method of the root window.
Here's a repaired version of your code.
import tkinter as tk
main = tk.Tk()
#StringVar for currentValue in R0C2
currentValue = tk.StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
currentValue.set(content)
#This kills the program
def exitProgram():
main.destroy()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
tk.Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry = tk.Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValueLabel = tk.Label(textvariable=currentValue)
currentValueLabel.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValuesButton = tk.Button(text='Set Values',width=30,command=setValues)
setValuesButton.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton = tk.Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
BTW, it's a Good Idea to avoid "star" imports. Doing from tkinter import * dumps 130 names into your namespace, which is unnecessary and creates the possibility of name collisions, especially if you do star imports from several modules. It also makes the code less readable, since the reader has remember which names you defined and which ones came from the imported module(s).
In my opinion the easiest way to do this would be using an object orientated method. This way you could declare a button with a command that calls a def which runs self.label.configure(text=self.entry.get()).
This can be seen below:
import tkinter as tk
class App:
def __init__(self, master):
self.master = master
self.label = tk.Label(self.master)
self.entry = tk.Entry(self.master)
self.button = tk.Button(self.master, text="Ok", command=self.command)
self.label.pack()
self.entry.pack()
self.button.pack()
def command(self):
self.label.configure(text=self.entry.get())
root = tk.Tk()
app = App(root)
root.mainloop()
The above creates a label, entry and button. The button has a command which calls a def within the class App and updates the value of the label to be the text contained within the entry.
This all works very smoothly and cleanly and more importantly is drastically easier (in my opinion) to read and update in the future.
From your code you are setting the 'currentValue', which is a StringVar:
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
to an object Label further down in your code. You cannot do this!
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue) ** this line is wrong
currentValue.grid(row=0,column=2)
You should name the label something different like:
#Displays what the value is currently set to.
lblCurrentValue = Label(textvariable=currentValue)
lblCurrentValue.grid(row=0,column=2)
Then in your "setValues" method you should use 'StringVar.set(value) to update the label like so:
def setValues():
content = entry.get()
currentValue.set(entry.get())------------------Here I set the value to the entry box value
print(content)
I tend to avoid stringVar and just use:
Label.config(text='*label's text*')
If you need more help I can post you my solution but try and solve it first becasue its the best way to learn. My tip is to make sure you are using correct naming conventions. In tkinter I tend to use lbl..., entryBox... etc before widgets so I know what they are and not to confuse them with variables.

Categories

Resources