英文:
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
or0.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
or0.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
命令的XSCALE
和YSCALE
参数在多次调用时会累积。由于项目的原始坐标会因四舍五入误差而发生变化,我不建议使用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 > 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)
...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论