python turtle奇怪的光标跳

时间:2018-04-30 18:01:22

标签: python turtle-graphics

我正在尝试使用鼠标绘制龟,我在下面的演示代码工作,但有时鼠标移动时光标跳跃:

//Override method for main menu
@Override
public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()) {
    case R.id.about_us:
        //open AboutUsActivity

        return(true);
    case R.id.about_measures:
        //add the function to perform here
        return(true);
    case R.id.feedback:
        //add the function to perform here
        return(true);
    case R.id.rate:
        //add the function to perform here
        return(true);
}
    return(super.onOptionsItemSelected(item));
}

请参阅下面的gif了解实际行为:

enter image description here

不确定是否有任何API调用错误!

3 个答案:

答案 0 :(得分:5)

我发现您的代码有几个问题:

  • 您正在将面向对象的界面与乌龟混合 该模块的功能接口。我推荐一个或者 其他,但不是两个。请参阅我的import更改以强制仅限OOP。

  • 您正在使用低级tkinter鼠标和键事件而不是 龟自己的事件。我建议你尝试在龟级工作 (虽然这比您的实现引入了一个小故障,请参阅 下文)。

  • 您通过不关闭事件来引入意外的递归 在你的事件处理程序中。禁用这些处理程序中的事件 花费大量时间将清理你的图形。

这是我在上面的代码中对代码的修改。一个小故障是,与你的原始不同,"移动龟在这里"并且"开始拖动"将点击两次,单击一次屏幕将乌龟移动到当前位置,然后单击一次乌龟开始拖动。这是由于乌龟为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

但请参阅this answer,其中我展示了如何让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

答案 1 :(得分:3)

我同意cdlane的意见,尽可能避免使用Tkinter级别的事件绑定,但我认为在不改变整体设计的情况下处理问题可能也很有趣。我的解决方案只需要额外导入:

from collections import deque

新版movearound

pending = deque()
def movearound(event):
    x = event.x
    y = event.y
    pending.append((x,y))
    if len(pending) == 1:
        while pending:
            x,y = pending[0]
            turtle.goto(x,y)
            pending.popleft()

正如我在评论中指出的那样,如果窗口的事件队列被备份,goto可以调用movearound,这可能导致堆栈溢出或竞争条件使得乌龟以不寻常的方式移动。这种方法旨在通过仅让movearound调用goto的最顶层实例来防止任意深度递归。 if len(pending) == 1:只能成功进行非递归调用;所有递归调用都会看到比这更大的队列。 while pending:循环遍历所有构建的事件,按照它们到达的顺序处理它们。

结果:一只尽职尽责地跟随光标路径的乌龟:

enter image description here

答案 2 :(得分:0)

添加turtle.tracer(2,0)似乎会使问题消失。这可能是一个临时解决方案,因为我不知道它是否只是隐藏了另一个问题。

...
screen = turtle.Screen()
screen.setworldcoordinates(0,height,width,0)
screen.listen()
turtle.tracer(2, 0)  # This line

turtle.mainloop()
...

在文档中我们可以阅读:

  

打开/关闭海龟动画并设置更新图纸的延迟。

正如@Keven指出的那样,问题似乎是回调中的隐式update。这会将update调用次数减少2.我认为它也可能会删除递归调用。

注意:我不知道原因,但删除screen.setworldcoordinates(0,height,width,0)可以消除毛刺行为,但仍然存在对update的递归调用。