The solution offered by @GordonAitchJay, and implementation with Tkinter, is excellent. Definitely the way to go, if you're running the script directly with Python or in an IDE like Spyder.
However, the OP is working in Jupyter and it turns out that Jupyter and Tkinter do not get along very well. The OP expressed difficulties, and while I do get it to work at first, if I push the code for performance, I'm also noticing serious lags and hiccups. This being the case, I thought I would just add a way to make the interaction work smoothly in Jupyter by using the ipywidgets framework.
# Jupyter notebook
import pandas as pd
import ipywidgets as widgets
from IPython.display import clear_output
from ipyfilechooser import FileChooser
from ipywidgets import interact
from pathlib import Path
# get home dir of user
home = str(Path.home())
# initialize a dict for the excel file; this removes the need to set global values
dict_file = {}
# change to simply `home` if you want users to navigate through diff dirs
fc = FileChooser(f'{home}/excel')
# same here
fc.sandbox_path = f'{home}/excel'
# limit file extensions to '.xls, .xlsb, .xlsm, .xlsx'
fc.filter_pattern = ['*.xls*']
fc.title = '<b>Select Excel file</b>'
display(fc)
# create empty dropdown for sheet names
dropdown = widgets.Dropdown(options=[''], value='', description='Sheets:', disabled=False)
# create output frame for the df
out = widgets.Output(layout=widgets.Layout(display='flex', flex_flow='column', align_items='flex-start', width='100%'))
# callback func for FileChooser
def get_sheets(chooser):
# (re)populate dict
dict_file.clear()
dict_file['file'] = pd.ExcelFile(fc.value)
sheet_names = dict_file['file'].sheet_names
# only 1 sheet, we'll print this one immediate (further below)
if len(sheet_names) == 1:
# set value of the dropdown to this sheet
dropdown.options = sheet_names
dropdown.value = sheet_names[0]
# disable the dropdown; so it's just showing the selection to the user
dropdown.disabled = True
else:
# append empty string and set this as default; this way the user must always make a deliberate choice
sheet_names.append('')
dropdown.options = sheet_names
dropdown.value = sheet_names[-1]
# allow selection by user
dropdown.disabled = False
return
# bind FileChooser to callback
fc.register_callback(get_sheets)
# prompt on selection sheet
def show_df(sheet):
if sheet == '':
if out != None:
# clear previous df, when user selects a new wb
out.clear_output()
else:
# clear previous output 'out' frame before displaying new df, else they'll get stacked
out.clear_output()
with out:
df = dict_file['file'].parse(sheet_name=sheet)
if len(df) == 0:
# if sheet is empty, let the user know
display('empty sheet')
else:
display(df)
return
# func show_df is called with input of widget as param on selection sheet
interact(show_df, sheet=dropdown)
# display 'out' (with df)
display(out)
Snippet of interaction in notebook:
