python - python turtle 奇怪的光标跳转
问题描述
我正在尝试用鼠标使用海龟绘图,我得到了下面的演示代码,但有时在鼠标移动期间光标会跳转:
#!/usr/bin/env python
import turtle
import sys
width = 600
height = 300
def gothere(event):
turtle.penup()
x = event.x
y = event.y
print "gothere (%d,%d)"%(x,y)
turtle.goto(x,y)
turtle.pendown()
def movearound(event):
x = event.x
y = event.y
print "movearound (%d,%d)"%(x,y)
turtle.goto(x,y)
def release(event):
print "release"
turtle.penup()
def circle(x,y,r):
turtle.pendown()
turtle.goto(x,y)
turtle.circle(r)
turtle.penup()
return
def reset(event):
print "reset"
turtle.clear()
#------------------------------------------------#
sys.setrecursionlimit(90000)
turtle.screensize(canvwidth=width, canvheight=height, bg=None)
turtle.reset()
turtle.speed(0)
turtle.setup(width, height)
canvas = turtle.getcanvas()
canvas.bind("<Button-1>", gothere)
canvas.bind("<B1-Motion>", movearound)
canvas.bind("<ButtonRelease-1>", release)
canvas.bind("<Escape>",reset)
screen = turtle.Screen()
screen.setworldcoordinates(0,height,width,0)
screen.listen()
turtle.mainloop()
#------------------------------------------------#
有关实际行为,请参见下面的 gif:
不确定是否有任何 API 调用错误!
解决方案
我发现您的代码有几个问题:
您正在将turtle 的面向对象接口与该模块的功能接口混合。我推荐一个或另一个,但不是两者兼而有之。请参阅我
import
对仅强制 OOP 的更改。您正在使用低级 tkinter 鼠标和键事件,而不是海龟自己的事件。我建议您尝试在海龟级别工作(尽管与您的实现相比,这会引入一个小故障,请参见下文。)
您通过不关闭事件处理程序中的事件来引入意外递归。在那些需要大量时间的处理程序中禁用事件将清理您的图形。
这是我对上述代码的修改。一个小故障是,与您原来的不同,“将海龟移动到此处”和“开始拖动”将需要两次单击,一次屏幕单击将海龟移动到当前位置,单击一次海龟开始拖动。这是由于 turtle 为 tkinter 事件提供的接口不同。(Python 3 在这方面要好一些,但不适用于这种情况。)
为了缓解这种情况,我使用了更大的海龟光标。我还添加了标题逻辑:
from turtle import Turtle, Screen, mainloop
WIDTH = 600
HEIGHT = 300
def gothere(x, y):
screen.onscreenclick(gothere) # disable events inside handler
turtle.penup()
print("gothere (%d,%d)" % (x, y))
turtle.goto(x, y)
turtle.pendown()
screen.onscreenclick(gothere)
def movearound(x, y):
turtle.ondrag(None) # disable events inside handler
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)
# screen.setworldcoordinates(0, HEIGHT, WIDTH, 0) # should work fine either way
turtle = Turtle('turtle')
turtle.speed('fastest')
turtle.ondrag(movearound)
turtle.onrelease(release)
screen.onscreenclick(gothere)
screen.onkey(reset, "Escape")
screen.listen()
mainloop() # normally screen.mainloop() but not in Python 2
但也请参阅这个答案,我在其中展示了如何使 tkinter 的onmove
事件对海龟可用。
...“移动到这里”然后“开始拖动”限制对用户来说非常不舒服?我们该如何改进呢?
将我上面的代码与我链接到的替代答案结合起来,我们得到的解决方案与您开始的地方相似,但没有小故障,而且风格更像乌龟:
from turtle import Turtle, Screen, mainloop
from functools import partial
WIDTH = 600
HEIGHT = 300
VERBOSE = False
def onscreenmove(self, fun, btn=1, add=None): # method missing from turtle.py
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): # method missing from turtle.py
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) # disable events inside handler
if VERBOSE:
print("movearound (%d,%d)" % (x, y))
turtle.setheading(turtle.towards(x, y))
turtle.goto(x, y)
screen.onscreenmove(movearound) # reenable events
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) # install missing methods
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() # normally screen.mainloop() but not in Python 2
推荐阅读
- asp.net-core - 如何命名并将 Sendgrid api 密钥和名称添加到 appSetting.json
- excel - Excel 中的单元格自引用
- sql - 连接来自同一个表的两个聚合查询 - SQL Server
- c# - UAC 和通过 Windows 服务执行作业
- autohotkey - 如果尚未启动程序,如何启动程序,如果已启动,则将焦点放在?
- awk - awk:当列总和在一个范围内时提取行
- javascript - laravel 验证,如何在客户端添加验证规则?
- regex - URL 的正则表达式将“.finance”显示为无效/错误
- c# - 在 Azure 表存储中写入后引发异常
- postgresql - Knex 和 PostgreSQL:如何删除唯一索引?