Tkinter – How to resize frame containing a text widget (in all directions)?

Question:

I’m trying to resize certain layered tkinter components, mostly because I’m curious. Right now, seems stuck in trying to resize a frame containing a text widget. here is my attempt:

import tkinter as tk

def make_draggable(widget):
    widget.bind("<Button-1>", on_drag_start)
    widget.bind("<B1-Motion>", on_drag_motion)
    widget.bind("<Button-3>", on_resize_start)
    widget.bind("<B3-Motion>", on_resize_motion)

def on_drag_start(event):
    widget = event.widget
    widget._drag_start_x = event.x
    widget._drag_start_y = event.y

def on_drag_motion(event):
    widget = event.widget
    x = widget.winfo_x() - widget._drag_start_x + event.x
    y = widget.winfo_y() - widget._drag_start_y + event.y
    widget.place(x=x, y=y)

def on_resize_start(event):
    widget = event.widget
    widget._resize_start_x = event.x
    widget._resize_start_y = event.y
    widget._resize_width = widget.winfo_width()
    widget._resize_height = widget.winfo_height()

def on_resize_motion(event):
    widget = event.widget
    width = widget._resize_width + event.x - widget._resize_start_x
    height = widget._resize_height + event.y - widget._resize_start_y
    widget.place(width=width, height=height)
    widget.winfo_children()[0].configure(width=width, height=height)

main = tk.Tk()

frame = tk.Frame(main, bd=4, bg="grey")
frame.place(x=10, y=10)
make_draggable(frame)

notes = tk.Text(frame)
notes.pack()

main.mainloop()

It is based on this other answer on SO.

This works, but only when right-clicking and dragging the mouse on the bottom and right side of the frame (the gray part).
I don’t know how to make it work on the other directions (eg: top and left, and if possible, the edges too)

How can this be done for all directions?

Note: I’m using 3.8.10 and Tk version 8.6.9 (patch level), on Win10

Asked By: Nordine Lotfi

||

Answers:

When you resize from the top or the left you should also be changing the position. In addition, the resizing is reversed. If you drag from the left to the right, the width becomes less and the x increases. So first you need to check which border you are dragging.

def on_resize_motion(event):
    widget = event.widget
    if widget._resize_start_x < 4:
        x = widget.winfo_x() - widget._resize_start_x + event.x
        width = widget.winfo_width() - (event.x - widget._resize_start_x)    
    else:
        x = widget.winfo_x()
        width = widget._resize_width + event.x - widget._resize_start_x
    if widget._resize_start_y < 4:
        y = widget.winfo_y() - widget._resize_start_y + event.y
        height = widget.winfo_height() - (event.y - widget._resize_start_y)
    else:
        y = widget.winfo_y()
        height = widget._resize_height + event.y - widget._resize_start_y    

This subtracts the difference in mouse position if you started on the left or top border. The else part is the same as what you had.

Finally, you need to update both x and y and the size.

    widget.place(x=x, y=y, width=width, height=height)
Answered By: Emanuel P