Tkinter multi-frame resize
frame, python, resize, scrollable, tkinter
Solution
You have widgets nested inside widgets. Even if you have the notebook set to expand properly in it's container, you need to also make sure that every container upwards also expands properly. You haven't done that. For example, ask yourself whether the canvas has been set up to properly grow inside of `lower`.
Since you are starting out, here is what I recommend. Instead of trying to get everything right at once, choose a "divide and conquer" approach. First, create frames for the major areas of your GUI. Do nothing but those frames, and get their resize behavior to be exactly what you want. It helps at this stage to give each its own color so you can clearly see where the widgets are. You can always change the color later. Also, if you only have a couple widgets, or all your widgets are oriented horizontally or vertically, pack is often easier to use than grid.
For example:
upper.pack(side="top", fill="x", expand=False)
lower.pack(side="bottom", fill="both", expand=True)
Once you have these major pieces resizing appropriately it's time to solve the problem for just one of those areas. Pick an area, and do the same: add in its children widgets, or subdivide into frames if you have areas within an area. Once you have this working, lather, rinse, repeat.
Problem
I have a device that understands multiple serial protocols. During development I created simple Tkinter UIs to play with the protocols. Each protocol got a new UI. Since the protocols have lots of commands, I implemented the entire UI within a scrollable canvas to permit it to be scrolled when used on smaller displays. The separate UIs worked fine, and I'm now trying to combine the separate UIs into a tabbed UI. The common elements of each UI are the serial port selector, which I separated out and put into a separate top frame. I then implemented a notebook, and put each protocol UI into a frame for each tab. But I'm unable to properly control sizing: I want the root window width to be fixed at the maximum width of any of the protocol frames or serial selector frame, with horizontal resizing disabled. I want the serial selector to always be present, and not be affected when the window is vertically resized (only the notebook is resized/scrolled). Below is what I have so far. All the pieces are present, but the notebook doesn't fill the complete window width, and the notebook doesn't resize when the window is resized (resizing just adds blank space). ``` def main(): ## Main window root = Tkinter.Tk() root.title("Multi-Protocol UI") ## Grid sizing behavior in window root.grid_rowconfigure(0, weight=0) root.grid_rowconfigure(1, weight=1) root.grid_columnconfigure(0, weight=1) root.grid_columnconfigure(1, weight=0) ## Window content upper = ttk.Frame(root) # Serial port selector upper.grid(row=0) upper.grid_rowconfigure(0, weight=0) upper.grid_columnconfigure(0, weight=1) upper.grid_columnconfigure(1, weight=0) lower = ttk.Frame(root) # For protocols lower.grid(row=1) lower.grid(row=1, sticky='nswe') lower.grid_rowconfigure(0, weight=1) lower.grid_columnconfigure(0, weight=1) lower.grid_columnconfigure(1, weight=0) # Setup serial control frame serial = SerialFrame(master=upper) # Serial port selector widget + Open button ## Protocol GUIs are large: Use a form within a scrollable canvas. cnv = Tkinter.Canvas(lower) cnv.grid(row=0, column=0, sticky='nswe') cnv.grid_rowconfigure(0, weight=1) cnv.grid_columnconfigure(0, weight=1) cnv.grid_columnconfigure(1, weight=0) # Scrollbar for canvas vScroll = Tkinter.Scrollbar( lower, orient=Tkinter.VERTICAL, command=cnv.yview) vScroll.grid(row=0, column=1, sticky='ns') cnv.configure(yscrollcommand=vScroll.set) # Frame in canvas window = Tkinter.Frame(cnv) window.grid() # Put the frame in the canvas's scrollable zone cnv.create_window(0, 0, window=window, anchor='nw') # Setup the notebook (tabs) within the scrollable window notebook = ttk.Notebook(window) frame1 = ttk.Frame(notebook) frame2 = ttk.Frame(notebook) notebook.add(frame1, text="ProtoA") notebook.add(frame2, text="ProtoB") notebook.grid(row=0, column=0, sticky='nswe') # Create tab frames protoA = ProtoAFrame(master=frame1) protoA.grid() protoA.update_idletasks() protoB = ProtoBFrame(master=frame2) protoB.grid() protoB.update_idletasks() ## Update display to get correct dimensions root.update_idletasks() window.update_idletasks() ## Configure size of canvas's scrollable zone cnv.configure(scrollregion=(0, 0, window.winfo_width(), window.winfo_height())) # Not resizable in width: root.resizable(width=0, height=1) ## Go! root.mainloop() ``` How do I lock down the top serial frame, expand the notebook to full width, and force window resizing to only affect the notebook frame? I'm a Tkinter newbie, so please be gentle if I've missed something "obvious". TIA!