我看到您的代码存在几个问题:
您将面向对象接口和函数式接口混合使用turtle模块。我建议您只选择一种,而不是两种接口都使用。请参见我的import
更改,以强制仅使用面向对象编程。
您正在使用低级tkinter鼠标和键盘事件,而非turtle自己的事件处理方法。我建议您尝试在turtle层面上进行操作(尽管与您的实现相比存在一些小问题,请参见下文)。
在事件处理程序中未关闭事件处理可能会导致意外的递归。在那些需要大量时间的事件处理程序中关闭事件可以清理图形界面。
这是我根据上述要求重构后的代码。唯一的问题是,与原始版本不同,“移动海龟到此处”和“开始拖曳”需要两次点击,一次是屏幕点击将海龟移动到当前位置,一次是点击海龟开始拖曳。这是由于turtle提供给tkinter事件的接口有所不同。(Python 3在这方面略有改进,但对于此情况并不适用。)
为了解决这一问题,我使用了一个更大的海龟光标,并添加了朝向逻辑:
from turtle import Turtle, Screen, mainloop
WIDTH = 600
HEIGHT = 300
def gothere(x, y):
screen.onscreenclick(gothere)
turtle.penup()
print("gothere (%d,%d)" % (x, y))
turtle.goto(x, y)
turtle.pendown()
screen.onscreenclick(gothere)
def movearound(x, y):
turtle.ondrag(None)
turtle.setheading(turtle.towards(x, y))
print("movearound (%d,%d)" % (x, y))
turtle.goto(x, y)
turtle.ondrag(movearound)
def release(x, y):
print("release (%d,%d)" % (x, y))
turtle.penup()
def reset():
print("reset")
turtle.clear()
screen = Screen()
screen.setup(WIDTH, HEIGHT)
turtle = Turtle('turtle')
turtle.speed('fastest')
turtle.ondrag(movearound)
turtle.onrelease(release)
screen.onscreenclick(gothere)
screen.onkey(reset, "Escape")
screen.listen()
mainloop()
但是请参见
这个答案,我展示了如何使tkinter的
onmove
事件可用于turtle。
...“移动到此处”然后“开始拖动”的限制对用户来说非常不舒适?我们该如何改进?
结合我上面的代码和我链接的替代答案,我们得到了这个解决方案,它类似于您开始的方式,但没有故障,并且以更符合海龟风格的方式呈现:
from turtle import Turtle, Screen, mainloop
from functools import partial
WIDTH = 600
HEIGHT = 300
VERBOSE = False
def onscreenmove(self, fun, btn=1, add=None):
if fun is None:
self.cv.unbind('<Button%s-Motion>' % btn)
else:
def eventfun(event):
fun(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale)
self.cv.bind('<Button%s-Motion>' % btn, eventfun, add)
def onscreenrelease(self, fun, btn=1, add=None):
if fun is None:
self.cv.unbind("<Button%s-ButtonRelease>" % btn)
else:
def eventfun(event):
fun(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale)
self.cv.bind("<Button%s-ButtonRelease>" % btn, eventfun, add)
def gothere(x, y):
if VERBOSE:
print("gothere (%d,%d)" % (x, y))
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
def movearound(x, y):
screen.onscreenmove(None)
if VERBOSE:
print("movearound (%d,%d)" % (x, y))
turtle.setheading(turtle.towards(x, y))
turtle.goto(x, y)
screen.onscreenmove(movearound)
def release(x, y):
if VERBOSE:
print("release (%d,%d)" % (x, y))
turtle.penup()
def reset():
if VERBOSE:
print("reset")
turtle.clear()
screen = Screen()
screen.setup(WIDTH, HEIGHT)
screen.onscreenrelease = partial(onscreenrelease, screen)
screen.onscreenmove = partial(onscreenmove, screen)
turtle = Turtle('turtle')
turtle.speed('fastest')
screen.onscreenclick(gothere)
screen.onscreenrelease(release)
screen.onscreenmove(movearound)
screen.onkey(reset, "Escape")
screen.listen()
mainloop()
sys.setrecursionlimit(90000)
对我来说有些可疑。如果你添加这个代码是因为之前遇到了一个非常长的堆栈跟踪的异常错误,那么我怀疑这个错误与你现在遇到的问题有关。 - Kevinsetrecursionlimit
行,则此代码偶尔会产生重复进入movearound
的回溯。我怀疑这是因为movearound
调用goto
,goto
调用update
,update
检查鼠标更新并可能再次调用movearound
。如果Tkinter不总是按接收顺序评估鼠标事件,则可能解释海龟的抖动移动。http://effbot.org/tkinterbook/widget.htm说在回调中使用`update()`可能会导致“恶性竞争条件”,这似乎正是这里发生的情况。 - Kevingoto()
。但如果他不这样做,那么OP该如何实现他想要的行为呢? - Kevinself.tracer(2,0)
似乎解决了你的问题! - TwistedSim