Thornydre Thornydre - 13 days ago 10
Python Question

Resize parts of a Python Tkinter grid

I just wanted if there is a way to grab and resize a column or a row of a grid widget with tkinter (as you grab a border of a window and pull it to resize the window).

I thought about a button, and maybe a pressed event or something, but I don't know how to transform the mouse position into a column width or a row height.

Red arrows where I want to grab and resize]:

enter image description here

def initUI(self):
self.parent["bg"] = self.main_color
self.parent.title("Super Pipe")
self.grid(sticky = N + S + E + W)

self.parent.bind("<F5>", self.refresh)

menu_bar = Menu(self.parent)

menu_file = Menu(menu_bar, tearoff = 0)
menu_file.add_command(label="New project", command = self.newProjectCommand)
menu_file.add_command(label="Set project", command = self.setProjectCommand)
menu_file.add_separator()
menu_file.add_command(label="Quit", command = self.parent.destroy)
menu_bar.add_cascade(label="File", menu = menu_file)

menu_edit = Menu(menu_bar, tearoff = 0)
menu_edit.add_command(label = "Clean backups", command = self.cleanBackupsCommand)
menu_edit.add_command(label = "Clean student versions", command = self.cleanStudentCommand)
menu_edit.add_separator()
menu_edit.add_command(label = "Project settings", command = self.projectSettingsCommand)
menu_edit.add_separator()
menu_edit.add_command(label = "Edit custom link", command = self.editCustomLinkCommand)
menu_edit.add_command(label = "Preferences", command = self.preferencesCommand)
menu_bar.add_cascade(label = "Edit", menu = menu_edit)

menu_help = Menu(menu_bar, tearoff = 0)
menu_help.add_command(label = "Credits", command = self.about)
menu_bar.add_cascade(label = "Help", menu = menu_help)

self.parent.config(menu = menu_bar)

self.parent.columnconfigure(0, pad = 0)
self.parent.columnconfigure(1, pad = 0)
self.parent.columnconfigure(2, pad = 0, weight = 2)
self.parent.columnconfigure(3, pad = 0)
self.parent.columnconfigure(4, pad = 0)

self.parent.rowconfigure(0, pad = 0)

###############################################################################################################

## // SIDE BAR \\ ##
left_side_bar = Frame(self.parent, bg = self.main_color)
left_side_bar.grid(row = 0, column = 0, sticky = N)

left_side_bar.columnconfigure(0, pad = 0)
left_side_bar.columnconfigure(1, pad = 0)

left_side_bar.rowconfigure(0, pad = 20)
left_side_bar.rowconfigure(1, pad = 5)
left_side_bar.rowconfigure(2, pad = 5)
left_side_bar.rowconfigure(3, pad = 5)
left_side_bar.rowconfigure(4, pad = 5)
left_side_bar.rowconfigure(5, pad = 20)

self.add_asset_button = Button(left_side_bar, text = "Add asset", state = DISABLED, bg = self.button_color2, activebackground = self.over_button_color2, activeforeground = self.text_color, fg = self.text_color, bd = 0, width = 8, height = 1, command = self.addAssetCommand)
self.add_asset_button.grid(row = 0, column = 0)

self.add_shot_button = Button(left_side_bar, text = "Add shot", state = DISABLED, bg = self.button_color2, activebackground = self.over_button_color2, activeforeground = self.text_color, fg = self.text_color, bd = 0, width = 8, height = 1, command = self.addShotCommand)
self.add_shot_button.grid(row = 0, column = 1)

## ASSETS LIST ##
asset_label = Label(left_side_bar, text = "Assets", bg = self.main_color, fg = self.text_color, font = "Helvetica 10 bold")
asset_label.grid(row = 1, column = 0, columnspan = 2)

self.asset_list = ttk.Treeview(left_side_bar, height = 16, show = "tree", selectmode = "browse")
ttk.Style().configure("Treeview", background = self.list_color)
self.asset_list.tag_configure("done", background = "#89C17F")
self.asset_list.tag_configure("urgent", background = "#E55252")
self.asset_list.tag_configure("high", background = "#EFB462")
self.asset_list.tag_configure("medium", background = "#F4E255")
self.asset_list.insert("", 1, "character", text = "CHARACTER")
self.asset_list.insert("", 3, "fx", text = "FX")
self.asset_list.insert("", 4, "props", text = "PROPS")
self.asset_list.insert("", 5, "set", text = "SET")
self.asset_list.grid(row = 2, column = 0, columnspan = 2, sticky = N + S + W + E)
self.asset_list.bind("<ButtonRelease-1>", self.assetListCommand)

## SHOTS LIST ##
shot_label = Label(left_side_bar, text = "Shots", bg = self.main_color, fg = self.text_color, font = "Helvetica 10 bold")
shot_label.grid(row = 3, column = 0, columnspan = 2)

self.shot_list = Listbox(left_side_bar, bg = self.list_color, selectbackground = self.second_color, bd = 0, highlightthickness = 0, width = 30, height = 31, exportselection = False)
self.shot_list.grid(row = 4, column = 0, columnspan = 2, sticky = N + S + W + E)
self.shot_list.bind("<<ListboxSelect>>", self.shotlistCommand)

self.shots_preview_button = Button(left_side_bar, text = "Shots preview", state = DISABLED, bg = self.button_color2, activebackground = self.over_button_color2, activeforeground = self.text_color, fg = self.text_color, bd = 0, width = 12, height = 1, command = self.shotsPreviewCommand)
self.shots_preview_button.grid(row = 5, column = 0, columnspan = 2)

self.custom_button = Button(left_side_bar, text = "Custom link", bg = self.button_color2, activebackground = self.over_button_color2, activeforeground = self.text_color, fg = self.text_color, bd = 0, width = 12, height = 1, command = self.customButtonCommand)
self.custom_button.grid(row = 6, column = 0, columnspan = 2)

###############################################################################################################

separator = Frame(self.parent, bg = self.separator_color, bd = 0, width = 5, height = 10)
separator.grid(row = 0, column = 1, sticky = N + S + W + E)

###############################################################################################################

## // SHOTS MAIN FRAME \\ ##
self.main_area_shot = Frame(self.parent, bg = self.main_color, bd = 0)
self.main_area_shot.grid(row = 0, column = 2, sticky = N + S + W + E)
self.main_area_shot.pi = self.main_area_shot.grid_info()

self.main_area_shot.columnconfigure(0, pad = 10, minsize = 40)
self.main_area_shot.columnconfigure(1, pad = 10)
self.main_area_shot.columnconfigure(2, pad = 10, minsize = 50)
self.main_area_shot.columnconfigure(3, pad = 10)
self.main_area_shot.columnconfigure(4, pad = 10)
self.main_area_shot.columnconfigure(5, pad = 10, weight = 2)
self.main_area_shot.columnconfigure(6, pad = 10)

self.main_area_shot.rowconfigure(0, pad = 20, minsize = 75)
self.main_area_shot.rowconfigure(1, pad = 5, minsize = 75)
self.main_area_shot.rowconfigure(2, pad = 5, minsize = 410)
self.main_area_shot.rowconfigure(3, pad = 5, minsize = 50)

Answer

The simplest solution is to use a paned window, which has this feature built in. A paned window is used like a frame, but instead of using pack or grid to add widgets to it, you use the add method. The widget will put a separator between the widgets, letting you adjust the relative size. Paned windows have either a horizontal or vertical orientation.

Example

This example assumes python 2.x. For 3.x you only need to change the imports.

Note: both tkinter and ttk provide a paned window. The following example uses the one from ttk which arguably looks just a little bit nicer.

import Tkinter as tk
import ttk

root = tk.Tk()

# the main window is divided into left and right sections,
# and the sidebar is divided into a top and bottom section.
pw = ttk.PanedWindow(orient="horizontal")
sidebar = ttk.PanedWindow(pw, orient="vertical")
main = tk.Frame(pw, width=400, height=400, background="black")
sidebar_top = tk.Frame(sidebar, width=200, height=200, background="gray")
sidebar_bottom = tk.Frame(sidebar, width=200, height=200, background="white")

# add the paned window to the root
pw.pack(fill="both", expand=True)

# add the sidebar and main area to the main paned window
pw.add(sidebar)
pw.add(main)

# add the top and bottom to the sidebar
sidebar.add(sidebar_top)
sidebar.add(sidebar_bottom)

root.mainloop()