Tkinter get OptionMenu Option List - python

I'm using Python 3, Tkinter module. I've looked at ttk library and one of the widgets there is an Option Menu. It's great, but I was wondering if there is a way to retrieve the list of options that are currently in use by the menu.
In this example:
Options_List=["option1","option2"]
My_Menu = OptionMenu(master, variable, *Options_List))
I'm aware that it may seem trivial. Just retrieve the Options_List variable. But now let's assume I'm making loads of options (using the same, or different lists):
Options_List=["option1","option2","option3"]
Menu_List = []
for Option in range(3):
My_Menu = OptionMenu(master, variable, *Options_List))
Menu_List.append(My_Menu)
Options_List.del(-1) #removes last item
I just took advantage of the fact that when the Option Menu is assigned, the options are the copy of the Options_List variable, not a reference to it, so when the code is executed, they all refer to their own version of Options_List.
Option output would yield:
Menu_List[0] -> ["option1","option2","option3"]
Menu_List[1] -> ["option1","option2"]
Menu_List[2] -> ["option1"]
Now you can see that I can't just retrieve Options_List, because each Option Menu has its own list to work off.
So, any ideas? Is there any way I can get hold of the list of options that my nth Option Menu is using?

The option menu is nothing more than a standard button with a menu attached to it. So, to get the values in the option menu you simply need to get the menu associated with the optionmenu, and use the methods available on the menu to get the items on the menu.
For example, let's assume that om represents the optionmenu. To get the menu you can do this:
menu = om['menu']
menu now is a reference to a Menu object. You can find out the index of the last item with the index method:
last_index = menu.index("end")
With that, you can iterate over the items in the menu. If you want the label, you can use entrycget to get the value of that attribute:
values = []
for i in range(last_index+1):
values.append(menu.entrycget(i, "label"))
With that, values will contain the values that appear on the menu.

Related

Tkinter optionmenu won't allow me to pass the frame of the object I want to update depending on the choice made

I have a list of frames that each have an optionmenu with the same list of choices. When a choice is made in that specific optionmenu, I've only been able to get the last entry widget to change, not the corresponding one. In other widgets I've been able to use something like "lambda F=F:function(args)" but that isn't working here.
I've tried a trace on the variable in the option menu, I've tried wrapper functions, I've tried every combination of a lambda in the command section of the optionmenu widget. Most approaches create errors, some, like the one attached, modify the bottom frame/entry but not the correct corresponding one.
This doesn't seem like it should be too hard. If the option for the top frame selected is "Continuous" or "Discrete", the entry next to it should be 'normal' state with "?..?" in the box, if it is categorical, it should change to be 'disabled' with no contents. I could do this easily if I could somehow pass the Frame dictionary key to the "updateOnChange" function, but I can't, it only allows a single argument to be passed and that is the string value of mType[F].
from tkinter import *
def updateOnChange(type):
print(type)
if type.upper()=='CATEGORICAL':
rangeEntry[F].delete(0,END)
rangeEntry[F].config(state='disabled')
print("runCat")
else:
rangeEntry[F].config(state='normal')
rangeEntry[F].delete(0,END)
rangeEntry[F].insert(0,'?..?')
print("runCont")
mType={}
frame={}
om={}
rangeEntry={}
root=Tk()
Frames=['FrameOne','FrameTwo']
miningTypes=['Continuous','Categorical','Discrete']
for F in Frames:
mType[F]=StringVar(root)
if F=='FrameOne':
mType[F].set("Continuous")
else:
mType[F].set("Categorical")
frame[F]=Frame(root,borderwidth=3,relief=SUNKEN)
frame[F].pack(side=TOP,fill=X)
rangeEntry[F]=Entry(frame[F],width=20,font=("Arial",12))
om[F]=OptionMenu(frame[F],mType[F],*miningTypes,command=updateOnChange)
om[F].pack(side=LEFT)
rangeEntry[F].pack(side=LEFT)
mainloop()
``
Your updateOnChange function hard-coded the entry to be changed as rangeEntry[F], which points to the last Entry widget created in your for loop. To properly associate each entry, you should pass the widget as a parameter:
def updateOnChange(type, entry):
if type.upper()=='CATEGORICAL':
entry.delete(0,END)
entry.config(state='disabled')
print("runCat")
else:
entry.config(state='normal')
entry.delete(0,END)
entry.insert(0,'?..?')
print("runCont")
And then pass the parameter in your command:
om[F]= OptionMenu(frame[F],mType[F],*miningTypes,command=lambda e, i=rangeEntry[F]: updateOnChange(e, i))

Adding check button in dropdown menu/popup menu of tkinter with dynamic input coming in form of list

So I am using xlrd module to read names of excel files of a workbook that user selects. Now this comes up in form of a list, which I am able to put in popup menu drop down(able to select on name at a time) now I want to
So far I am able to use the drop down menu of tkinter with dynamic input but able to select just one name at a time, now I want to select multiple items at a time by adding checkbox to the same. Can anyone help.. Here is sample code on which you anyone may please try adding checkbox and select multiple names at a time.
from tkinter import *
root = Tk()
a = [] #creates a list to store the job names in
var = StringVar() # creates a stringvar to store the value of options
for i in range(20): # fills list with nonsense jobs for troubleshooting
a.append("Job Name "+str(i))
var.set(a[0]) # sets the default option of options
options = OptionMenu(root, var, *a)
# creates an option menu populated with every element of the list
button = Button(root, text="Ok", command=lambda:print(var.get()))
# prints the current value of options
options.pack()
button.pack()
The above code shows a drop down with single select able item, I just want multiple selection functionality enabled by adding checkbox in front of every option
Use the MenuButton feature in tkinter. It has a method called Menu. To this method you could add as many check buttons as you want. This will support multiselect dropdown option. Give a variable name to each checkbutton and access its state through the get command.

How to create a combobox that includes checkbox for each item?

Fairly new to tkinter and python I was wondering how to achieve a button that would act like this :
Click on button drops down a list (so that's a combobox)
Each line of the list has a checkbox.
Finally if a checkbox is clicked run a function, or (even better) once combobox is no more dropped run a function with items checked as args.
UPDATE
The button/menuButton will have to act like a filter. When menu is dropped down user can uncheck multiple options (without the menu to disappear each time an item is clicked) he don't want. Therefore it's really important to be able to see checkboxes so as the user know which options are currently active.
I finally used the idea of Bryan by creating a top level frame. Here is what I have :
There is no widget to do what you want. You'll have to create a toplevel window with a bunch of checkbuttons. You can then trigger the appearance with a normal button.
I don't think the OptionMenu is intended to hold anything but strings. It sounds like you want the functionality of a Listbox, which has options to allow for multiple selections, get all selected items, and so on.
This gives you an OptionMenu with checkboxes in the contained Menu. Check whichever items you like, then right-click in the tkinter window to print the values of the checkboxes to the console.
from tkinter import *
master = Tk()
var = StringVar(master)
var.set("Check")
w = OptionMenu(master, variable = var, value="options:")
w.pack()
first = BooleanVar()
second = BooleanVar()
third = BooleanVar()
w['menu'].add_checkbutton(label="First", onvalue=True,
offvalue=False, variable=first)
w['menu'].add_checkbutton(label="Second", onvalue=True,
offvalue=False, variable=second)
w['menu'].add_checkbutton(label="Third", onvalue=1,
offvalue=False, variable=third)
master.bind('<Button-3>', lambda x: print("First:", first.get(), " Second:",
second.get(), " - Third:", third.get()))
mainloop()
See also this.

Make Tkinter.Listbox selection persist

I have a program where I need to take a selection from Tkinter.Listbox and an entry field and do something with that data. However, if I highlight any text within the entry field (i.e., to delete previous entry), the Listbox selection is cleared. How can I overcome it so that the Listbox selection persists?
import Tkinter as tk
master = tk.Tk()
listbox = tk.Listbox(master)
listbox.grid(row=0, column=0)
items = ['a', 'b', 'c']
for item in items:
listbox.insert(tk.END, item)
efield = tk.Entry(master)
efield.grid(row=1, column=0)
tk.mainloop()
Steps to reproduce:
Type something in the entry field.
Select something in the listbox.
Highlight whatever you entered in the entry field => selection in the listbox gets cleared.
This related question with similar issue How to select at the same time from two Listbox? suggests to use exportselection=0, which doesn't seem to work for me. In such case selection = listbox.selection_get() throws an error while the right line is still highlighted.
I know this is an old question, but it was the first google search result when I came across the same problem. I was seeing odd behavior using 2 list boxes when using selection_get() and also the selection persistence issue.
selection_get() is a universal widget method in Tkinter, and was returning selections that were last made in other widgets, making for some really strange behavior. Instead, use the ListBox method curselection() which returns the selected indices as a tuple, then you can use the ListBox's get(index) method to get the value if you need.
To solve the persistence issue, set exportselection=0 when instantiating the ListBox instance.
list_box = tk.Listbox(master, exportselection=False)
...
selected_indices = list_box.curselection()
first_selected_value = list_box.get(selected_indices[0])
As for now, I wasn't able to cleanly overcome the problem. One way around is to create a variable which will store the selected list value on click:
selected = None
def onselect(e):
global selected
selected = listbox.selection_get()
listbox.bind('<<ListboxSelect>>', onselect)
This doesn't keep the highlight, but the selection is now stored in a variable and can be used further.

Dynamically modifying/refreshing menu contents in PyGTK

I am trying to implement a list of recently opened items in the menu of a GUI I am writing with PyGTK. I initialize the menu like so:
self.filemenu = gtk.Menu()
self.init_file_menu()
self.fileitem = gtk.MenuItem("File")
self.fileitem.set_submenu(self.filemenu)
menubar = gtk.MenuBar()
menubar.append(self.fileitem)
outerframe.pack_start(menubar, False, False)
def init_file_menu(self):
for widget in self.filemenu.get_children():
self.filemenu.remove(widget)
openitem = gtk.MenuItem("Open")
self.filemenu.append(openitem)
openitem.connect("activate", self.open_file)
self.filemenu.append(gtk.SeparatorMenuItem())
for recentitem in self.settings['recentfiles']:
item = gtk.MenuItem(os.path.basename(recentitem))
self.filemenu.append(item)
item.connect("activate", self.open_file, recentitem)
self.filemenu.show()
self.settings['recentitems'] is a deque that is modified when opening a file; init_file_menu is then called again. My goal is to empty the menu, then repopulate it with the right items in the right order. This code works fine when populating the menu at startup. But for some reason while the calls to Menu.remove all work fine and empty the menu, the append calls do not re-add the items to it and it remains empty. If I call its get_children method, I see that they are all there internally, but the interface has not been updated to reflect this. How do I refresh the displayed menu and make the recent items list work as expected?
I would also accept direction on how to use the RecentChooserMenu widget if that is what I am looking for.
Since gtk.Menu is a container, you need to .show every item after adding it to menu (Because you don't want to call menu.show_all() shortcut which shows the menu itself), so you do item.show() just after menu.append(item)

Categories

Resources