How can i set a safety for ttk panedwindow resizing that all content from the panes is always visible?

100 Views Asked by At

I have a PanedWindow from ttk (using the ttk variant for the weights to only resize the top window using the sash, without the weight option the bottom gets resized, but i want the bottom to stay the same size), and i would like to stop the user from moving the sash down enough to move the bottom pane "outside of the window" (streamable video to show what i mean with that)
https://streamable.com/jsu50e
i would like the bottom pane to always stay on screen no matter what and the sash to stop going down if the limit is reached, minsize is not available in the ttk panedwindow from what i can tell, and the weight from the ttk variant was the easiest way for me to achieve the bottom pane not resizing when the window itself gets resized. if there is a way to get the bottom pane to stay the same size when resizing the outer window with tk.panedwindow minsize would be an option as long as i can get the height of the text size and use that to set the minsize to the size of the content of the bottom pane, as the default/min size of it currently is set using text units.

my current code:

import tkinter as tk
import tkinter.ttk as ttk

window = tk.Tk()
w_main = ttk.PanedWindow(master=window, orient=tk.VERTICAL)

frm_img = ttk.Frame(master=w_main, relief=tk.GROOVE, borderwidth=5)
lbl_img = ttk.Label(master=frm_img)
lbl_img.pack(fill=tk.BOTH, expand=True)

frm_cl = ttk.Frame(master=w_main, relief=tk.GROOVE, borderwidth=5)
frm_cl_history = ttk.Frame(master=frm_cl)
txt_cl_history = tk.Text(master=frm_cl_history, width=120, height=20)
txt_cl_history.pack(fill=tk.BOTH, expand=True)
frm_cl_entry = ttk.Frame(master=frm_cl, relief=tk.GROOVE, borderwidth=3)
ent_cl_entry = ttk.Entry(master=frm_cl_entry, width=110)
btn_cl_entry = ttk.Button(master=frm_cl_entry, width=10, text="Enter")
ent_cl_entry.pack(fill=tk.X, side=tk.LEFT, expand=True)
btn_cl_entry.pack()
frm_cl_history.pack(fill=tk.BOTH, expand=True)
frm_cl_entry.pack(fill=tk.X)

w_main.add(frm_img, weight=1)
w_main.add(frm_cl, weight=0)
w_main.pack(fill=tk.BOTH, expand=True)

window.mainloop()
2

There are 2 best solutions below

0
acw1668 On BEST ANSWER

Use tk.PaneWindow instead of ttk.PaneWindow because the former supports minsize option on the panes:

import tkinter as tk
import tkinter.ttk as ttk

window = tk.Tk()
# use tk.PaneWindow instead
w_main = tk.PanedWindow(master=window, orient=tk.VERTICAL)

frm_img = ttk.Frame(master=w_main, relief=tk.GROOVE, borderwidth=5)
lbl_img = ttk.Label(master=frm_img)
lbl_img.pack(fill=tk.BOTH, expand=True)

frm_cl = ttk.Frame(master=w_main, relief=tk.GROOVE, borderwidth=5)
frm_cl_history = ttk.Frame(master=frm_cl)
txt_cl_history = tk.Text(master=frm_cl_history, width=120, height=20)
txt_cl_history.pack(fill=tk.BOTH, expand=True)
frm_cl_entry = ttk.Frame(master=frm_cl, relief=tk.GROOVE, borderwidth=3)
ent_cl_entry = ttk.Entry(master=frm_cl_entry, width=110)
btn_cl_entry = ttk.Button(master=frm_cl_entry, width=10, text="Enter")
ent_cl_entry.pack(fill=tk.X, side=tk.LEFT, expand=True)
btn_cl_entry.pack()
frm_cl_history.pack(fill=tk.BOTH, expand=True)
frm_cl_entry.pack(fill=tk.X)

w_main.add(frm_img, stretch="always") # this pane always stretch
w_main.add(frm_cl, stretch="never")   # this pane never stretch
w_main.pack(fill=tk.BOTH, expand=True)

# set the minsize of the bottom pane
frm_cl.update()
w_main.paneconfigure(frm_cl, minsize=frm_cl.winfo_height())

window.mainloop()
0
patthoyts On

We can add support for a minsize (or minpos here) by monitoring the sash dragging event (B1-Motion) and resetting the sash position if it moves outside some limit. The following example works for making the left pane have a minimum size and can be modified to limit the lower pane for a vertically arranged panedwindow.

"""
Implement a minimum position for the panedwindow sash.
"""

import sys
import tkinter as tk
import tkinter.ttk as ttk


class PanedWindow(ttk.PanedWindow):
    def __init__(self, master, **kwargs):
        self.minpos = None
        if 'minpos' in kwargs:
            self.minpos = kwargs['minpos']
            del kwargs['minpos']
        super(PanedWindow, self).__init__(master, **kwargs)
        self.bind_class('TPanedwindow', '<B1-Motion>', self.on_drag, add='+')

    def on_drag(self, ev):
        if self.minpos:
            if self.sashpos(0) < self.minpos:
                self.sashpos(0, self.minpos)


class App(tk.Tk):
    def __init__(self, **kwargs):
        super(App, self).__init__(**kwargs)
        self.wm_geometry('800x480')
        self.pw = PanedWindow(self, orient=tk.HORIZONTAL, minpos=300)
        self.page1 = ttk.Frame(self.pw, width=300, height=250)
        self.page2 = tk.Frame(self.pw, width=300, height=250, background="SteelBlue")
        self.pw.add(self.page1)
        self.pw.add(self.page2)
        self.pw.place(relwidth=1.0, relheight=1.0)


def main(args=None):
    app = App()
    app.mainloop()
    return 0

if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))