I’m trying get a list of .xlsm files from a folder, and generate a scrollable canvas from which the tabs needed for import can be selected manually using the check buttons (all having the same tab format e.g. tab1, tab2, tab3, tab4).
The major issue I’m having is getting weights to work correctly for the headers in relation to their canvas columns, as longer file names distorts the weight.
I’ve tried playing with the weights and can’t seem to figure out a workaround. I also attempted using treeview
as an alternative but this seems to introduce far bigger issues with using checkbuttons. Would it possible to freeze the top row if the headers were placed inside the canvas itself, or could I implement something like a bind so that the header frames individual columns align with the width of the columns of the canvas frame?
import os import tkinter as tk from tkinter import filedialog from tkinter import ttk class MainFrame: def __init__(self, master): master.geometry('1000x200') self.master_tab = ttk.Notebook(master) self.master_tab.grid(row=0, column=0, sticky='nsew') # Sub-Classes self.file_select = FileSelect(self.master_tab, main=self) class FileSelect: def __init__(self, master, main): self.main = main # ================== Primary Frame ================== self.primary_frame = tk.Frame(master) self.primary_frame.grid(row=0, column=0, sticky='NSEW') master.add(self.primary_frame, text='Import Selection') self.primary_frame.columnconfigure(0, weight=1) self.primary_frame.rowconfigure(1, weight=1) # ================== File Selection Frame ================== self.selection_frame = tk.Frame(self.primary_frame) self.selection_frame.grid(row=0, column=0, sticky='EW') # Button - Select Directory self.fp_button = tk.Button(self.selection_frame, text='Open:', command=self.directory_path) self.fp_button.grid(row=0, column=0, sticky='W') # Label - Display Directory self.fp_text = tk.StringVar(value='Select Import Directory') self.fp_label = tk.Label(self.selection_frame, textvariable=self.fp_text, anchor='w') self.fp_label.grid(row=0, column=1, sticky='W') # ================== Canvas Frame ================== self.canvas_frame = tk.Frame(self.primary_frame) self.canvas_frame.grid(row=1, column=0, sticky='NSEW') self.canvas_frame.rowconfigure(1, weight=1) # Canvas Header Labels for header_name, x in zip(['File Name', 'Tab 1', 'Tab 2', 'Tab 3', 'Tab 4'], range(5)): tk.Label(self.canvas_frame, text=header_name, anchor='w').grid(row=0, column=x, sticky='EW') self.canvas_frame.columnconfigure(x, weight=1) # Scroll Canvas self.canvas = tk.Canvas(self.canvas_frame, bg='#BDCDFF') self.canvas.grid(row=1, column=0, columnspan=5, sticky='NSEW') self.canvas.bind('<Configure>', self.frame_width) # Scrollbar self.scroll_y = tk.Scrollbar(self.canvas_frame, orient="vertical", command=self.canvas.yview) self.scroll_y.grid(row=1, column=5, sticky='NS') # Canvas Sub-Frame self.canvas_sub_frame = tk.Frame(self.canvas) for x in range(5): self.canvas_sub_frame.columnconfigure(x, weight=1) self.canvas_frame_window = self.canvas.create_window(0, 0, anchor='nw', window=self.canvas_sub_frame) self.canvas_sub_frame.bind('<Configure>', self.config_frame) def config_frame(self, event): self.canvas.configure(scrollregion=self.canvas.bbox('all'), yscrollcommand=self.scroll_y.set) def frame_width(self, event): canvas_width = event.width event.widget.itemconfigure(self.canvas_frame_window, width=canvas_width) def directory_path(self): try: # Select file path directory = filedialog.askdirectory(initialdir='/', title='Select a directory') self.fp_text.set(str(directory)) os.chdir(directory) # Updates GUI with .xlsm file list & checkboxes if len(os.listdir(directory)) != 0: y = -1 for tb in os.listdir(directory): if not tb.endswith('.xlsm'): print(str(tb) + ' does not have ;.xlsm file extension') else: y += 1 file_name = tk.Label(self.canvas_sub_frame, text=tb, anchor='w', bg='#96ADF3') file_name.grid(row=y, column=0, sticky='EW') for x in range(4): tb_period = tk.Checkbutton(self.canvas_sub_frame, anchor='w', bg='#C2D0F9') tb_period.grid(row=y, column=x+1, sticky='EW') else: print('No files in directory') # Filepath error handling exception except os.error: print('OS ERROR') if __name__ == '__main__': root = tk.Tk() root.rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) MainFrame(root) root.mainloop()
Advertisement
Answer
The simplest solution is to use two canvases, and then set up a binding so that whenever the size of the inner frame changes, you update the headers to match the columns.
It might look something like this:
def config_frame(self, event): self.canvas.configure(scrollregion=self.canvas.bbox('all'), yscrollcommand=self.scroll_y.set) self.canvas.after_idle(self.reset_headers) def reset_headers(self): for column in range(self.canvas_sub_frame.grid_size()[0]): bbox = self.canvas_sub_frame.grid_bbox(column, 0) self.canvas_frame.columnconfigure(column, minsize = bbox[2])