Home > OS >  Show all items on the tkinter canvas
Show all items on the tkinter canvas

Time:04-11

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()
  • Related