pythontkinter

How to place Tkinter PanedWindow sash correctly on a Notebook tab?


Issue with PanedWindow Sash Placement in a Notebook Tab.

I am trying to create a Notebook tab that contains a PanedWindow with four panes.

However, when I attempt to position the sashes manually, the third sash does not appear at the expected position.

Expected Behavior:

The three sashes should be placed at positions 100, 657, and 1000.

Observed Behavior:

Here is my code:



import tkinter as tk
from tkinter import ttk


class Main():
    def __init__(self) -> None:
        self.root = tk.Tk()
        self.root.geometry('1100x400')
        self.root.protocol("WM_DELETE_WINDOW", self.dismiss)
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)

        notebook = self._notebook(self.root)
        notebook.grid(row=0, column=0, sticky=tk.NSEW)

        # Debugging button to retry sash placement
        button = tk.Button(self.root, text='...', command=self.click)
        button.grid(row=1, column=0)

        self.root.mainloop()

    def _notebook(self, master: tk.Frame) -> ttk.Notebook:
        notebook = ttk.Notebook(master)
        self.test_tab = TestFrame(notebook)
        notebook.add(self.test_tab.master_frame, text='Test')
        return notebook

    def dismiss(self, *args) -> None:
        print([self.test_tab.master_frame.sash_coord(index) for index in range(3)])
        self.root.destroy()

    def click(self, *args) -> None:
        # Attempt to reposition sashes (1 moves, 2 doesn't)
        sashes = [(100, 1), (500, 1), (1000, 1)]
        for index, sash in enumerate(sashes):
            self.test_tab.master_frame.sash_place(index, sash[0], 0)


class TestFrame():
    def __init__(self, master) -> None:
        self.master_frame = self._master_frame(master)
        self.master_frame.grid(row=0, column=0, sticky=tk.EW)

        # Expected sash positions
        sashes = [(100, 1), (657, 1), (1000, 1)]
        for index, sash in enumerate(sashes):
            self.master_frame.sash_place(index, sash[0], 0)

    def _master_frame(self, master) -> ttk.PanedWindow:
        frame = tk.PanedWindow(master, orient=tk.HORIZONTAL)

        for _ in range(4):
            listbox = tk.Listbox(frame)
            frame.add(listbox)

        return frame


if __name__ == '__main__':
    Main()

Even after the window is created, on clicking the button, the sash will not get positioned correctly.

System Information:

OS: Manjaro Linux
KDE Plasma Version: 6.2.5
KDE Frameworks Version: 6.10.0
Qt Version: 6.8.2
Python Version: 3.13.1
tk.TkVersion: 8.6

Any insights into why the third sash isn't placed correctly would be greatly appreciated!


Solution

  • When I execute your code and then close the window immediately, I get the following result in the terminal:

    [(100, 1), (500, 1), (503, 1)]
    

    It is not the same as you said in the question: 100, 657, 663. Note that my platform is Windows, not Linux.

    If I print out both the requested and actual widths of frame inside TestFrame._master_frame():

    class TestFrame():
        ...
    
        def _master_frame(self, master) -> ttk.PanedWindow:
            frame = tk.PanedWindow(master, orient=tk.HORIZONTAL)
    
            for _ in range(4):
                listbox = tk.Listbox(frame)
                frame.add(listbox)
    
            # print out both requested and actual widths
            print(frame.winfo_reqwidth(), frame.winfo_width())
    
            return frame
    

    I get:

    507 1
    

    So the width of frame is smaller than 1000 when you try to put the third sash at 1000. It may be the cause.

    You can specify the width of those listboxes so that the total widths is larger than 1000:

    class TestFrame():
        ...
    
        def _master_frame(self, master) -> ttk.PanedWindow:
            frame = tk.PanedWindow(master, orient=tk.HORIZONTAL)
    
            for _ in range(4):
                listbox = tk.Listbox(frame)
                frame.add(listbox, width=300) # added setting width option
    
            return frame
    

    Then those sashes can be placed at the specified positions.

    Note also that the following line inside TestFrame.__init__() is not necessary and can be removed:

    self.master_frame.grid(row=0, column=0, sticky=tk.EW)