Home > Mobile >  What units does the scaling method work in tkinter canvas?
What units does the scaling method work in tkinter canvas?

Time:06-25

Trying to figure out how the scaling method in the Tkinter Canvas package works.

import tkinter

root = tkinter.Tk()
root.geometry("1200x800")
root.resizable(False,False)
root.title("Examples")

myCanvas = tkinter.Canvas(root, bg="white", height=700, width=1000)
myCanvas.create_rectangle(0,200,300,300, tags="myTag")
myCanvas.scale("myTag", 8200,1200,0.99,0.99)

myCanvas.create_rectangle(0,400,300,300, tags="myTag2")
myCanvas.move("myTag2",200,0)

input_elem = tkinter.Entry(root,width=50)
input_elem.grid(row=1, column=0)

btn = tkinter.Button(root,width=50, text="here", height=5)
btn.grid(row=1, column=2)

myCanvas.grid(row=0, column=0)
root.mainloop()

in the documentation I found this:

.scale(tagOrId, xOffset, yOffset, xScale, yScale) Scale all objects according to their distance from a point P=(xOffset, yOffset). The scale factors xScale and yScale are based on a value of 1.0, which means no scaling. Every point in the objects selected by tagOrId is moved so that its x distance from P is multiplied by xScale and its y distance is multiplied by yScale.

This method will not change the size of a text item, but may move it.

Based on this I would have expected the .scale() to work in the same units as the canvas (by default pixels) but it seems like it doesn't. My xOffset value is fairly large and the rectangle moved only very little. So I created a second rectangle to compare and realized it's scaling based off of the width of the canvas, so this :

myCanvas.scale("myTag", (20*0.99)*1000,1200,0.99,0.99)
myCanvas.move("myTag2",(0.99*200),0)

equals to the same xOffset. Why is is the scaling a factor of 10 though? Shouldn't (200*0.99)*1000 in the scaling method equal to 0.99*200 in the move method? Or can someone point me to a more detailed documentation?

CodePudding user response:

I think the best way to understand Canvas move and scale is to see them in action.

Here is a small interactive demo that may help.

It draws a small rectangle (square) at origin.

Control for moving (pick and place) the square is single Button-1 click

Control for scale is via keyboard (Up, Right) = grow (Down and Left) = shrink

Run the code, pick up the square and place it near canvas center.

Make it grow by pressing and holding Up or Right key. Now make it shrink by pressing and holding Down or Left key.

Try moving mouse pointer inside and outside of square while changing scale.

Note how the square moves away from your mouse pointer when growing and moves toward your mouse pointer when shrinking.

Exactly what one would expect from objects approaching or receding.

You can change scale and move square at the same time.

This works with objects line, polygon, rectangle and oval.

import tkinter as tk

class moveScale(tk.Tk):

    def __init__(self):
        super().__init__()

        # Remove various paddings so the entire canvas is visible
        self.canvas = tk.Canvas(
            self, highlightthickness = 0, borderwidth = 0,
            cursor = "crosshair")
        self.canvas.grid(sticky = tk.NSEW)

        self.xx = self.yy = 0 # Store for dynamic mouse pointer position
        self.con = False      # Flag controls move on|off
        self.tag = "myTag"

        self.sU = 1.005       # Scale up (growth)
        self.sD = 1 / self.sU # Scale down (shrink)

        self.canvas.create_rectangle(
            0, 0, 40, 40, fill = "", tags = self.tag)

        # Mouse and Keyboard bindings
        self.canvas.bind("<Motion>", self.mover)
        self.canvas.bind("<Button-1>", self.swap)

        self.canvas.event_add("<<BIG>>", "<Up>", "<Right>")
        self.canvas.event_add("<<GIB>>", "<Left>", "<Down>")

        self.canvas.bind("<<BIG>>", self.big)
        self.canvas.bind("<<GIB>>", self.small)
        # Keyboard input only works when canvas has the focus
        self.canvas.focus_force()

    # Movement control
    def swap(self, ev):
        self.con = self.con == False

    def mover(self, ev):
        x, y = self.canvas.canvasx(ev.x), self.canvas.canvasy(ev.y)
        if self.con:
            self.canvas.move(self.tag, x - self.xx, y - self.yy)
        self.xx, self.yy = x, y

    def big(self, ev):
        self.canvas.scale(self.tag, self.xx, self.yy, self.sU, self.sU)

    def small(self, ev):
        self.canvas.scale(self.tag, self.xx, self.yy, self.sD, self.sD)

main = moveScale()
main.mainloop()
  • Related