最终目标是构建一个图表,即y轴加主字段。 主字段由单独的框组成(假设为 20x20 像素)。至少最多 500x500 盒,我希望更多。 主字段应该是可拖动的。
在 Stackoverflow 的帮助下,我选择将图像放在主画布上,而不仅仅是标签。并使用 canvas.move() 而不是在拖动时重新绘制元素。 y 轴也是画布,但带有标签。
此解决方案适用于小型画布(100x100 元素),但在拖动 500x500 元素画布时遇到延迟(滞后、惯性)。
然后我尝试添加额外的画布,这是内部的。这个想法是在画布上移动内部画布(带有图像),而不是仅在单个画布上移动图像。
因此,内部画布必须放置在窗口内。我用“canv”标签标记了窗口。并在调用move函数时参考它。
但我只需单击内部画布周边的 1 像素小区域即可捕获窗口。
为了在单击主字段本身(而不仅仅是 1-pxl 画布轮廓)时拖动图表的主字段,应该更改什么?或者解决方案架构发生变化?
并且需要记住,y 轴必须与主场的垂直移动同步。我成功地通过以前的解决方案实现了这种同步,但通过当前的解决方案却未能实现。
import tkinter as tk
from functools import partial
class DraggableCanvas(tk.Canvas):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.start_pos = 0, 0
self.bind('<Button>', self.on_drag_start)
self.image = tk.PhotoImage(file='images\\image_ .png') # icon-like 20*20 pxls image
self.tag_bind('canv', '<B1-Motion>', partial(self.on_drag_motion, 'canv'))
self.add_images()
self.add_internal_canvas()
def on_drag_start(self, event):
self.start_pos = event.x, event.y
def add_internal_canvas(self):
int_canvas = tk.Canvas(self, width=200, height=200)
self.create_window(50, 25, anchor='nw', window=int_canvas, tags='canv')
for i in range(100):
for j in range(100):
int_canvas.create_image(10+j*21, 10+i*21, image=self.image)
def add_images(self):
pass
# for i in range(100):
# for j in range(100):
# self.create_image(10+j*21, 10+i*21, image=self.image, tags='img')
def on_drag_motion(self, imgs_tag, event):
self.tag_raise(imgs_tag)
self.move(imgs_tag, event.x - self.start_pos[0], event.y - self.start_pos[1])
self.on_drag_start(event)
def main():
root = tk.Tk()
canvas1 = tk.Canvas(root, bg='#303040', width=100, height=480)
canvas1.pack(side='left', anchor='n')
for i in range(10):
lbl = tk.Label(canvas1, text='y_axis', width=5, bg='#303030', fg='white')
lbl.pack(anchor='n')
DraggableCanvas(root, bg='grey', width=750, height=480).pack(anchor='nw')
root.mainloop()
if __name__ == "__main__":
main()
我得到了这个:
import tkinter as tk
from functools import partial
class DraggableCanvas(tk.Canvas):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.start_pos = 0, 0
#self.bind('<Button>', self.on_drag_start)
#self.tag_bind('canv', '<B1-Motion>', partial(self.on_drag_motion, 'canv'))
self.image = tk.PhotoImage(file=r"imgs/pawn.black.png") # icon-like 20*20 pxls image
self.add_images()
self.add_internal_canvas()
def on_drag_start(self, event):
self.tag_raise("canv")
eventx = event.x_root-self.bbox("canv")[0] + self.winfo_rootx()
eventy = event.y_root-self.bbox("canv")[1] + self.winfo_rooty()
self.start_pos = eventx, eventy
def add_internal_canvas(self):
int_canvas = tk.Canvas(self, width=200, height=200, bd=0)
self.create_window(50, 25, anchor='nw', window=int_canvas, tags="canv")
int_canvas.bind('<Button>', self.on_drag_start)
int_canvas.bind('<B1-Motion>', self.on_drag_motion)
for i in range(100):
for j in range(100):
int_canvas.create_image(10+j*21, 10+i*21, image=self.image)
def add_images(self):
pass
for i in range(100):
for j in range(100):
self.create_image(10+j*21, 10+i*21, image=self.image)
def on_drag_motion(self, event):
dx = event.x_root + self.winfo_rootx()-self.start_pos[0]
dy = event.y_root + self.winfo_rooty()-self.start_pos[1]
# The max(..., 0) claps it down so that it can't go off of the screen to the left/top (you can remove it)
self.moveto("canv", max(dx, 0), max(dy, 0))
def main():
root = tk.Tk()
canvas1 = tk.Canvas(root, bg='#303040', width=100, height=480)
canvas1.pack(side='left', anchor='n')
for i in range(10):
lbl = tk.Label(canvas1, text='y_axis', width=5, bg='#303030', fg='white')
lbl.pack(anchor='n')
DraggableCanvas(root, bg='grey', width=750, height=480).pack(anchor='nw')
root.mainloop()
if __name__ == "__main__":
main()
我认为您的代码的问题在于
int_canvas
吞噬了其中发生的所有鼠标事件,因此 on_drag_start
和 on_drag_motion
不会被调用。为了解决这个问题,我绑定到 int_canvas
并稍微更改了 on_drag_start
和 on_drag_motion
中的计算。我认为我的代码效率不是很高,所以如果有人想更新它,请随意这样做。
现在有一个问题,您可以将画布拖到屏幕之外。我通过使用
max(..., 0)
添加了一个小修复,但这仅适用于左侧/顶部。
至于滞后问题,我在我的计算机上没有看到它,但如果您仍然看到它,请尝试最小化
tkinter
必须处理的事情的数量。例如,您可以使用 PIL
组合一些图像并将其显示为 1 个单图像。