Question

I am having issues with creating a menu that suits the needs of my application. I am creating an app that requires the user to choose possible items to compare inside of a source menu.

Right now my menu organization is indended to look something like

FILE | EDIT | OPTIONS | SOURCE | HELP

My issue is, the number of choices inside source is quite large (~100) and needs to be navicable quickly (<5 seconds) or the target audience of my application will probably not use it.

The solution I have in mind is to nest options underneath the data structures they come from. Effectively this means I can treat the Source options and suboptions as two lists:

["Data1", "Data2", "Data3"] 

and

[["Option1_1", "Option1_2", "Option1_3"],
["Option2_1","Option2_2","Option2_3"],
["Option3_1","Option3_2","Option3_3"]]

I have searched extensively and can't find how to create submenus effectively in Tkinter. When I get a submenu of radiobuttons (checkbuttons would also work), the button click does not activate the command, and that is not a viable solution.

What I would like to do is create a menu that looks something like

FILE | SOURCE | ETC...
          Data1    |
               Option1_1
               Option1_2
               Option1_3
         Data2    |
         Data3

How can I do this? Preferably while also storing buttons to a list, then appending them to the menu, so that I can call select and deselect if I need to? Radiobuttons or checkbuttons would work, checkbuttons are preferred provided I still have access to them after creation.

Thank you

Here's one example of non-working code:

from Tkinter import *
class App(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.makeMenu(master)

    def makeMenu(self, parent):
        menuBar = Frame(parent, relief = 'raised', borderwidth = 1)
        fileMenu = Menubutton(menuBar, text = "File")
        fileMenu.pack(side = 'left')
        fileMenu.menu = Menu(fileMenu, tearoff = 0)
        load = Menubutton(fileMenu.menu, text="Load")
        load.pack(anchor=W)
        source = Menubutton(fileMenu.menu, text="Source")
        source.pack(anchor=W)
        source.menu = Menu(source, tearoff = 0)
        self._listi = ["who", "what", "where", "how"]
        self._buttonList = []
        for l in self._listi:
            c = Checkbutton(master = source.menu, text=l, command=lambda arg0=l: self.test(arg0))
            self._buttonList.append([l, c])
            c.pack()
        save = Button(master=fileMenu.menu, text="Save")
        save.pack(anchor=W)
        exit = Button(master=fileMenu.menu, text="Exit", command = self.quit)
        exit.pack()
        fileMenu['menu'] = fileMenu.menu
        menuBar.pack()

    def test(self, arg0):
        for b in self._buttonList:
            if arg0 != b[0]:
                b[1].deselect()
            elif arg0 == b[0]:
                b[1].select()


# create app
myApp = App()

# start program
myApp.mainloop()
Was it helpful?

Solution

You cannot add Checkbutton or Button instances to a menu. You must use the add_checkbutton or add_radiobutton or add_command methods. Also, you don't need to create a menubar from a frame and menubuttons -- you can attach a menu directly to a window. Also, you don't need to programatically select or deselect the checkbuttons or radiobuttons, that is taken care of by tkinter.

I've modified your code to illustrate:

from Tkinter import *
class App(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.makeMenu(master)

    def makeMenu(self, parent):
        menuBar = Menu(parent)
        parent.configure(menu=menuBar)
        fileMenu = Menu(menuBar)
        sourceMenu = Menu(menuBar)
        menuBar.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_command(label="Load")
        fileMenu.add_cascade(label="Source", menu=sourceMenu)
        self._listi = ["who","what","where","how"]
        for l in self._listi:
            sourceMenu.add_checkbutton(label=l, command=lambda arg0=l: self.test(arg0))
        fileMenu.add_command(label="Save")
        fileMenu.add_command(label="Exit", command=self.quit)


    def test(self, arg0):
        print "you clicked on", arg0


# create app
root = Tk()
myApp = App(root)

# start program
myApp.mainloop()

Note that I also added a line of code to create the root window, and I pass that root window into the constructor for the app.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top