I am working on a app which loads floor plans into a image viewer so they can be zoomed for electrical workers to evaluate. I am having a slight issue with centering the image.
I have 2 questions I would love help with.
I have made a reset button which when loaded and clicked centers the image. This works as I would like. However, once the image is moved on the canvas and the reset button is pressed the image instantly slides to the side and doesn't center anymore. So firstly how would I overcome this? I have tried messing with canvas.move, canvas.moveto and canvas.coords with no success.
The second issue is how would I center the image when the app is first loaded. I tried adding self.canvas.scan_mark and self.canvas.scan_dragto to the class init however this doesnt seem to work. I am guessing because the order of how the GUI is loaded.
Reference image is here:
To replicate open app then press reset button. Image will center then move the canvas anywhere with your mouse randomly and press the reset button again, now the image moves the left hand side and doesn't center anymore.
example of code below. Thanks
import random
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
class AutoScrollbar(ttk.Scrollbar):
''' A scrollbar that hides itself if it's not needed.
Works only if you use the grid geometry manager '''
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.grid_remove()
else:
self.grid()
ttk.Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise tk.TclError('Cannot use pack with this widget')
def place(self, **kw):
raise tk.TclError('Cannot use place with this widget')
class Zoom(ttk.Frame):
''' Simple zoom with mouse wheel '''
def __init__(self, mainframe, path):
''' Initialize the main Frame '''
ttk.Frame.__init__(self, master=mainframe)
self.master.title('zoom')
self.master.geometry("1200x800")
# Vertical and horizontal scrollbars for canvas
vbar = AutoScrollbar(self.master, orient='vertical')
hbar = AutoScrollbar(self.master, orient='horizontal')
vbar.grid(row=0, column=1, sticky='ns')
hbar.grid(row=1, column=0, sticky='we')
# Open image
self.image = Image.open(path)
# Create canvas and put image on it
self.canvas = tk.Canvas(self.master, highlightthickness=0,
xscrollcommand=hbar.set, yscrollcommand=vbar.set)
self.button = tk.Button(self.master,text="Reset Canvas to center",command=self.reset_canvas)
self.button.grid()
self.canvas.grid(row=0, column=0, sticky='nswe')
#self.canvas.scan_mark(0, 0)
#self.canvas.scan_dragto(200, 0, gain=1)
vbar.configure(command=self.canvas.yview) # bind scrollbars to the canvas
hbar.configure(command=self.canvas.xview)
# Make the canvas expandable
self.master.rowconfigure(0, weight=1)
self.master.columnconfigure(0, weight=1)
# Bind events to the Canvas
self.canvas.bind('<ButtonPress-1>', self.move_from)
self.canvas.bind('<B1-Motion>', self.move_to)
self.canvas.bind('<MouseWheel>', self.wheel) # with Windows and MacOS, but not Linux
self.canvas.bind('<Button-5>', self.wheel) # only with Linux, wheel scroll down
self.canvas.bind('<Button-4>', self.wheel) # only with Linux, wheel scroll up
# Show image and plot some random test rectangles on the canvas
self.imscale = 1.0
self.imageid = None
self.delta = 0.75
width, height = self.image.size
minsize, maxsize = 5, 20
for n in range(10):
x0 = random.randint(0, width - maxsize)
y0 = random.randint(0, height - maxsize)
x1 = x0 + random.randint(minsize, maxsize)
y1 = y0 + random.randint(minsize, maxsize)
color = ('red', 'orange', 'yellow', 'green', 'blue')[random.randint(0, 4)]
self.canvas.create_rectangle(x0, y0, x1, y1, outline='black', fill=color,
activefill='black', tags=n)
# Text is used to set proper coordinates to the image. You can make it invisible.
self.text = self.canvas.create_text(0, 0, anchor='nw', text='Scroll to zoom')
self.show_image()
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
def reset_canvas(self):
print("reset pressed")
self.canvas.scan_dragto(200, 0,gain=1)
def move_from(self, event):
''' Remember previous coordinates for scrolling with the mouse '''
self.canvas.scan_mark(event.x, event.y)
def move_to(self, event):
''' Drag (move) canvas to the new position '''
self.canvas.scan_dragto(event.x, event.y, gain=1)
def wheel(self, event):
''' Zoom with mouse wheel '''
scale = 1.0
# Respond to Linux (event.num) or Windows (event.delta) wheel event
if event.num == 5 or event.delta == -120:
scale *= self.delta
self.imscale *= self.delta
if event.num == 4 or event.delta == 120:
scale /= self.delta
self.imscale /= self.delta
# Rescale all canvas objects
x = self.canvas.canvasx(event.x)
y = self.canvas.canvasy(event.y)
self.canvas.scale('all', x, y, scale, scale)
self.show_image()
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
def show_image(self):
''' Show image on the Canvas '''
if self.imageid:
self.canvas.delete(self.imageid)
self.imageid = None
self.canvas.imagetk = None # delete previous image from the canvas
width, height = self.image.size
new_size = int(self.imscale * width), int(self.imscale * height)
imagetk = ImageTk.PhotoImage(self.image.resize(new_size))
# Use self.text object to set proper coordinates
self.imageid = self.canvas.create_image(self.canvas.coords(self.text),
anchor='nw', image=imagetk)
self.canvas.lower(self.imageid) # set it into background
self.canvas.imagetk = imagetk # keep an extra reference to prevent garbage-collection
path = 'floor_plan.jpg' # place path to your image here
root = tk.Tk()
app = Zoom(root, path=path)
root.mainloop()
