Python Tkinter 大型自定义图表拖放功能的性能和其他问题

问题描述 投票:0回答:1

最终目标是构建一个图表,即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()
python performance tkinter drag-and-drop
1个回答
0
投票

我得到了这个:

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 个单图像。

© www.soinside.com 2019 - 2024. All rights reserved.