OptionsMenu display only first item in each list? - python

Here I have a basic OptionsMenu and a list with lists inside of it for my Menu options. What I need to do is display only the first item of the sub lists in the options menu but when that item is selected to pass the 2nd item in the sub list to a function.
Currently I can display all of the sub list and pass all of the sub list to a function. The main problem I am having is trying to display JUST the first item "ID 1" or "ID 2" in the drop down menu.
import tkinter as tk
root = tk.Tk()
def print_data(x):
print(x[1])
example_list = [["ID 1", "Some data to pass later"], ["ID 2", "Some other data to pass later"]]
tkvar = tk.StringVar()
tkvar.set('Select ID')
sellection_menu = tk.OptionMenu(root, tkvar, *example_list, command=print_data)
sellection_menu.config(width=10, anchor='w')
sellection_menu.pack()
root.mainloop()
This is what I get:
What I want:
As you can see in the 2nd image only the "ID 1" is displayed in the menu and the data for that ID is printed to console.
I cannot find any documentation or post abut this issue so it may not be possible.

The only way I can think about is this
import tkinter as tk
root = tk.Tk()
do a for loop that takes the first index and that is the ID
sellection_menu = tk.OptionMenu(root, tkvar,
*[ID[0] for ID in example_list],
command=print_data)
search for the given index but this is slow and not so good if you are using a lot of data
def print_data(x):
for l in example_list:
if x == l[0]:
print(l[1])
break
example_list = [["ID 1", "Some data to pass later"],
["ID 2", "Some other data to pass later"]]`

Related

how does the Entry widget in tkinter work?

I'm still a beginner with Tkinter and I'm not quite sure how the Entry widget work. I can't seem to get the value I enter I tried binding the root window to this function but I can't figure out why it's not working.
def get_value(event):
current_obj = root.focus_get()
if (current_obj in entries):
text = current_obj.get()
data.append(text)
You can use get to get the value from the entry.
First you define the entry like this:
e = tk.Entry()
e.pack()
Then you can have a static function which gets the value of the entry by calling entry.get()
def get_entry_value(entry)
entry.get()
Alternatively, if you have multiple entries in your app which are all contained in some iterable:
def get_entries(self, event=None):
data = list()
for e in self.entries:
data.append(e.get())
return data

Get a list of all options from OptionMenu

I want to get a list of all options from an OptionMenu widget in tkinter like so:
import tkinter
root = tkinter.Tk()
var = tkinter.StringVar(root)
var.set('OptionMenu')
optionMenu = tkinter.OptionMenu(root, var, 'foo1', 'foo2')
optionMenu.pack()
listOfAllOptions = optionMenu.getOptions()
# listOfAllOptions == ['foo1', 'foo2']
root.mainloop()
Is there a function that achieve that ?
If not what is the workaround?
You can get the menu associated with the optionmenu (eg: optionMenu["menu"]), and with that you can use menu methods to get the items. It takes several lines of code. But honestly, the easiest thing to do is put the values in a list that you attach to the widget (eg: optionMenu.items = the_list_of_values)
If you want to pull the data from the actual widget, you would do something like this:
menu = optionMenu["menu"]
last = menu.index("end")
items = []
for index in range(last+1):
items.append(menu.entrycget(index, "label"))
print "items:", items

Python Tkinter / How to let OptionMenus share one item list?

i'm trying to build multiple option menus sharing the same "base item list". A multiple selection of one item in different menus should not be possible, so all menus have to be updated when an item is selected in one of the available menus.
from tkinter import *
# for example 5 fields
number_of_fields = 5
starting_list = ["item1","item2","item3","item4","item5"]
entry_list = []
option_list = []
option_var = []
def quit():
raise SystemExit()
# if an item is selected in one of the
# menus run this function
def reset_menu(sel_item):
# for each field
for field in range(number_of_fields):
new_list = []
selection = option_var[field].get()
# look for selected items in all menus
# and build new list which contains all
# items from the starting_list minus the
# items which are already selected
# keep the one selected (for a menu itself)
for option in starting_list:
marker = 0
for j in range(number_of_fields):
if(str(option_var[j].get()) == str(option)):
marker = 1
if(marker == 0):
new_list.append(str(option))
else:
pass
if(str(selection) == str(option)):
new_list.append(str(option))
# print new generated item list
# just to be sure it works so far
print("field",field,"new list=",new_list)
# NOW HERE SOMETHING IS WRONG I GUESS
# empty menu
option_list[field]["menu"].delete(0, "end")
# add new menu items
for item in new_list:
option_list[field]['menu'].add_command(label=item, command=lambda value=item:option_var[field].set(value))
root = Tk()
root.title("OptionMenu")
# menu variable for each field
for i in range(number_of_fields):
option_var.append(StringVar(root))
# initial value for each field
for i in range(number_of_fields):
option_var[i].set("")
# create menu for each field
for i in range(number_of_fields):
option_list.append(OptionMenu(root, option_var[i], *starting_list, command=reset_menu))
# create entry for each field
for i in range(number_of_fields):
entry_list.append(Entry(root))
# build gui
for i in range(number_of_fields):
entry_list[i].grid(row=int(i),column=0,sticky=N+S+W+E)
option_list[i].grid(row=int(i), column=1,sticky=N+S+W+E)
button = Button(root, text="OK", command=quit)
button.grid(row=number_of_fields,column=1,sticky=N+S+W+E)
mainloop()
Now everthing seems to be fine until i try to update the menus. The new menu item lists are generated correctly (see print statement) and the menus have the right items, but after selected one menu, the only menu that changes its selected state is the last one. Any ideas?
Regards Spot
I found your question because I too was trying to complete the same task. After doing a bit of poking around in dir(tkinter), I have found a solution, which you have inspired me to create an account to post.
I have left your original comments in the code for sections that I left unchanged.
First, your code for generating your options is unnecessarily cluttered. Instead of manually populating the list from empty, it seems cleaner to remove items from the full list.
You are currently using tkinter.OptionMenu(). If you instead use tkinter.ttk.OptionMenu(), it has a method called set_menu(*values) that takes any number of values as its arguments and sets the choices of that menu to be those arguments.
If you make the switch, there one thing to note - ttk's OptionMenu does not allow its default value to chosen in the dropdown, so it's recommended to make that value blank, as I have done in the declaration for starting_list.
In order to persist the blank option, I added an additional blank option, in order for it to be selectable. This way, if you mistakenly choose the wrong selection, you can revert your choice.
from tkinter import *
from tkinter.ttk import *
# for example 5 fields
number_of_fields = 5
starting_list = ["","item1","item2","item3","item4","item5"]
entry_list = []
option_list = []
option_var = []
def quit():
raise SystemExit()
# if an item is selected in one of the
# menus run this function
def reset_menu(sel_item):
# for each field
for field in range(number_of_fields):
new_list = [x for x in starting_list]
selection = option_var[field].get()
# look for selected items in all menus
# and build new list which contains all
# items from the starting_list minus the
# items which are already selected
# keep the one selected (for a menu itself)
for option in starting_list[1:6]:
#add selectable blank if option is selected
if (str(selection) == str(option)):
new_list.insert(0,"")
for j in range(number_of_fields):
if(str(selection) != str(option) and str(option_var[j].get()) == str(option)):
new_list.remove(option)
# print new generated item list
# just to be sure it works so far
print("field",field,"new list=",new_list)
#set new options
option_list[field].set_menu(*new_list)
root = Tk()
root.title("OptionMenu")
# menu variable for each field
for i in range(number_of_fields):
option_var.append(StringVar(root))
# initial value for each field
for i in range(number_of_fields):
option_var[i].set("")
# create menu for each field
for i in range(number_of_fields):
option_list.append(OptionMenu(root, option_var[i], *starting_list, command=reset_menu))
# create entry for each field
for i in range(number_of_fields):
entry_list.append(Entry(root))
# build gui
for i in range(number_of_fields):
entry_list[i].grid(row=int(i),column=0,sticky=N+S+W+E)
option_list[i].grid(row=int(i), column=1,sticky=N+S+W+E)
button = Button(root, text="OK", command=quit)
button.grid(row=number_of_fields,column=1,sticky=N+S+W+E)
mainloop()
Something you may want to look into is making your option generation a bit more efficient. Right now, for n options, you're looping through your menus n^2 times. I would suggest looking at passing the value that was just selected in the callback instead of searching each menu to see what was previously selected.
As an additional minor note, your "OK" button causes a crash. I'm not sure if that was intentional behavior, a quirk in my system, or something else.
I hope this helps!
its been a while and ive found a possible solution for my problem...here is the code:
from tkinter import *
from tkinter import _setit
# for example 5 fields
number_of_fields = 5
starting_list = ["choose","item1","item2","item3","item4","item5"]
entry_list = []
option_list = []
option_var = []
def quit():
raise SystemExit()
# print entry_field text and selected option_menu item
def output():
print("---------------------------------------")
for nr,item in enumerate(entry_list):
if(item.get() != ""):
print(item.get() + " --> " + option_var[nr].get())
print("---------------------------------------")
# if an item is selected in one of the
# menus run this function
def reset_menu(*some_args):
for field in range(number_of_fields):
new_list = []
selection = option_var[field].get()
for option in starting_list[1:]:
marker = 0
for j in range(number_of_fields):
if(str(option_var[j].get()) == "choose"):
continue
if(str(option_var[j].get()) == str(option)):
marker = 1
if(marker == 0):
new_list.append(str(option))
else:
pass
if(str(selection) == str(option)):
new_list.append(str(option))
option_list[field]["menu"].delete(0, "end")
option_list[field]["menu"].insert(0, "command", label="choose", command=_setit(option_var[field], "choose"))
# add new menu items
for i in range(len(new_list)):
option_list[field]["menu"].insert(i+1, "command", label=new_list[i], command=_setit(option_var[field], new_list[i]))
root = Tk()
root.title("OptionMenu")
# menu variable for each field
for i in range(number_of_fields):
option_var.append(StringVar(root))
# initial value for each field
for i in range(number_of_fields):
# set "choose" as default value
option_var[i].set("choose")
# trace each variable and call "reset_menu" function
# if variable change
option_var[i].trace("w", reset_menu)
# create menu for each field
for i in range(number_of_fields):
option_list.append(OptionMenu(root, option_var[i], *starting_list))
# create entry for each field
for i in range(number_of_fields):
entry_list.append(Entry(root))
entry_list[i].insert(0, "entry"+str(i))
# build gui
for i in range(number_of_fields):
entry_list[i].grid(row=int(i), column=0, sticky=N+S+W+E)
option_list[i].grid(row=int(i), column=1, sticky=N+S+W+E)
button1 = Button(root, text="OK", command=quit)
button2 = Button(root, text="PRINT", command=output)
button1.grid(row=number_of_fields, column=0, sticky=N+S+W+E)
button2.grid(row=number_of_fields, column=1, sticky=N+S+W+E)
mainloop()
This solution also runs under python 2.7, just change "from tkinter ..." to "from Tkinter ...".
Please take a look at the smarter solution sephirothrr has posted (see post above)!
Regards
Spot

How to change the labels of the choices in an option menu?

I have a Tkinter option menu like this:
import Tkinter as tk
choices = [1,2,3]
GUI = tk.Tk()
var2set = tk.IntVar(GUI)
tk.OptionMenu(GUI, var2set, *choices).grid(column=0, row=0)
GUI.mainloop()
Is it possible to change the labels of the choices [1,2,3] to strings like "title 1", "title 2", "title 3" WITHOUT a modification of the variable choices? It is important, that each option in the option menu delivers the chosen integer value from the choices-list.
If I fully understood what you are asking for then
Yes I think dictionary can be used here but as you said you want the contents of choices not to be disturbed then you can find this useful..
import Tkinter as tk
def select():
title=var.get()
print "selected string :"+title
print "corresponing integer value :"+str(choices[Dic[title]])
choices = [1,2,3]
Dic={'title 1':0,'title 2':1,'title 3':2}
GUI = tk.Tk()
var = tk.StringVar(GUI)
var.set('title 1')
op=tk.OptionMenu(GUI, var, *Dic.keys())
op.pack(side='left',padx=20,pady=10)
bt=tk.Button(GUI,text='check value',command=select)
bt.pack(side='left',padx=20,pady=10)
GUI.mainloop()
The indexing is quite messy but may be its exactly the way you want it
Also you can make dictionary of any length(same as of choices) using trivial loops that's too obvious :)

How to say nothing is selected by using curselection in Tkinter

I have a code that uses Listbox.curselection():
self.index = int(self._Listbox.curselection()[0])
I would like to raise an error window when nothing is selected in the Listbox.
Any feedback would be appreciated.
Thanks!
I'm not 100% sure what the problem is. If there are no items selected, the self._Listbox.curselection() should return an emtpy list. Since you then grab index 0, it should throw an IndexError.
Demo Code:
from Tkinter import *
master = Tk()
listbox = Listbox(master)
listbox.pack()
listbox.insert(END, "a list entry")
for item in ["one", "two", "three", "four"]:
listbox.insert(END, item)
def callback():
items = map(int, listbox.curselection())
if(len(items) == 0):
print "No items"
else:
print items
button = Button(master,text="press",command=callback)
button.pack()
mainloop()
Based on the behavior of the code above (nothing selected returns an empty list), your code should throw an IndexError when you don't have anything selected ... now you just need to handle the exception:
try:
self.index = int(self._Listbox.curselection()[0])
except IndexError:
tkMessageBox.showwarning("Oops","Need to select something")
Finally, I suppose I'll leave a link to some documentation on the standard Tkinter dialogs (tkMessageBox module)

Categories

Resources