Tkinter – 在Canvas中缩放文本和其他元素

huangapple go评论58阅读模式
英文:

Tkinter - Zoom with text and other elements in Canvas

问题

I'm trying to add support for zooming in and out inside a Canvas widget, containing both text element (created using create_text) and non-text elements, such as rectangles (created with create_rectangle), etc.

So far, I made the following MRE using both part of this answer and this one:

import tkinter as tk
from tkinter.font import Font

root = tk.Tk()

canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()

font = Font(family="Arial", size=10)
fontsize = 10

# Add elements to canvas
rectangle = canvas.create_rectangle(100, 100, 300, 200, fill='red')
oval = canvas.create_oval(150, 150, 250, 250, fill='blue')
text = canvas.create_text(200, 175, text="Hello", font=font)

def do_zoom(event):
    global fontsize

    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)
    factor = 1.001 ** event.delta

    if (event.delta > 0):
        fontsize *= 1.1
        font.configure(size=int(fontsize))
    elif (event.delta < 0):
        fontsize *= 0.9
        font.configure(size=int(fontsize))
    canvas.scale("all", x, y, factor, factor)


canvas.bind("<MouseWheel>", do_zoom)
canvas.bind('<ButtonPress-1>', lambda event: canvas.scan_mark(event.x, event.y))
canvas.bind("<B1-Motion>", lambda event: canvas.scan_dragto(event.x, event.y, gain=1))

root.mainloop()

This seems to work, but has one or two issues:

  • Once the font size gets to 0 or 0.0??? in floats (happens when zooming out), the font size doesn't match with the actual visual size of the font, as it seems to be fixed in a higher size (can be seen on this gif here)
  • When zooming in and out fast enough, repeatedly, a discrepancy can be seen in the font size on the previous mousewheel scrolling and the next one (can be seen by printing the font size).

In short, I'm wondering if there is a way, either to fix the above (and the reasons for them happening, aside from my own conjecture) or if there is a better way to handle this.

英文:

I'm trying to add support for zooming in and out inside a Canvas widget, containing both text element (created using create_text) and non-text elements, such as rectangles (created with create_rectangle), etc.

So far, I made the following MRE using both part of this answer and this one:

import tkinter as tk
from tkinter.font import Font

root = tk.Tk()

canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()

font = Font(family="Arial", size=10)
fontsize = 10

# Add elements to canvas
rectangle = canvas.create_rectangle(100, 100, 300, 200, fill='red')
oval = canvas.create_oval(150, 150, 250, 250, fill='blue')
text = canvas.create_text(200, 175, text="Hello", font=font)

def do_zoom(event):
    global fontsize

    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)
    factor = 1.001 ** event.delta

    if (event.delta > 0):
        fontsize *= 1.1
        font.configure(size=int(fontsize))
    elif (event.delta < 0):
        fontsize *= 0.9
        font.configure(size=int(fontsize))
    canvas.scale("all", x, y, factor, factor)


canvas.bind("<MouseWheel>", do_zoom)
canvas.bind('<ButtonPress-1>', lambda event: canvas.scan_mark(event.x, event.y))
canvas.bind("<B1-Motion>", lambda event: canvas.scan_dragto(event.x, event.y, gain=1))

root.mainloop()

This seems to work, but has one or two issues:

  • Once the font size get to 0 or 0.0??? in floats (happen when zooming out), the font size doesn't match with the actual visual size of the font, as it seems to be fixed in a higher size (can be seen on this gif here)
  • When zooming in and out fast enough, repeatedly, a discrepancy can be seen on the font size on previous mousewheel scrolling, and the next one (can be seen by printing the font size).

In short, I'm wondering if there is a way, either to fix the above (and the reasons for them happening, aside from my own conjecture) or if there is a better way to handle this.

答案1

得分: 4

正数被视为点,负数被视为像素。您需要编写代码来处理这一点。

官方的tcl/tk文档对“size”选项有以下解释:

"如果大小参数是正数,它被解释为点的大小。如果大小是负数,它的绝对值被解释为像素的大小。如果无法以指定的大小显示字体,将选择附近的大小。如果大小未指定或为零,将选择与平台相关的默认大小。"

英文:

Positive numbers are treated as points, negative numbers are treated as pixels. You will need to write code to account for that.

The official tcl/tk documentation says this about the size option:

”If the size argument is a positive number, it is interpreted as a size in points. If size is a negative number, its absolute value is interpreted as a size in pixels. If a font cannot be displayed at the specified size, a nearby size will be chosen. If size is unspecified or zero, a platform-dependent default size will be chosen.”

答案2

得分: 2

因为在您的实现中,画布项的坐标缩放因子与字体大小的缩放因子不同。请注意画布的scale命令的XSCALEYSCALE参数在多次调用时会累积。由于项目的原始坐标会因四舍五入误差而发生变化,我不建议使用scale()来实现缩放(例如,请参见多边形的实现)。

无论如何,如果您不介意四舍五入误差,可以这样做:

...
factor = 1.0
def do_zoom(event):
    global factor

    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)

    if (event.delta > 0):
        ratio = 1.1
    elif (event.delta < 0):
        ratio = 0.9
    factor *= ratio

    font.configure(size=int(fontsize*factor))
    canvas.scale("all", x, y, ratio, ratio)
...
英文:

That's because the zoom factor for the coordinates of the canvas items and the zoom factor for the font size are different in your implementation. Keep in mind the XSCALE and YSCALE parameters of the scale command of the canvas accumulate for repeated calls. Because the original coordinates of the items are transformed with roundoff errors, I don't recommend using the scale() to implement zooming. (See the implementation of the polygon, for example.)

Anyway, if you don't mind the roundoff errors, do like this.

...
factor = 1.0
def do_zoom(event):
    global factor

    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)

    if (event.delta &gt; 0):
        ratio = 1.1
    elif (event.delta &lt; 0):
        ratio = 0.9
    factor *= ratio

    font.configure(size=int(fontsize*factor))
    canvas.scale(&quot;all&quot;, x, y, ratio, ratio)
...

huangapple
  • 本文由 发表于 2023年4月11日 08:30:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75981635.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定