【问题标题】:turtle graphics: How do I implement a pause function?海龟图形:如何实现暂停功能?
【发布时间】:2020-03-29 17:13:36
【问题描述】:

我正在尝试使用 python 3 乌龟图形来做演示软件之类的事情:画一些东西,暂停击键以便演示者解释,然后画下一个东西。

这是我尝试过的一种解决方案(不起作用):

import turtle
import time

paused = False

def unpause():
    print("unpause() called")
    global paused
    paused = False

def pause():
    global paused
    paused = True
    while paused:
        time.sleep(0.1)


t = turtle.Turtle()

# set up listener
t.screen.listen()
t.screen.onkeypress(unpause)

# draw something
t.hideturtle()
t.pensize(5)
t.speed(1)
t.pu()
t.goto(-300,-300)
t.pd()
t.goto(-300, 300)

# pause until key is pressed
pause()

# draw some more
t.pu()
t.goto(300,-300)
t.pd()
t.goto(300, 300)

t.screen.mainloop()

问题是睡眠调用循环完全阻止了按键被检测到,即使我使用了一个非常短(100 毫秒)睡眠的 while 循环。

如果我在绘制第一行时按了一个键,我会在控制台中看到“unpause() called”,因此我知道键绑定处于活动状态。

为什么没有检测到按键?我不知道内部原理,但我认为击键会记录在某个缓冲区中,并且在睡眠调用之间的休息期间,侦听器将读取缓冲区并取消设置 paused 全局变量。这不会发生。

还有其他方法可以实现吗?

这是在 Debian Linux 系统上。

【问题讨论】:

    标签: python python-3.x turtle-graphics python-turtle


    【解决方案1】:

    Turtle 图形基于 tkinter,它是一个事件驱动的 GUI 框架,因此您无法像在常规程序驱动程序中那样执行操作 — 请参阅 @Bryan Oakley 对问题 Tkinter — executing functions over time 的回答以获得更详细的解释(尽管turtle 模块隐藏了大部分这些细节)。无论如何,这个事实意味着你不应该像这样在一个紧密的循环中调用time.sleep(),因为一切都必须在不干扰mainloop() 的运行的情况下发生。

    避免调用time.sleep() 的“技巧”是使用turtle.ontimer() 函数安排对全局变量的定期检查——所以如果它的第一部分如下所示,您的程序将可以运行:

    import turtle
    
    paused = False
    
    def unpause():
        print("unpause() called")
        global paused
        paused = False
    
    def pause():
        global paused
        paused = True
        pausing()  # Start watching for global to be changed.
    
    def pausing():
        if paused:
            turtle.ontimer(pausing, 250)  # Check again after delay.
        # else quit checking.
    
    t = turtle.Turtle()
    
    # set up listener
    t.screen.onkeypress(unpause)  # Reversed order of
    t.screen.listen()             # these two statements.
    
    # draw something
    t.hideturtle()
    t.pensize(5)
    t.speed(1)
    t.pu()
    t.goto(-300,-300)
    t.pd()
    t.goto(-300, 300)
    
    # pause until key is pressed
    pause()
    
    # draw some more
    t.pu()
    t.goto(300,-300)
    t.pd()
    t.goto(300, 300)
    
    t.screen.mainloop()
    

    【讨论】:

    • 如果我按任意键程序就会停止,它对你有用吗?
    • 抱歉,此代码对我不起作用。如果我不按任何键,第二行仍然会被绘制。我认为解决方案可以以某种方式使用ontimer,但它似乎只是确保在计时器时间到时调用该函数:它不会停止主程序代码的执行。我通过添加这个函数并在绘制第一行之前调用它来测试这一点:def disrupt(): global t t.goto(t.ycor() - 10, t.xcor() + 10) turtle.ontimer(disrupt, 500) 线条绘制在中间中断并绘制了一个有趣的图案。
    • 呸,StackOverflow 只会让我编辑我的 cmets 5 分钟。我希望可以推断出我发布的 python 函数中的换行符。
    • 您不能在 cmets 中放置换行符,因此它们不是发布多行代码的好地方。无论如何,250 毫秒延迟可能太长了,试着让它更短。同时,我会看看我是否能想出一个更好的解决方案。
    【解决方案2】:

    采纳您的建议给我的想法(感谢 martineau 和 kederrac!)我能够想出一个解决方案。它涉及将我的每个绘图任务包装在一个函数中,然后使用一个调度函数,该函数要么等待带有ontimer 循环的按键,要么调用下一个绘图函数。

    这个概念验证代码完全使用了太多的全局变量,但它展示了这种技术:

    import turtle
    
    t = turtle.Turtle()
    paused = False
    current_task = 0
    
    def unpause():
        global paused
        paused = False
    
    def turtle_setup():
        global t
        t.screen.onkeypress(unpause)
        t.screen.listen()
        t.hideturtle()
        t.pensize(5)
        t.speed(1)
    
    def draw_task_finished():
        global paused, current_task, drawing_tasks
        current_task += 1
        paused = True
        if current_task < len(drawing_tasks):
            draw_task_after_keypress()
    
    def draw_task_after_keypress():
        global paused, current_task
        if paused:
            turtle.ontimer(draw_task_after_keypress, 100)
        else:
            drawing_tasks[current_task]()
    
    def draw_thing_one():
        global t
        t.pu()
        t.goto(-300,-300)
        t.pd()
        t.goto(-300, 300)
        draw_task_finished()
    
    def draw_thing_two():
        global t
        t.pu()
        t.goto(300,-300)
        t.pd()
        t.goto(300, 300)
        draw_task_finished()
    
    drawing_tasks = [draw_thing_one, draw_thing_two]
    
    turtle_setup()
    drawing_tasks[0]()
    
    t.screen.mainloop()
    

    【讨论】:

      【解决方案3】:

      你可以使用turtle.done()function. 只需创建一个 input() 函数,如果输入了输入,程序就会运行。 我用基本方法尝试过。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-06
        • 1970-01-01
        • 1970-01-01
        • 2018-05-30
        • 2014-03-18
        • 1970-01-01
        • 1970-01-01
        • 2012-05-17
        相关资源
        最近更新 更多