The Tkinter Canvas move() method shifts an existing Canvas item by a horizontal and vertical distance. It can move rectangles, ovals, lines, text, images and groups of items identified by a Canvas tag.
canvas.move(item, dx, dy)
move() does not set an absolute position. It adds dx and dy to the item's current coordinates.canvas.move(tag_or_id, dx, dy)
| Argument | Meaning |
|---|---|
tag_or_id | Canvas item id or Canvas tag identifying the item or items to move. |
dx | Horizontal distance to move. |
dy | Vertical distance to move. |
Canvas coordinates normally increase from the top-left corner.
| Movement | dx | dy |
|---|---|---|
| Right | Positive | 0 |
| Left | Negative | 0 |
| Down | 0 | Positive |
| Up | 0 | Negative |
| Right and down | Positive | Positive |
create_rectangle(), or a Canvas tag assigned to one or more items.
create_rectangle() returns an item id. Pass this id to move().
import tkinter as tk
root=tk.Tk()
root.geometry('420x250')
canvas=tk.Canvas(root, width=400, height=170, bg='lightgreen')
canvas.pack(padx=10, pady=10)
rect=canvas.create_rectangle(20, 70, 50, 100, fill='red')
def move_rectangle():
canvas.move(rect, 5, 5)
tk.Button(root, text='Move', command=move_rectangle).pack()
root.mainloop()
Each button click adds 5 pixels to both coordinates, so the rectangle moves right and down.
canvas.move(rect, 5, 5)
One reusable function can move the item in any direction.
import tkinter as tk
root=tk.Tk()
root.geometry('420x300')
canvas=tk.Canvas(root, width=400, height=180, bg='lightgreen')
canvas.grid(row=0, column=0, columnspan=3, padx=10, pady=10)
rect=canvas.create_rectangle(190, 80, 220, 110, fill='red')
step=10
def move_item(dx, dy):
canvas.move(rect, dx, dy)
tk.Button(root, text='Up', command=lambda: move_item(0, -step)).grid(row=1, column=1, sticky='ew')
tk.Button(root, text='Left', command=lambda: move_item(-step, 0)).grid(row=2, column=0, sticky='ew')
tk.Button(root, text='Down', command=lambda: move_item(0, step)).grid(row=2, column=1, sticky='ew')
tk.Button(root, text='Right', command=lambda: move_item(step, 0)).grid(row=2, column=2, sticky='ew')
root.mainloop()
Keyboard events can call the same movement function.
root.bind('<Right>', lambda event: move_item(step, 0))
root.bind('<Left>', lambda event: move_item(-step, 0))
root.bind('<Up>', lambda event: move_item(0, -step))
root.bind('<Down>', lambda event: move_item(0, step))
The event callback receives an event object, so the lambda accepts event before calling the regular movement function.
move() changes an item's position relative to where it is now.
canvas.move(rect, 10, 0)
This means:
Move the rectangle 10 pixels to the right.
coords() can read or replace an item's coordinates.
canvas.coords(rect, 50, 60, 100, 110)
This means:
Place the rectangle at these coordinates.
| Method | Use |
|---|---|
move(item, dx, dy) | Relative movement from the current position |
coords(item) | Read the item's current coordinates |
coords(item, ...) | Replace the item's coordinates |
For a rectangle:
print(canvas.coords(rect))
Sample Output
[20.0, 70.0, 50.0, 100.0]
These values represent:
[x1, y1, x2, y2]
For an image using its default center anchor:
print(canvas.coords(image_item))
Sample Output
[100.0, 80.0]
Without a boundary check, repeated movement can move an item completely outside the visible Canvas.
bbox() returns the bounding rectangle of a Canvas item.
print(canvas.bbox(rect))
We can use it before moving:
def move_inside(dx, dy):
x1, y1, x2, y2=canvas.bbox(rect)
width=int(canvas.cget('width'))
height=int(canvas.cget('height'))
if x1+dx < 0 or x2+dx > width:
dx=0
if y1+dy < 0 or y2+dy > height:
dy=0
canvas.move(rect, dx, dy)
Now movement stops at the Canvas boundary.
A Canvas tag can identify several items. Passing that tag to move() moves every matching item.
canvas.create_rectangle(30, 30, 80, 70, fill='lightblue', tags='group1')
canvas.create_text(55, 50, text='Move', tags='group1')
canvas.move('group1', 20, 10)
Both the rectangle and text move together.

create_image() also returns a Canvas item id, so it can be passed directly to move().
import tkinter as tk
root=tk.Tk()
canvas=tk.Canvas(root, width=500, height=300, bg='lightgreen')
canvas.pack()
photo=tk.PhotoImage(file='plus2net.png')
image_item=canvas.create_image(100, 80, image=photo)
canvas.move(image_item, 20, 10)
root.mainloop()
By default, the coordinates passed to create_image() represent the center of the image.
If top-left coordinates are easier for the application:
image_item=canvas.create_image(100, 80, image=photo, anchor='nw')
PhotoImage in a variable such as photo while the Canvas is displaying it.
The cleanest approach is to bind mouse events directly to the Canvas image item. This means dragging starts only when the user presses the mouse button over that image.
import tkinter as tk
root=tk.Tk()
root.geometry('620x420')
canvas=tk.Canvas(root, width=600, height=360, bg='lightgreen')
canvas.pack(padx=10, pady=10)
photo=tk.PhotoImage(file='plus2net.png')
image_item=canvas.create_image(120, 100, image=photo)
drag_data={'x': 0, 'y': 0}
def start_drag(event):
drag_data['x']=event.x
drag_data['y']=event.y
def drag(event):
dx=event.x-drag_data['x']
dy=event.y-drag_data['y']
canvas.move(image_item, dx, dy)
drag_data['x']=event.x
drag_data['y']=event.y
canvas.tag_bind(image_item, '<ButtonPress-1>', start_drag)
canvas.tag_bind(image_item, '<B1-Motion>', drag)
root.mainloop()
When the mouse button is first pressed, the code stores the mouse coordinates:
drag_data['x']=event.x
drag_data['y']=event.y
During movement, the change is calculated:
dx=event.x-drag_data['x']
dy=event.y-drag_data['y']
The image moves by exactly the same change:
canvas.move(image_item, dx, dy)
This avoids the jump that can occur when the image is immediately moved to the mouse pointer's absolute coordinates.
When several Canvas items are draggable, the built-in current tag identifies the item currently under the mouse pointer.
import tkinter as tk
root=tk.Tk()
canvas=tk.Canvas(root, width=500, height=300, bg='white')
canvas.pack()
canvas.create_rectangle(50, 50, 120, 100, fill='red', tags='draggable')
canvas.create_oval(180, 60, 240, 120, fill='lightblue', tags='draggable')
drag_data={'item': None, 'x': 0, 'y': 0}
def start_drag(event):
current=canvas.find_withtag('current')
if current:
drag_data['item']=current[0]
drag_data['x']=event.x
drag_data['y']=event.y
def drag(event):
item=drag_data['item']
if item is None:
return
dx=event.x-drag_data['x']
dy=event.y-drag_data['y']
canvas.move(item, dx, dy)
drag_data['x']=event.x
drag_data['y']=event.y
def stop_drag(event):
drag_data['item']=None
canvas.tag_bind('draggable', '<ButtonPress-1>', start_drag)
canvas.tag_bind('draggable', '<B1-Motion>', drag)
canvas.tag_bind('draggable', '<ButtonRelease-1>', stop_drag)
root.mainloop()
Both shapes share the draggable tag, but only the item under the pointer is stored and moved.
This:
rect=canvas.create_rectangle(10, 10, 50, 50)
canvas.move(rect, 10, 0)
uses a Canvas item id. rect is not a Tkinter widget.
This moves relative to the current location:
canvas.move(rect, 20, 10)
This changes the coordinates directly:
canvas.coords(rect, 50, 60, 100, 110)
Move up:
canvas.move(rect, 0, -10)
Move down:
canvas.move(rect, 0, 10)
Instead of:
command=lambda: my_move('x')
use:
command=my_move
when the function does not need an event object.
If one Canvas item should be draggable, bind the event directly to that item:
canvas.tag_bind(image_item, '<ButtonPress-1>', start_drag)
canvas.tag_bind(image_item, '<B1-Motion>', drag)
A drag calculation requires the previous mouse coordinates:
drag_data['x']=event.x
drag_data['y']=event.y
Without this, the first movement can jump by an incorrect offset.
Use an individual item id or determine the item under the pointer instead of always moving a single global collection.
Keep the image object in a variable:
photo=tk.PhotoImage(file='plus2net.png')
image_item=canvas.create_image(100, 80, image=photo)
Prefer a project-relative file such as:
photo=tk.PhotoImage(file='plus2net.png')
or build the path from the application's directory.
Use bbox() to check its current visual bounds when movement must remain inside the visible Canvas.
Canvas.move() moves an existing Canvas item by a relative offset.move(tag_or_id, dx, dy).create_rectangle() return item ids.move() for relative movement.coords() when reading or replacing coordinates.bbox() when the item's displayed bounding box is required.move() moves all matching items.create_image() can be moved like other Canvas items.PhotoImage while it is displayed.tag_bind() to attach dragging directly to a Canvas item.current tag can identify the item under the mouse pointer.draggable.bbox() checks when objects must remain inside Canvas boundaries.Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.