I have a simple program in tkinter that allow to zoom in/out and pan the canvas. I am trying to define a button that will enable me to show all the items on the canvas. The code is below:
##### Imports #####
import tkinter as tk
from tkinter import ttk
###################################################################
# FUNCTIONS #
###################################################################
# Defining zoom function -----------------------------------------------------------------------------------------------
def wheel(mainCanvas, event, arg):
global x, y, delta, fontSize
scale = 1.0
if event.delta < 0:
scale *= delta
fontSize *=delta
if event.delta > 0:
scale /= delta
fontSize /=delta
# Rescaling canvas
x = mainCanvas.canvasx(event.x)
y = mainCanvas.canvasy(event.y)
mainCanvas.scale('all', x, y, scale, scale)
mainCanvas.configure(scrollregion=mainCanvas.bbox('all'))
# Getting all the text item on the canvas
for i in mainCanvas.find_withtag("text"):
mainCanvas.itemconfigure(i, font=("Arial", int(fontSize)))
print(fontSize)
# Defining pan function ------------------------------------------------------------------------------------------------
def move_from(mainCanvas, event, arg):
global x, y
mainCanvas.scan_mark(event.x, event.y)
def move_to(mainCanvas, event, arg):
global x, y
mainCanvas.scan_dragto(event.x, event.y, gain=1)
###################################################################
# MAIN #
###################################################################
def main():
root = tk.Tk()
root.title('Scroll and zoom')
root.geometry("600x600")
mainFrame = ttk.Frame(root).grid()
vertBar = ttk.Scrollbar(root, orient='vertical')
horiBar = ttk.Scrollbar(root, orient='horizontal')
vertBar.grid(row=0, column=1, sticky='ns')
horiBar.grid(row=1, column=0, sticky='we')
mainCanvas = tk.Canvas(mainFrame, highlightthickness=0, xscrollcommand=horiBar.set, yscrollcommand=vertBar.set)
mainCanvas.grid(row=0, column=0, sticky='nswe')
# Binding scrollbars to the canvas and event binding to the canvas--------------------------------------------------
vertBar.configure(command=mainCanvas.yview)
horiBar.configure(command=mainCanvas.xview)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
global delta, fontSize
delta = 0.9
fontSize = 10
mainCanvas.bind('<ButtonPress-1>', lambda event, arg=mainCanvas: move_from(mainCanvas, event, arg))
mainCanvas.bind('<B1-Motion>', lambda event, arg=mainCanvas: move_to(mainCanvas, event, arg))
mainCanvas.bind('<MouseWheel>', lambda event, arg=mainCanvas: wheel(mainCanvas, event, arg))
# Drawing on the canvas and configuring canvas ---------------------------------------------------------------------
x0 =100
y0 = 100
x1 = 200
y1 = 200
rect1 = mainCanvas.create_rectangle(x0, y0, x1, y1, outline='black', fill='grey', activefill='grey')
rect2 = mainCanvas.create_rectangle(-x0*2, -y0*2, -x1*2, -y1*2, outline='black', fill='grey', activefill='grey')
text = mainCanvas.create_text(100, 100, anchor='nw', text='Scroll to zoom', font=("Arial", int(fontSize)), tags='text', width=0)
mainCanvas.configure(scrollregion=mainCanvas.bbox('all'))
buttonShowAll = tk.Button(root, text='Show All')
buttonShowAll.grid()
root.mainloop()
###################################################################
# RUN #
###################################################################
def run():
print('\nStart script')
main()
print('Finished script')
run()
What I am trying to achieve is that at any level of zoom or location of pan, the button Show all
can show all the items on the canvas. It would be great to achieve this without mainCanvas.delete('all')
or mainCanvas.destroy()
methods.
CodePudding user response:
You have to get size of bbox('all')
with items, and size of widget Canvas
to calculate scale and scale it
def show_all(mainCanvas):
region = mainCanvas.bbox('all')
old_width = region[2] - region[0]
old_height = region[3] - region[1]
print('old:', old_width, old_height)
new_width = mainCanvas.winfo_width()
new_height = mainCanvas.winfo_height()
print('new:', new_width, new_height)
scale = min(new_width/old_width, new_height/old_height)
print('scale:', scale)
centerx = (region[0] region[2])/2
centery = (region[1] region[3])/2
print('center:', centerx, centery)
# rescale objects
mainCanvas.scale('all', centerx, centery, scale, scale)
mainCanvas.configure(scrollregion=mainCanvas.bbox('all'))
# rescale font and text
fontSize *= scale
for i in mainCanvas.find_withtag("text"):
mainCanvas.itemconfigure(i, font=("Arial", int(fontSize)))
#TODO: center objects on canvas
Full working code:
I had to add Button-4
, Button-5
and change wheel()
to run mousewheel
on Linux.
And I changed mainFrame
because you assigned None
to this variable.
##### Imports #####
import tkinter as tk
from tkinter import ttk
###################################################################
# FUNCTIONS #
###################################################################
# Defining zoom function -----------------------------------------------------------------------------------------------
def wheel(mainCanvas, event, arg):
global fontSize
print('wheel', event, arg)
scale = 1.0
if event.num == 5 or event.delta < 0:
scale *= delta
fontSize *=delta
if event.num == 4 or event.delta > 0:
scale /= delta
fontSize /=delta
# Rescaling canvas
x = mainCanvas.canvasx(event.x)
y = mainCanvas.canvasy(event.y)
mainCanvas.scale('all', x, y, scale, scale)
mainCanvas.configure(scrollregion=mainCanvas.bbox('all'))
# Getting all the text item on the canvas
for i in mainCanvas.find_withtag("text"):
mainCanvas.itemconfigure(i, font=("Arial", int(fontSize)))
# print(fontSize)
# Defining pan function ------------------------------------------------------------------------------------------------
def move_from(mainCanvas, event, arg):
print('move_from')
mainCanvas.scan_mark(event.x, event.y)
def move_to(mainCanvas, event, arg):
print('move_to')
mainCanvas.scan_dragto(event.x, event.y, gain=1)
def show_all(mainCanvas):
global fontSize
region = mainCanvas.bbox('all')
old_width = region[2] - region[0]
old_height = region[3] - region[1]
print('old:', old_width, old_height)
new_width = mainCanvas.winfo_width()
new_height = mainCanvas.winfo_height()
print('new:', new_width, new_height)
scale = min(new_width/old_width, new_height/old_height)
print('scale:', scale)
centerx = (region[0] region[2])/2
centery = (region[1] region[3])/2
print('center:', centerx, centery)
# rescale objects
mainCanvas.scale('all', centerx, centery, scale, scale)
mainCanvas.configure(scrollregion=mainCanvas.bbox('all'))
# rescale font and text
fontSize *= scale
for i in mainCanvas.find_withtag("text"):
mainCanvas.itemconfigure(i, font=("Arial", int(fontSize)))
#TODO: center objects on canvas
###################################################################
# MAIN #
###################################################################
def main():
global delta, fontSize
root = tk.Tk()
root.title('Scroll and zoom')
root.geometry("600x600")
mainFrame = ttk.Frame(root)
mainFrame.grid(row=0, column=0, sticky='news')
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
vertBar = ttk.Scrollbar(root, orient='vertical')
horiBar = ttk.Scrollbar(root, orient='horizontal')
vertBar.grid(row=0, column=1, sticky='ns')
horiBar.grid(row=1, column=0, sticky='we')
mainCanvas = tk.Canvas(mainFrame, highlightthickness=0, xscrollcommand=horiBar.set, yscrollcommand=vertBar.set)
mainCanvas.pack(fill='both', expand=True)
# Binding scrollbars to the canvas and event binding to the canvas--------------------------------------------------
vertBar.configure(command=mainCanvas.yview)
horiBar.configure(command=mainCanvas.xview)
delta = 0.9
fontSize = 10
mainCanvas.bind('<ButtonPress-1>', lambda event, arg=mainCanvas: move_from(mainCanvas, event, arg))
mainCanvas.bind('<B1-Motion>', lambda event, arg=mainCanvas: move_to(mainCanvas, event, arg))
mainCanvas.bind('<MouseWheel>', lambda event, arg=mainCanvas: wheel(mainCanvas, event, arg))
mainCanvas.bind('<Button-5>', lambda event, arg=mainCanvas: wheel(mainCanvas, event, arg))
mainCanvas.bind('<Button-4>', lambda event, arg=mainCanvas: wheel(mainCanvas, event, arg))
# Drawing on the canvas and configuring canvas ---------------------------------------------------------------------
x0 =100
y0 = 100
x1 = 200
y1 = 200
rect1 = mainCanvas.create_rectangle(x0, y0, x1, y1, outline='black', fill='grey', activefill='grey')
rect2 = mainCanvas.create_rectangle(-x0*2, -y0*2, -x1*2, -y1*2, outline='black', fill='grey', activefill='grey')
text = mainCanvas.create_text(100, 100, anchor='nw', text='Scroll to zoom', font=("Arial", int(fontSize)), tags='text', width=0)
mainCanvas.configure(scrollregion=mainCanvas.bbox('all'))
buttonShowAll = tk.Button(root, text='Show All', command=lambda:show_all(mainCanvas))
buttonShowAll.grid()
root.mainloop()
###################################################################
# RUN #
###################################################################
def run():
print('\nStart script')
main()
print('Finished script')
run()