-->

“Stop” Button in Tkinter

2019-09-21 18:19发布

问题:

I'm trying to have a turtle animation start with a button and stop with a button. It's very easy to start with a button but I can't seem to be able to figure out a stop button? Here's my code so far:

import turtle
import tkinter as tk
def start():
    t.forward(100)
    t.right(90)
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.right(90)
    t.forward(100)
    t.right(90)
    t.forward(100)

def stop():
    t.stop

def clear():
    canvas.delete("all")

root = tk.Tk()
canvas = tk.Canvas(width = 500, height = 500)
canvas.pack()

t = turtle.RawTurtle(canvas)

tk.Button(text = "Start", command = start).pack(side = tk.LEFT)
tk.Button(text = "Stop", command = stop).pack(side = tk.LEFT)
tk.Button(text = "Clear", command = clear).pack(side = tk.LEFT)

root.mainloop()

Also the clear button works but afterwards the start button doesn't work anymore. If someone can help me with that as well.

Thank you to @Mike - SMT for helping me with this code. Here's the edited and fully functioning code:

import turtle
import tkinter as tk


def start(turtle_object, draw_path):
    global tracker, start_ndex, end_ndex, started
    tracker = False
    if started == False:
        started = True
        for i in range(start_ndex, end_ndex):
            if tracker == False and i <= end_ndex:
                pth = draw_path[i]
                if pth[0] == "f":
                    turtle_object.forward(pth[1])
                elif pth[0] == "r":
                    turtle_object.right(pth[1])
                elif pth[0] == "l":
                    turtle_object.left(pth[1])
                start_ndex += 1


running = True

def stop():
    global tracker, started
    tracker = True
    started = False

def clear():
    global t, tracker, started, start_ndex
    t.reset()
    start_ndex = 0
    started = False
    t = turtle.RawTurtle(canvas)


root = tk.Tk()
tracker = False
start_ndex = 0
started = False # added this tracking variable to prevent issues with     spamming the start button.

draw_path = [["f", 100], ["r", 90], ["f", 100], ["l", 90], ["f", 100], ["r", 90], ["f", 100], ["r", 90], ["f", 100]]


end_ndex = len(draw_path)

canvas = tk.Canvas(width = 500, height = 500)
canvas.pack()

t = turtle.RawTurtle(canvas)
tk.Button(text = "Start", command = lambda: start(t, draw_path)).pack(side = tk.LEFT)
tk.Button(text = "Stop", command = stop).pack(side = tk.LEFT)
tk.Button(text = "Clear", command = clear).pack(side = tk.LEFT)
root.mainloop()

回答1:

You cannot stop each draw statement unless you provide a checker in between each line drawn.

The below code is just a rough mock up of how you could make something to check for a tracking variable used to tell it to no longer draw new lines.

The closest thing you can do to being able to stop drawing is something like this:

import turtle
import tkinter as tk

def start():
    global tracker
    tracker = False
    if tracker == False:
        t.forward(100)
    if tracker == False:
        t.right(90)
    if tracker == False:
        t.forward(100)
    if tracker == False:
        t.left(90)
    if tracker == False:
        t.forward(100)
    if tracker == False:
        t.right(90)
    if tracker == False:
        t.forward(100)
    if tracker == False:
        t.right(90)
    if tracker == False:
        t.forward(100)


def stop():
    global tracker
    tracker = True

def clear():
    canvas.delete("all")

root = tk.Tk()
tracker = False
canvas = tk.Canvas(width = 500, height = 500)
canvas.pack()

t = turtle.RawTurtle(canvas)

tk.Button(text = "Start", command = start).pack(side = tk.LEFT)
tk.Button(text = "Stop", command = stop).pack(side = tk.LEFT)
tk.Button(text = "Clear", command = clear).pack(side = tk.LEFT)

root.mainloop()

This will at least stop drawing after each line but you cannot stop mid line draw.

Just for the fun of it if we add some tracking variables and use some cleaner logic we can start, stop and start again.

Update: From @cdlane's comment below I have added addition tracking and updated the clear function. This should allow for start stop start without issues and also be able to clear the field.

import turtle
import tkinter as tk

def start(turtle_object, draw_path):
    global tracker, start_ndex, end_ndex, started
    tracker = False
    if started == False:
        started = True
        for i in range(start_ndex, end_ndex):
            if tracker == False and i <= end_ndex:
                pth = draw_path[i]
                if pth[0] == "f":
                    turtle_object.forward(pth[1])
                elif pth[0] == "r":
                    turtle_object.right(pth[1])
                elif pth[0] == "l":
                    turtle_object.left(pth[1])
                start_ndex += 1

def stop():
    global tracker, started
    tracker = True
    started = False

def clear():
    global t, tracker, started, start_ndex
    canvas.delete("all")
    tracker = False
    start_ndex = 0
    started = False
    t = turtle.RawTurtle(canvas)

root = tk.Tk()
tracker = False
start_ndex = 0
started = False # added this tracking variable to prevent issues with spamming the start button.

draw_path = [["f", 100], ["r", 90], ["f", 100], ["l", 90], ["f", 100], ["r", 90], ["f", 100], ["r", 90], ["f", 100]]


end_ndex = len(draw_path)

canvas = tk.Canvas(width = 500, height = 500)
canvas.pack()

t = turtle.RawTurtle(canvas)
tk.Button(text = "Start", command = lambda: start(t, draw_path)).pack(side = tk.LEFT)
tk.Button(text = "Stop", command = stop).pack(side = tk.LEFT)
tk.Button(text = "Clear", command = clear).pack(side = tk.LEFT)
root.mainloop()


回答2:

I agree with the ideas in @Mike-SMT's answer but I'd go about it a different way. The turtle and its controls seem very much like a Python generator to me so I've recast it as such. The turtle moves along a path, one pixel at a time, yielding control which may, or may not return. Or it may exhaust its path and stop the iteration:

import tkinter as tk
from turtle import RawTurtle

PATH = [(100.00, 0.00), (100.00, -100.00), (200.00, -100.00), (200.00, -200.00), (100.00, -200.00)]

def run():
    for position in PATH:
        turtle.setheading(turtle.towards(position))

        while turtle.distance(position) > 1:
            turtle.forward(1)
            yield

def start():
    global generator, running

    running = True

    while running:
        try:
            next(generator)

        except ValueError:  # user clicked start but already running
            return

        except TypeError:  # new run
            turtle.reset()
            generator = run()

        except StopIteration:  # end of complete run
            generator = None
            running = False
            break

def stop():
    global running
    running = False

def clear():
    global generator
    turtle.reset()
    generator = None

root = tk.Tk()
canvas = tk.Canvas(width=500, height=500)
canvas.pack()

turtle = RawTurtle(canvas, "turtle")

running = True
generator = None

tk.Button(text="Start", command=start).pack(side=tk.LEFT, expand=tk.TRUE)
tk.Button(text="Stop", command=stop).pack(side=tk.LEFT, expand=tk.TRUE)
tk.Button(text="Clear", command=clear).pack(side=tk.LEFT, expand=tk.TRUE)

root.mainloop()

Also the clear button works but afterwards the start button doesn't work anymore. If someone can help me with that as well.

You can replace your current clear() function with:

def clear():
    t.clear()

if you just want to erase the path that's been drawn but leave the turtle where it ended up. If you want to erase the path and reset the turtle back to the starting point, instead do:

def clear():
    t.reset()