How to disable a tkinter OptionMenu - python

I can't figure out or find how to disable a tkinter OptionsMenu. I have 3 optionsmenu's in my GUI and want to disable them when a button is clicked
self.menu = OptionMenu(self, var, *items)
btn = Button(self, text="disable", command = self.disable)
btn,pack()
self.disable(self):
//Disable menu here...
Is there a way to just call a built in function for OptionMenu and disable it? Or do I have to disable every option in the menu? (Which i also can't figure out)
BTW: I used the menu.pack() for a separate Topleve() window that pops up, but I started off with the grid() system in my main Tk window, used by menu.grid(row=0,column=0)
EDIT:
So I forgot to mention that I have multiple OptionMenus being generated by a constructor method. This is what I tried doing and didn't work:
makeMenu():
menu = OptionMenu(self, var, *items)
....//whole bunch of menu settings
return menu
menu1 = makeMenu()
all_menus.append(menu)
Now the reason this didn't work is because I had to append it after creation. I don't know why the settings don't carry over, but what I had to do is this:
makeMenu():
menu = OptionMenu(self, var, *items)
....//whole bunch of menu settings
return menu
makeMenu():
menu = OptionMenu(self, var, *items)
....//whole bunch of menu settings
all_menus.append(menu)
makeMenu()
And with this change, I can use this to disable menus later on:
for menu in all_menus:
menu.config(state=DISABLED)

Like with any other widget, you use the configure method to set the state to "disabled":
self.menu.configure(state="disabled")
The above will work for both the tkinter and ttk OptionMenu widgets.

Related

How call a function in a Tkinter menu

I would like to call a function clicking on fileMenu 'Action', I was able to call the function (below is just one example) only by entering it in the submenu, is there a way for make it?
import tkinter as tk
from tkinter import Menu
def stateMenu():
fileMenu.entryconfig("Delete", state="disabled")
fileMenu.entryconfig("Transfer", state="disabled")
root = tk.Tk()
menubar = Menu()
fileMenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="Action", menu=fileMenu) // I would like call function here
fileMenu.add_command(label="Delete", command=stateMenu, state="normal") // no here
fileMenu.add_command(label="Transfer", command=stateMenu, state="normal") // no here
root.config(menu=menubar)
root.mainloop()
The only way to call a command from a menubar is by adding the command with add_command. Some operating systems allow you to add commands to the root menubar, some don't.
From a usability perspective it's generally considered a bad idea to add commands directly on the menubar. Users expect to see a menu when they click on something on the menubar, and would be surprised if some action immediately happened.

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!

tkinter optionmenu close event

I am trying to display a tooltip for optionmenu items in tkinter. I have figured out how to do that. The problem I am left with is closing the tooltip (toplevel) window. I want to do so when the user selects an option or clicks outside the optionmenu (which closes the menu of the option menu). The enter and leave events don't seem to be doing the trick. Any help would be appreciated!
Edit: I have added some code on what I have tried. The "test" print occurs whenever I hover over menu item 1 (which is what I want) but I want to fire enter and leave events whenever the user opens or closes the menu as a whole. I tried binding them to the optionmenu but it is unreliable and sometimes triggers leave events when it shouldn't.
from tkinter import Tk, Frame, BOTH, Menu, Label, SUNKEN, X, BOTTOM
import tkinter as tk
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, background = "white")
self.parent = parent
vals = ["1","2","3"]
var = tk.StringVar()
var.set("1")
option = tk.OptionMenu(root,var,*vals)
option.pack()
t = option.children["menu"]
tk.Menu
#Do I need to unbind
#t.bind("<<MenuSelect>>", self.test_func)
t.bind("<<MenuSelect>>", self.test_func)
t.bind("<Enter>",self.enter_func)
t.bind("<Leave>",self.leave_func)
def test_func(self,event = None):
if self.parent.call(event.widget,"index","active") == 0:
print("test")
def enter_func(self,event=None):
print("entered")
def leave_func(self,event=None):
print("left")
root = tk.Tk()
Application(root)
root.mainloop()
One way I have found to close the tooltip (not shown in example) is to trace the variable associated with the optionmenu and when it changes kill the topwindow. However, if the user opens the menu, hovers over options, then clicks outside the menu items, the topwindow tooltip stays because the menu item never changed.
The question is very similar to this one

Tkinter menu copy/paste/cut option

I have coded in that when you right click you get a menu With copy/cut/paste after some googeling, but i havent found anything about a dropdown menu With copy/cut/paste...
What i got:
from Tkinter import *
master = Tk()
Edit.add_command(label="Copy")
Edit.add_command(label="Paste")
Edit.add_command(label="Cut")
bar.add_cascade(label="Edit", menu=Edit)
mainloop()
Notes:
I am coding in Python 2.7
You need to create a menubar,
mymenu = Menu(master)
create the Edit menu,
editmenu = Menu(mymenu, tearoff=0) # editmenu is now a child of mymenu
add your menu options with labels and commands,
editmenu.add_command(label='Cut', command=cut) # 'cut' is a cut function you wrote
editmenu.add_command(label='Copy', command=copy) # need a copy function too
editmenu.add_command(label='Paste', command=paste) # paste function
then add that Edit menu to the menubar,
mymenu.add_cascade(label='Edit', menu=editmenu)
then add the menubar to the master tk object:
master.config(menu=mymenu)
Then a menubar will appear at the top of the window when you run the program. Make sure you define the cut, copy, and paste functions, or you'll get an error. You can use print as a placeholder if you want.

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