-1

I am having the problem where I am able to populate two optionmenus using Tkinter, (the second one dynamically based off the first), but I notice when I try to select one of the values in the second optionmenu, it does not allow me to select. I have noticed on the same GUI, sometimes when running another function, it will have this affect on a different option menu that worked fine prior to running the function. The options will show correctly, and the mouse can scan over them, but when you click on one it doesn't show that it has been selected, or execute the command set to it. Has anyone had this problem before?

enter image description here Ok, so my hopes of someone else having the same issue are going down, and I'll include more code by request of some of the responders in case this might shed light on the issue. I'll try and pull everything applicable:

class GUI(Frame):

    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.build_gui()
    #reads the tabs on an excel and inputs the tab names as the values in the first optionmenu (this works)
    def read_board_tabs(self):
        #many of these variables in the function may not be defined. Ignore them as they are taken out of context. This should just help visually see the structure.
        filepath = 'Board Control.xlsx'
        wkbk = load_workbook((filepath))
        sheets = wkbk.get_sheet_names()
        print sheets
        return sheets

    #reads content of that excel tab chosen to populate the second optionmenu (populating the option menu works, but they can't be selected once populated)
    def read_serials(self, board):
        #many of these variables in the function may not be defined. Ignore them as they are taken out of context. This should just help visually see the structure.
        sheet = board
        if(sheet == ''):
            return ['']
        else:
            filepath = 'Board Control.xlsx'
            workbook = excel_transfer.workbook(filepath)
            wb = workbook.open_existing_workbook()
            ws = workbook.activate_specific_worksheet(wb, sheet)
            row = 1
            current_row = 0
            previous_row = 0
            serials = []
            #read the serials that exist for the board selected
            while current_row != None:
                previous_row = current_row
                row = row + 1
                cell = 'A' + str(row)
                current_row = workbook.read_new_cell(cell, ws)
                if(current_row != None):
                    serials.append(current_row)
            if(len(serials) == 0):
                serials = ['']
            self.BOARD_SERIAL_OPTION['menu'].delete(0, 'end')
            for item in serials:
                self.BOARD_SERIAL_OPTION['menu'].add_command(label=item)  

    #this is the command that won't execute when I try to select the second optionmenu value
    def find_board_info(self, serial):
        #many of these variables in the function may not be defined. Ignore them as they are taken out of context. This should just help visually see the structure.
        self.board_checkout_status_var.set('')
        sheet = (self.boards_var).get()
        new_search = StringFound.StringSearch(serial)
        results = new_search.read_board(sheet)
        if(results == ['']):
            self.boardstatusvar.set('No board was found')
        else:
            self.boardstatusvar.set('Board: %s Serial: %s' %(results[1], serial))
            self.boardstatus_locationvar.set('Found in file: %s' %results[0])
            self.boards_var.set('%s serial %s' %(results[1], serial))
            self.dispositionvar.set(results[3])            
            self.TXvar.set(results[5])  
            self.RXvar.set(results[6])   
            self.lastvar.set(results[10])  
            self.datevar.set(results[9])
            if(results[14] != None):
                self.currentvar.set(results[10]) 
            self.locationvar.set(results[4])     
            self.imagevar.set(results[8]) 
            self.notesvar.set(results[12]) 
        self.current_board_chosen = [sheet, serial, results[15]]

    #creates the gui
    def build_gui(self):
        n = Notebook(self)
        board_process = Tkinter.LabelFrame(self, text="Board Updates")
        n.add(board_process, text='Board Signout')
        n.pack()
        self.boards_var = StringVar()
        self.boards_var.set("")
        self.serials_var = StringVar()
        self.serials_var.set("")
        self.SEARCHBOARDFRAME = Tkinter.LabelFrame(board_process, text='Find Board')
        self.SEARCHBOARDFRAME.grid(row=0, column=0, sticky='WE')
        self.BOARD_SEARCH_LABEL = Label(self.SEARCHBOARDFRAME, text='Type:')
        self.BOARD_SEARCH_LABEL.grid(row=0, column=0, sticky='W', padx=5, pady=2)
        self.BOARD_SEARCH_OPTION = OptionMenu(self.SEARCHBOARDFRAME, self.boards_var, *self.list_of_boards, command=self.read_serials) 
        self.BOARD_SEARCH_OPTION.grid(row=0, column=1, sticky='W', padx=5, pady=2)   
        self.BOARD_SERIAL_LABEL = Label(self.SEARCHBOARDFRAME, text='Serial:')
        self.BOARD_SERIAL_LABEL.grid(row=1, column=0, sticky='W', padx=5, pady=2)
        self.BOARD_SERIAL_OPTION = OptionMenu(self.SEARCHBOARDFRAME, self.serials_var, *self.list_of_serials, command=self.find_board_info)
        self.BOARD_SERIAL_OPTION.grid(row=1, column=1, sticky='W', padx=5, pady=2)

if __name__ == '__main__':
    root = Tk()
    app = GUI(root)
    root.mainloop()
Joe
  • 59
  • 1
  • 9
  • You may want to be more precise. Show a minimal piece of code reproducing your problem. – joaquin Sep 24 '14 at 16:03
  • I think that's where a lot of the problem is. I'm not sure exactly where the problem is in the code, and with over a thousand lines of code it's impractical to post it all here. I had an earlier post [here](http://stackoverflow.com/questions/25898211/tkinter-how-to-make-a-dynamic-optionmenu-without-using-a-dictionary) that shows more of the structure of the code if that helps. I was hoping someone else might have run into a similar problem with not being able to select a value once a function is run, and might be able to share their experience of why they had that problem. – Joe Sep 24 '14 at 16:08
  • 3
    @Joe: the exercise of reducing the problem down is usually sufficient to find the problem. Tkinter has no bugs like this, so the problem is certainly how you're doing things. Without us being able to see code that replicates the problem, we can't help. – Bryan Oakley Sep 24 '14 at 16:12
  • The code as posted doesn't run -- you're missing imports, and then I get `AttributeError: GUI instance has no attribute 'list_of_boards'` When I fix those errors, the window is blank because you don't pack/place/grid the GUI frame. – Bryan Oakley Sep 25 '14 at 16:16

1 Answers1

2

The problem is in these lines of code:

for item in serials:
    self.BOARD_SERIAL_OPTION['menu'].add_command(label=item)  

You are putting items in the option menu with a label, but you aren't giving them a command. The whole reason the Optionmenu widget exists is to add a special command to each item that gives the option menu its behavior. Unless you add that command when you dynamically create new items, those items won't do anything when you select them -- it's just a dumb menu with labels on it.

Unfortunately, the command that is associated with each item is returned from a private factory class (Tkinter._setit), so you don't have any officially supported way to add new items to an option menu. If you aren't afraid to use private commands, you can change your code to be this:

for item in serials:
    command=Tkinter._setit(self.serials_var, item, self.find_board_info)
    self.BOARD_SERIAL_OPTION['menu'].add_command(label=item, command=command)

For another way to solve this problem see Change OptionMenu based on what is selected in another OptionMenu

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you for the help! Adding the command to the add_command fixed the issue! – Joe Sep 26 '14 at 15:13
  • I ended up having to change it to this to have the argument pass through: self.BOARD_SERIAL_OPTION['menu'].add_command(label=item, command=lambda item=item: self.find_board_info(item)) – Joe Sep 26 '14 at 15:51