Create cascading Listbox in Tkinter - python

The question is simple. I would like to create a drop menu of list of elements calling back the item selected using Tkinter. Is that possible?

#It is possible! It's actually called an OptionMenu in Tkinter.
window = Tk()
clicked = StringVar()
clicked.set("Choose Option")
drop_menu = OptionMenu(window, clicked, "option1", "option2", "option3", "option4")
drop_menu.pack()
window.mainloop()

Related

Python tkinter - radio buttons, how to change outcome according to user selection multiple times?

So in my code I have a menu of radio buttons. I've assigned them to display a string of text depending on what radio button is selected. However, how can I make it dynamic to where, if the user changes their mind after selecting one option, the outcome changes?
For example, let's say my menu options are:
-blue
-red
-yellow
User selects blue. Then the text "blue" displays as an outcome of that selection. But now, the user changes their selection to red instead. If they now click on the "red" option from the menu, how can I sort of undo the previous text and display the new text in its place instead?
I hope my question makes sense.
You should create the result label once and set textvariable option to the same as the tkinter variable used for those radiobuttons. Then every time you select one of the radiobuttons, the result label will be updated.
Below is a simple example:
import tkinter as tk
root = tk.Tk()
root.geometry("200x100")
menubar = tk.Menu(root)
root.config(menu=menubar)
menu = tk.Menu(menubar, tearoff=0)
var1 = tk.StringVar()
for color in ("blue", "red", "yellow"):
menu.add_radiobutton(label=color, variable=var1, value=color)
menubar.add_cascade(label="Color", menu=menu)
tk.Label(root, textvariable=var1, font="Arial 32 bold", width=10).pack()
root.mainloop()

How do I get an attribute from a Combobox and put it into a variable?

I'm making a project in Tkinter Python and I want users to select an attribute from a Combobox widget and press a button and that attribute will be stored in a variable. I've searched all over the web, but I can't make heads or tails of the code and have no idea how to store this attribute. Can someone tell me how to do this
I've tried the .get thing... (module? widget?) but that is not working and as I said, the internet ain't no help.
This is my basic code with the window and the Combobox:
from tkinter import *
from tkinter import ttk
master = Tk()
ver = ttk.Combobox(master, state="readonly", values=["test1", "test2"]).pack()
Button(master, text="Run").pack()
master.mainloop()
I want to be able to store the selected item in the Combobox and put it in a variable.
pack returns None if you want to assign to a variable, you must do it on a separate line.
If you want action, Button requires a command key word arg to which you assign a callback.
After you have fixed the mistakes, you can use the get method on the Combobox:
import tkinter as tk
from tkinter import ttk
def print_selected():
print(combo.get())
master = tk.Tk()
combo = ttk.Combobox(master, state="readonly", values=["test1", "test2"])
combo.pack()
tk.Button(master, text="Run", command=print_selected).pack()
master.mainloop()

Python/Tkinter OptionMenu Update creates a "Shadow" of the Menu

So I'm still quite new to this (my code should make this obvious) and I am working on a GUI with tkinter.
I am trying to have an OptionMenu which shows Keys from a dict and and upon clicking on a key I would like to see the value.
I want to modify that dict and would like to be able to update said OptionMenu.
So far, so good. Now I've been able to get this to "work" - but when I update the menu (after changing it or not) I get a shadow of the menu itself.
I've conconcted a small test program:
import tkinter as tk
from tkinter import ttk
class MyApp():
def __init__(self,master):
self.master = master
self.myDict = {'Key1':1, 'Key2': 2, 'Key3':3}
self.valueVar = tk.StringVar()
self.valueVar.set("0.00")
self.RCS = tk.Label(master, textvariable=self.valueVar).grid(row=5, column=3)
updateButton = tk.Button(text= "Update List", command = self.update)
updateButton.grid(row=4,column=4)
changeButton = tk.Button(text= "Change list", command = self.changeDict)
changeButton.grid(row=5,column=4)
self.keyVar = tk.StringVar(master)
self.om = ttk.OptionMenu(self.master, self.keyVar, "Select Key ", *self.myDict, command = self.setKey )
self.om.configure(width=20)
self.om.grid(row=4, column=3)
def setKey(self,Surface):
self.valueVar.set(self.myDict[Surface])
def update(self):
menu = self.om["menu"]
menu.delete(0,"end")
menu.destroy
menu = ttk.OptionMenu(self.master, self.keyVar, "Select Key", *self.myDict, command = self.setKey )
menu.grid(row=4, column=3)
def changeDict(self):
self.myDict = {'Key4':4, 'Key5': 5, 'Key6':6}
root = tk.Tk()
app = MyApp(root)
root.mainloop()
What do I have to change? Why?
Usually I work with Matlab. I guess it shows.
Much appreciated!
As far as I understand this mini-program you which for it to display an optionmenu with the keys of a dict and then when you press "change dict" and then update it should switch the optionmenu to the other set of items? In this case, you were calling the destroy on the wrong widget. The only issue was with the update function which should be changed to:
def update(self):
#menu = self.om["menu"]
#menu.delete(0,"end")
#The above two menu items are not neede dif you are just going to destroy the widget
self.om.destroy()
self.om = ttk.OptionMenu(self.master, self.keyVar, "Select Key", *self.myDict, command = self.setKey )
self.om.configure(width=20)
self.om.grid(row=4, column=3)
This will do what I think you want it to do. Just so you know, the optionmenu widget is actually a combination of a button and a menu widget. So when you do menu = self.om["menu"], you are actually getting the menu object of the optionmenu widget and then destroying that. Then you are replacing that variable with an optionmenu and losing the original menu whilst not destroying the original optionmenu (self.om). This is why you got the shadow. Some other notes though:
There is no need for the user to have to press 2 buttons when switching lists - call the update function from the changedict function so that it automatically populates.
You can iterate over the menu items as you began to do but you need to decide on one or the other. I can't comment on the efficiency of completely destroying and enabling the widget or changing the menu items of the current widget as I am still rather experienced.
Hopefully that helps!

How to create entry inputs in a toplevel window

i have a problem and can't get my head around it. How to create a child window on pressing a button using tkinter in python were I can entry values like for example:
import tkinter
root = Tk()
Button(root, text='Bring up Message', command=Window).pack()
root.mainloop()
def messageWindow():
win = Toplevel()
-------->calculate------
Label(win, text=message).pack()
Button(win, text='OK', command=win.destroy).pack()
and on the message window i would like to have two entry fields were I can enter a and b and afterwards it should calc a+b and give me the result.
Thank you.
First, you should use from tkinter import * since there isn't a tkinter. preceding the module's classes used in your script.
Also, is your "Bring up Message" button supposed to call the messageWindow() function? Right now it's calling an undefined function Window. If so, you should change the Button's command and move your messageWindow() function above the line where you created the button or else it will call the function before it is defined and generate an error.
The syntax of an Entry widget in Tkinter goes as follows:
entry = Entry(root, *options)
entry.pack()
You need to pack() the entry widget after you define it. You won't be able to retrieve the input inside it if you pack() it on the same line as you define it as it will become a NoneType object.
You will need at least two Entry widgets, one to enter input a and one to enter input b.
You can also add a third Entry to print the result of the sum of a and b to, though you can use a label or just print it to the console.
entry_a = Entry(win)
entry_a.pack()
entry_b = Entry(win)
entry_b.pack()
# Optional answer entry
entry_ans = Entry(win)
entry_ans.pack()
You should then create a function (still within the messageWindow() function) that will retrieve the input from the two entries and add them, as well as another Button to call that function. I implemented some additional error-checking in the form of a try-except for when the entries are blank or contain something other than integers:
def add():
try:
a = int(entry_a.get())
b = int(entry_b.get())
ab_sum = a + b
# Optional printing to answer entry
entry_ans.delete(0, 'end')
entry_ans.insert(0, ab_sum)
except:
pass
Button(win, text="Add", command=add).pack()
"How to create entry inputs in a toplevel window"
import tkinter as tk
...
toplevel = tk.Toplevel(...)
tk.Entry(toplevel)
"How to create a child window on pressing a button..."
import tkinter as tk
...
def create_child_window(widget):
tk.Toplevel(widget)
...
root = tk.Tk()
tk.Button(root, command=lambda w = root: create_child_window(w))

Keep a menu open in Tkinter

I want to keep a menu cascade open, after a command button within the cascade is clicked. So it basically only closes when the user clicks anywhere else (like it would normally too). Can't seem to find a proper option or a method to open said menu in the callback. The invoke() function only works on buttons wihtin the cascade right? How would you go about that?
Yes, I know this was asked a long time ago, but I was curious if there was any way to accomplish this with tkinter, so I fiddled about for a while and figured out how to do it. I was unable to come up with a way to properly place the persistent menu where it was when it originally opened, but I have managed to make it persist in any location you request (I use upper-left corner of root window). And yes, I know this isn't a nice proper class based implementation, but I was just going for as simple a test as I could write without obscuring it with too many extraneous details.
try:
from tkinter import *
from tkinter.ttk import *
except:
from Tkinter import *
from ttk import *
root = Tk()
var = StringVar()
def menu_click(menu, item):
global root
var.set(item)
menu.post(root.winfo_rootx(), root.winfo_rooty())
root.option_add('*tearOff', False) # remove tearoff from all menus
Label(root, textvariable=var).pack() # just to give menu clicks some feedback
root.geometry('400x300')
menubar = Menu(root)
root['menu'] = menubar
menu_test = Menu(menubar)
menubar.add_cascade(menu=menu_test, label='Test')
menu_test.add_command(label='One', command=lambda: menu_click(menu_test, 'One'))
menu_test.add_command(label='Two', command=lambda: menu_click(menu_test, 'Two'))
menu_test.add_command(label='Three', command=lambda: menu_click(menu_test, 'Three'))
menu_cas = Menu(menu_test)
menu_test.add_cascade(menu=menu_cas, label='Four')
menu_cas.add_command(label='One', command=lambda: menu_click(menu_cas, 'Fourty One'))
menu_cas.add_command(label='Two', command=lambda: menu_click(menu_cas, 'Fourty Two'))
menu_cas.add_command(label='Three', command=lambda: menu_click(menu_cas, 'Fourty Three'))
root.mainloop()

Categories