Python Forum

Full Version: populating dropdown from Method
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello,

(about Tkinter in python 3.7.3)
I am trying to create a GUI where my user can select an excel file via a browse button.
He should then get a dropdown list, that contains all the sheets within that file.
The idea is that I can then use that info to start up a script.

However, I can't seem to populate my dropdown from within my method.
I've read that I'm supposed to use *options as dropdown list, but I get an error when using self.*options

So basically, whenever i browse an excel file, I'd love my dropdown to get the data that is now in that Entry widget.
Any help would be vastly appreciated!

import tkinter as tk
from tkinter import ttk
import tkinter.filedialog
import pandas as pd
    
class MainProgram(tk.Tk):
    def __init__(self, *args, **kwargs):   
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack()
        
        self.frames = {}
        
        for F in (PageOne, PageTwo):
            frame = F(container, self)    
            self.frames[F] = frame
            frame.grid(row=0, column= 0, sticky="nsew")
        
        self.show_frame(PageOne)
        
    def show_frame(self, cont):
        frame_to_activate = self.frames[cont]
        frame_to_activate.tkraise()
    
class PageOne(tk.Frame):
    
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.bc_frame = tk.Frame(self, width=250, height=300)
        self.bc_label = tk.Label(self.bc_frame
                                 , text="locate your brand company list")

        self.bc_file_location = tk.Entry(self.bc_frame,  
                            text = "location of the brand company list", 
                            width = 25) 

        self.bc_sheets = tk.Entry(self.bc_frame, width = 50)     
        self.bc_sheets.insert('end', 'temporary location of excel sheets')
        self.bc_browse_btn = tk.Button(self.bc_frame, text = "Browse File"
                                , command = self.browseExcelFile)
        self.bc_sheet = tk.StringVar()
        self.bc_sheet.set("This should be the list of sheets")
        
        self.bc_file_sheets = tk.OptionMenu(self.bc_frame
                                            , self.bc_sheet
                                            , self.bc_sheets.get())
        self.navigate_btn = tk.Button(self, text = "Go to Page 2"
                                  , relief = tk.RAISED
                                  , command = lambda: controller.show_frame(PageTwo))
        #---------------------------------------------------------------------
        # Put the widgets on the screen
        #---------------------------------------------------------------------
        self.bc_frame.grid_propagate(0)
        self.bc_frame.grid(row=1, column=1, padx=10, pady=10)        
        self.bc_label.grid(row=0, column=0, padx=10, pady=10, sticky='W')
        self.bc_browse_btn.grid(row = 1, column = 0, padx=10, pady=10, sticky='W') 
        self.bc_file_location.grid(row = 2, column = 0, padx=10, pady=10, sticky='W')  
        self.bc_file_sheets.grid(row=3, column=0, padx=10, pady=10, sticky='W')
        self.bc_sheets.grid(row=4, column=0, padx=10, pady=10, sticky='W')
        self.navigate_btn.grid(row = 0, column = 1)

    def browseExcelFile(self): 
        filename = tk.filedialog.askopenfilename(initialdir = "/", 
                                              title = "Select a File", 
                                              filetypes = (("Excel files", 
                                                            "*.xlsx*"), 
                                                           ("all files", 
                                                            "*.*"))) 
        # Delete what is in the entry and replace with the browse path 
        self.bc_file_location.delete(0, 'end')
        self.bc_file_location.insert(0, filename)
        sheets_in_file = pd.ExcelFile(filename).sheet_names
        self.bc_sheets.delete(0, 'end')
        self.bc_sheets.insert(0, sheets_in_file)
        
class PageTwo(tk.Frame):
    
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text= "Page 2")
        label.pack(pady=10, padx=10)
        button1 = ttk.Button(self, text = "Back to first page",
                            command = lambda: controller.show_frame(PageOne))
        button1.pack()
                
        
app = MainProgram()
app.geometry("700x500")
app.mainloop()
It would be *self.options, not self.*options. But that has nothing to do with your problem.

I think your "*option" reference refers to this or a similar post on stackoverflow
choices = ('network one', 'network two', 'network three')

def refresh():
    # Reset var and delete all old options
    var.set('')
    network_select['menu'].delete(0, 'end')

    # Insert list of new options (tk._setit hooks them up to var)
    new_choices = ('one', 'two', 'three')
    for choice in new_choices:
        network_select['menu'].add_command(label=choice, command=tk._setit(var, choice))

network_select = tk.OptionMenu(root, var, *choices)
Here *choices unpacks the choices list into positional arguments so the OptionMenu command above becomes:
network_select = tk.OptionMenu(root, var, 'network one', 'network two', 'network three')
But that is not going to do what you want. That only sets an initial set of choices, I don't see that OptionMenu has any kind of variable that you can bind to automatically update the list. What you need to do is the other part of the post, the "refresh()". This method would bet called when you open a spreadsheet and get a list of the sheets. It clears out the old choices and builds a new list of choices.
(Apr-06-2020, 05:41 PM)deanhystad Wrote: [ -> ]It would be *self.options, not self.*options. But that has nothing to do with your problem.

I think your "*option" reference refers to this or a similar post on stackoverflow
choices = ('network one', 'network two', 'network three')

def refresh():
    # Reset var and delete all old options
    var.set('')
    network_select['menu'].delete(0, 'end')

    # Insert list of new options (tk._setit hooks them up to var)
    new_choices = ('one', 'two', 'three')
    for choice in new_choices:
        network_select['menu'].add_command(label=choice, command=tk._setit(var, choice))

network_select = tk.OptionMenu(root, var, *choices)
Here *choices unpacks the choices list into positional arguments so the OptionMenu command above becomes:
network_select = tk.OptionMenu(root, var, 'network one', 'network two', 'network three')
But that is not going to do what you want. That only sets an initial set of choices, I don't see that OptionMenu has any kind of variable that you can bind to automatically update the list. What you need to do is the other part of the post, the "refresh()". This method would bet called when you open a spreadsheet and get a list of the sheets. It clears out the old choices and builds a new list of choices.

My god, I spent 4 hours looking for this Big Grin
It was indeed as simple as adding the part below, I had never heard of tk._setit Dance
Thanks so much!

for choice in new_choices:
        network_select['menu'].add_command(label=choice, command=tk._setit(var, choice))