How to Get Tkinter Canvas to Dynamically Resize to Window Width

How to get tkinter canvas to dynamically resize to window width?

I thought I would add in some extra code to expand on @fredtantini's answer, as it doesn't deal with how to update the shape of widgets drawn on the Canvas.

To do this you need to use the scale method and tag all of the widgets. A complete example is below.

from Tkinter import *

# a subclass of Canvas for dealing with resizing of windows
class ResizingCanvas(Canvas):
def __init__(self,parent,**kwargs):
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()

def on_resize(self,event):
# determine the ratio of old width/height to new width/height
wscale = float(event.width)/self.width
hscale = float(event.height)/self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)

def main():
root = Tk()
myframe = Frame(root)
myframe.pack(fill=BOTH, expand=YES)
mycanvas = ResizingCanvas(myframe,width=850, height=400, bg="red", highlightthickness=0)
mycanvas.pack(fill=BOTH, expand=YES)

# add some widgets to the canvas
mycanvas.create_line(0, 0, 200, 100)
mycanvas.create_line(0, 100, 200, 0, fill="red", dash=(4, 4))
mycanvas.create_rectangle(50, 25, 150, 75, fill="blue")

# tag all of the drawn widgets
mycanvas.addtag_all("all")
root.mainloop()

if __name__ == "__main__":
main()

Dynamically resize widget's when resizing a window in tkinter

Try this raw source code example:

from tkinter import *

root = Tk()
root.geometry("777x575")
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=2)

root.rowconfigure(0, weight=1)

text = Text(root, width = 95 , height = 95)
text.grid(column=0, row=0, ipadx=500, ipady=10, sticky="NSEW")

scrollbar = Scrollbar(text, orient=VERTICAL)
scrollbar.pack(fill=Y, side=RIGHT)

button = Button(root, text = "Sample Button")
button.grid(column=0, row=1, ipadx=500, ipady=10, sticky="NSEW")

mainloop()

Tkinter Canvas dynamically resize image

You are nearly close.In the effbot document.

<Configure>:
The widget changed size (or location, on some platforms). The new size is provided in the width and height attributes of the event object passed to the callback.

After you bind <Configure> event for your widget,it will call the resize_image function when you run this code.(I guess that's because tkinter will draw the image on the canvas,so the width and the height of the canvas will be changed.)

And also refer this question,That's why your image will disappear.You need to keep a reference in the function to prevent python Garbage Collection.

In the function resize_image:

def resize_image(event):
.......
photo1 = ImageTk.PhotoImage(img3)
canv_1.image = photo1
canv_1.itemconfig(image_on_canvas, image =photo1)

How to to resize canvas objects to window size in tkinter without using object oriented programming?

You can resize all the items in the canvas using the Canvas.coords(tagorId, x0, y0...). This can be used for any item in the canvas other than the image. For image use Image.resize((x, y), resample).

Bind the <Configure> event to an event handler. So whenever the canvas is resized the event handler is called.

Here is a demo.(Note the other items on the canvas might be hidden by the image). The below code will automatically resize items to the canvas.

from tkinter import *
from PIL import ImageTk, Image

def resize_widgets(event):
global resized
for items in canvas.find_all():
if items != image:
canvas.coords(items, 0, 0, event.width, event.height)

resized = ImageTk.PhotoImage(im.resize((event.width, event.height), resample = Image.NEAREST))
canvas.itemconfig(image, image=resized)
canvas.moveto(image, 0, 0)

win = Tk()
win.title("Dodge Car Challenger")
win.geometry("600x600")
win.resizable()
w = 700
h = 700
x = 600 // 2
y = 600 // 2

resized = None

canvas = Canvas(win, width=w, height=h, bg="grey")
#canvas.grid(row=0, column=0, padx=50, pady=50)
canvas.pack(expand=True, fill='both')
canvas.bind('<Configure>', resize_widgets)

im = Image.open(r"your imagepath")
img = ImageTk.PhotoImage(im)

canvas.create_rectangle(0, 0, 75, 500, fill="green")
item = canvas.create_rectangle(425, 0, 500, 500, fill="green")
canvas.create_rectangle(75, 0, 80, 500, fill="brown")

image = canvas.create_image(x, 440, image=img)

win.mainloop()

How to resize the width of the canvas dynamically

Since scrollable_frame will be resized to show the longest button, so you can expand canvas inside the callback bound to <Configure> event on scrollable_frame:

...
scrollable_frame = Frame(canvas)
frame_id =canvas.create_window((0, 0), window=scrollable_frame, anchor='nw')

def on_frame_resized(event):
canvas_width = canvas.winfo_reqwidth()
if event.width > canvas_width:
# expand canvas to the width of scrollable_frame
canvas.configure(width=event.width)
else:
# expand scrollable_frame to the width of canvas
canvas.itemconfigure(frame_id, width=canvas_width)
canvas.configure(scrollregion=canvas.bbox("all"))

# tell the canvas how large the frame will be, so that it knows how much it can scroll:
scrollable_frame.bind("<Configure>", on_frame_resized)
...

However you need to remove root.geometry(...) because it will block the main window from expanding due to resize of canvas. Also you need to remove canvas.bind("<Configure>", ...) because it will mess up the work done by on_frame_resized().

...
#root.geometry("300x650")
...
#canvas.bind("<Configure>",canvas.itemconfig(frame_id, width=canvas.winfo_reqwidth()))
...

Resizing Canvas on Tkinter first run issue

The problem is here. self.root.winfo_screenwidth()
Change it to self.cv.width. I don't know why.

def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
self.cv.create_image(self.root.winfo_screenwidth()/2,
self.root.winfo_screenheight()/2, image=self.img, tags=('all'))

Change the last line to

self.cv.create_image(self.cv.width/2, 
self.cv.height/2, image=self.img, tags=('all'))

Fixes the issue.

Tk.winfo_screenwidth() according to https://tkdocs.com/shipman/universal.html returns the width of the screen, indepedant of the size of the window, so even if you have a small window on a 1920x1080 display, this function will return 1920.

self.cv.width returns the width of the canvas object.

Python tkinter frame canvas resize

No need to reinvent the wheel here. Canvas() is a tkinter widget, and like all tkinter widgets it has to be drawn in the window using a geometry manager. This means that we can manipulate how it appears in the window.

Example program below modified from an example found on this page.

If we use .pack() then we can do something like the below:

from tkinter import *

root = Tk()

w = Canvas(root, width=200, height=100)
w.pack(fill="both", expand=True)

w.create_line(0, 0, 200, 100)
w.create_line(0, 100, 200, 0, fill="red", dash=(4, 4))

w.create_rectangle(50, 25, 150, 75, fill="blue")

root.mainloop()

Where the combination of fill="both" and expand=True tells the widget to eat up all the extra space it's given in the window and expand to fill it.

If we're using .grid() then we have to do something slightly different:

from tkinter import *

root = Tk()

root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

w = Canvas(root, width=200, height=100)
w.grid(sticky=N+S+E+W)

w.create_line(0, 0, 200, 100)
w.create_line(0, 100, 200, 0, fill="red", dash=(4, 4))

w.create_rectangle(50, 25, 150, 75, fill="blue")

root.mainloop()

Here we use .rowconfigure() and .columnconfigure to tell row 0 and column 0 (where our canvas is) to have a higher weight than any other row or column, meaning they get given more of the free space in the window than the others (In this case all of it seeing as how no other rows or columns exist), we then need to tell the widget to actually expand with the "cell" it resides in, which we do by specifying sticky=N+S+E+W, which tells the widget to stick to all 4 edges of the cell when they expand, thus expanding the widget.

How to dynamically resize window with Tkinter?

If you use a frame the geometry mangers of tkinter is doing the job for you see:

import tkinter as tk

root= tk.Tk()
root.title('Quick Googling')
myframe = tk.Frame(root)
myframe.pack(fill='both',expand=True)

entry1 = tk.Entry (myframe,background="#8FB1CC")
entry1.pack(fill='x')

button1 = tk.Button(myframe,text='Search Google', bg = "blue")
button1.pack(fill='both',expand=1)
root.mainloop()

Look on a small overview I wrote over the basics.

  1. fill stretch the slave horizontally, vertically or both expand The
  2. slaves should be expanded to consume extra space in their master.

So what the above code does is to create a frame in its natrual size, this means it shrinks itself to the minimum space that it needs to arrange the children.

After this we use for the first time the geometry manager pack and give the options fill, which tells the frame to stretch and using the optional argument expand to consume extra space. Together they resize your frame with the window, since there is nothing else in it.

After that we create an Entry and using once again the method pack to arrange it with this geometry manager. We are using again the optional argument fill and give the value 'x', which tells the geometry manager to stretch the slave/entry vertically.

So the entry streches vertically if the frame is expandes.

At least we create a Button and using the known keywords, to let it resize with the frame.



Related Topics



Leave a reply



Submit