Tkinter画布失效问题(移动对象时剪切对象)

3

我对Python还比较陌生,目前正在尝试使用Tkinter,它看起来非常简单。我尝试使用以下代码实现一个简单的拖放效果(右键创建一个圆,左键允许拖动):

from tkinter import *

class Point:
    def __init__(self, ref, x, y):
        self.ref = ref
        self.x = x
        self.y = y

points = []
selected = None

def OnSelect(event):
    global selected
    for p in points:
        if event.x>=(p.x-10) and event.y>=(p.y-10) and event.x<(p.x+10) and event.y<(p.y+10):
            selected = p
            break

def OnMMove(event):
    if selected is not None:
        selected.x = event.x
        selected.y = event.y
        canvas.coords(selected.ref, event.x-10, event.y-10, event.x+10, event.y+10)

def OnStopDrag(event):
    global selected
    selected = None

def OnCreate(event):
    point = canvas.create_oval(event.x-10, event.y-10, event.x+10, event.y+10, fill="black")
    points.append(Point(point, event.x, event.y))

window = Tk()
window.wm_title("Python")

canvas = Canvas(window, width=800, height=600, background='white')
canvas.bind("<Button-1>", OnSelect)
canvas.bind("<B1-Motion>", OnMMove)
canvas.bind("<ButtonRelease-1>", OnStopDrag)
canvas.bind("<Button-3>", OnCreate)
canvas.pack(fill=BOTH, expand=YES)

window.mainloop()

如您所见,这段代码中我使用了canvas.cords来移动拖拽的对象。当鼠标慢慢地拖拽时,一切都正常运作,但是当鼠标快速移动时,拖拽的圆似乎会在一个矩形范围内被部分裁剪,如下图所示(当拖拽停止或减缓时,整个圆将被正确地完整绘制):

Drag and drop cropping issue illustrated

我之前在Win32 C应用程序中使用GDI时也遇到过类似的问题,当调用屏幕无效时,重绘窗口客户区域仅覆盖当前拖拽圆的初始位置。
实际上,在我的示例代码中创建的窗口位于不断完全重绘的窗口上方,例如视频游戏窗口时,拖拽元素时的裁剪效果不会出现,拖拽时整个圆会被正确地重绘。
是否有一种方法可以解决这个问题,例如设置画布使窗口无效时调用更广泛或整个客户区域?我想坚持使用Tkinter,所以我对切换到另一个GUI API/框架不是很感兴趣。此代码已在Windows 10上进行了测试。

1
我无法在Linux上复制此问题,也无法访问Windows系统。关于如何更改tkinter刷新窗口的方式,你无能为力。这在tkinter层面上是不可见的。 - Bryan Oakley
1个回答

1
这个答案可能会让你感到困惑(我也是)。但解决方法是在OnMMove中配置光标。以下是在Windows上为我工作的摘录。
def OnMMove(event):
    if selected is not None:
        canvas.configure(cursor='arrow')
        selected.x = event.x
        selected.y = event.y
        canvas.coords(selected.ref, event.x-10, event.y-10, event.x+10, event.y+10)

可能会导致另一轮布局...也许?只是猜测。-- 当我使用.after()设置椭圆形的coords()时,我注意到了相同的剪切问题...当它在每个任务调度器片段(在我的盒子上为15.625ms)运行时,它会被剪切,但如果它每次移动获取两个时间片,则看起来可以接受。 - Christoph Rackwitz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接