title: Tkinter multi-frame resize
body: <p>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.</p>
  
  <p>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.</p>
  
  <p>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).</p>
  
  <p>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).</p>
  
  <pre><code>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()
  </code></pre>
  
  <p>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".</p>
  
  <p>TIA!</p>