Tkinter how to make a dynamic optionmenu without u

2019-08-10 05:05发布

问题:

I found this question, which was helpful to see the methodology, but I want to use their method without using a dictionary.

Basically, in Python I'm trying to use two optionmenu's from Tkinter where the first will contain the tabs of an excel document, and then the second will contain information that I've read from one of the columns once the tab has been selected from the first optionmenu. As I don't want to store and read every single tab prior, it seems much more efficient to just call the function to read the data once the tab was chosen rather than create a dictionary beforehand. Does someone know a way of doing this?

Here is what I have so far(excel_transfer is a self created library based off openpyxl- if you have further questions on what I'm doing feel free to ask, otherwise it doesn't seem necessary to go into it for the purposes of this question):

import Tkinter
from Tkinter import *
from ttk import Frame
import manipulate_excel
import openpyxl
from openpyxl import load_workbook

class GUI(Frame):
    def read_tabs(self):
        wkbk = load_workbook('file.xlsx')
        sheets=wkbk.get_sheet_names()
        return sheets
    def read_columns(self):
        sheet = self.tabs_var.get()
        #if no value is selected, keep return a blank value
        if(sheet == ''):
            return ['']
        else:
            filepath = 'file.xlsx'
            workbook = excel_transfer.workbook(filepath)
            wb = workbook.open_existing_workbook()
            ws = wb(sheet)
            #step through the rows until a blank value is read
            row = 1
            current_row = 0
            previous_row = 0
            values = []
            #read the column, and append values to the array
            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):
                    values.append(current_row)
            #if there are no values, still return a single array value so it doesn't barf
            if(values== []):
                values= ['']
            return values

    def build_gui(self):
        n = Notebook(self)
        tab = Tkinter.LabelFrame(self, text='Tab')
        n.add(tab, text='Tab')
        n.pack()
        self.tabsvar = StringVar()
        self.tabsvar.set('')
        self.valuesvar = StringVar()
        self.valuesvar.set('')
        self.list_of_tabs = self.read_tabs()
        self.list_of_values = self.read_columns()
        self.TAB_OPTION = OptionMenu(tab, self.tabsvar, *self.list_of_tabs)
        self.TAB_OPTION.grid(row=0, column=0)
        self.VALUES_OPTION = OptionMenu(tab, self.valuesvar, *self.list_of_values)
        self.VALUES_OPTION.grid(row=1, column=0)

if __name__ == '__main__':
    root = Tk()
    app = GUI(root)
    root.mainloop()

The first option works to find the tabs, but the problem I have is the read_column only happens when the GUI is initially opened, it remains a blank value. How would I make the self.list_of_values responsive to which tab is chosen in the first optionmenu? Can I use trace without a dictionary? I'm sure there's prettier ways of writing the code I did- I'm open to constructive criticism as there's always plenty to learn. Thank you for the help!

回答1:

Ok, so I found the answer after continuing to work with it to help out anyone in future having the same problem. I changed self.TAB_OPTION to:

self.TAB_OPTION(tab, self.tabsvar, *self.list_of_tabs, command=self.read_columns)

By adding this, I had to pass in a variable into self.read_columns, as adding the command automatically passes the value the user selected in the first optionmenu. So, I changed self.read_columns to:

def read_columns(self, board):
    sheet = board
    #sheet = self.tabs_var.get()

I also took out the line shown to be commented above, so it received the value that was selected in the first optionmenu from the TAB_OPTION rather than reading what is selected from the GUI. I also added in:

self.VALUES_OPTION['menu'].delete(0, 'end')
for item in values:
    self.VALUES_OPTION['menu'].add_command(label=item)

at the end of the read_columns function so it would regenerate the values for the second optionmenu. It deletes all of the previous values, then adds each value that had been read from the excel in by iterating through the array. I no longer needed the return value at the end of the function. I hope this helps other confused coders :)