Im making a turtle race game, its a game where there are a few turtles who are assigned random speeds and then one turtle wins. However, just for fun im trying to add a few things to the game. For example, a button to exit the game and a button to restart the race. I have made only the exit button for now, and gave the command to exit the game. The button works, however not in the right time.
The problem is is that i have a piece of code that makes the canvas (background), Which is just the turtle drawing. I have another piece of code that places the buttons and tells them what to do when being clicked. And then I have a piece of code that assigns random speeds to the turtles.
This is the buttons code.(The try again button command is not finished yet.)
screen = Screen()
screen.setup(width=600, height=400)
def exit_game():
exit()
canvas = screen.getcanvas()
button = Button(canvas.master, text="Exit Game", command=exit_game, width=10, height=4, fg="white", bg="dodgerblue")
button.pack()
button.place(x=150, y=530)
canvas2 = screen.getcanvas()
button2 = Button(canvas2.master, text="Try Again", command=exit_game, width=10, height=4, fg="white", bg="dodgerblue" )
button2.pack()
button2.place(x=50, y=530)
And here is the code for assigning random numbers to the turtles.
for movement in range (230):
red.forward(randint(1,8))
blue.forward(randint(1,8))
purple.forward(randint(1,8))
orange.forward(randint(1,8))
The problem is, is that when for example the turtles are moving, i can press the button, but it does not do the command. After the movement loop goes through 230 times, only then it exits the game. So basically my code is just reading the speed to the turtles and forgot about the button commands.
Is there a way to override this somehow and make my button exit the game when being clicked at all times?
Also i did try to put the button into an infinite loop, but it did not work(maybe I did it wrong).
import turtle
import time
from random import randint
from tkinter import *
from turtle import Screen, Turtle
import tkinter
import tkinter as tk
# Window Customization
Window = turtle.Screen()
Window.title('Turtle Race Game')
#Complete back canvas for the game
def back_canvas():
# Main drawing turtle
pen = turtle.Turtle()
pen.speed(0)
# far left -640; far right 633
#top 330; bottom -320
# Landscape making
#Making the ground
pen.hideturtle()
pen.color("sienna")
pen.penup()
pen.left(90)
pen.setpos(-640, -320)
pen.pendown()
pen.begin_fill()
pen.color("sienna")
for i in range(2):
pen.forward(162.5)
pen.right(90)
pen.forward(1272)
pen.right(90)
pen.end_fill()
#Making Racing Area
for i in range(2):
pen.forward(162.5)
pen.color("lime")
pen.begin_fill()
for i in range(2):
pen.forward(162.5)
pen.right(90)
pen.forward(1272)
pen.right(90)
pen.end_fill()
#Making Top Area
pen.color("dodgerblue")
pen.begin_fill()
pen.forward(162.5)
for i in range(2):
pen.forward(162.5)
pen.right(90)
pen.forward(1272)
pen.right(90)
pen.end_fill()
pen.penup()
# Writing "Turtle Race Game"
pen.color('lime')
pen.setpos(-170,250)
pen.color("black")
pen.write("Turtle Race Game",pen, font=("Arial", 27, 'normal'))
# Making the first finishline
pen.setpos(500,143)
pen.right(180)
for i in range(7):
pen.color('black')
pen.begin_fill()
pen.left(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(180)
pen.forward(20)
pen.end_fill()
pen.color('white')
pen.begin_fill()
pen.left(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(180)
pen.forward(20)
pen.end_fill()
# Making the second finishline
pen.setpos(520,143)
for i in range(7):
pen.color('white')
pen.begin_fill()
pen.left(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(180)
pen.forward(20)
pen.end_fill()
pen.color('black')
pen.begin_fill()
pen.left(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(90)
pen.forward(20)
pen.right(180)
pen.forward(20)
pen.end_fill()
# placing main pen to right place to say who won
pen.setpos(520,180)
# Making all the turtles
def race():
# Making the turtles, turtle 1
red = turtle.Turtle()
red.speed(0)
red.shape("turtle")
red.penup()
red.color("red")
red.setpos(-550, 90)
red.pendown()
# Making the turtles, turtle 2
blue = turtle.Turtle()
blue.shape("turtle")
blue.speed(0)
blue.penup()
blue.color("blue")
blue.setpos(-550,30)
blue.pendown()
# Making the turtles, turtle 3
purple = turtle.Turtle()
purple.speed(0)
purple.shape("turtle")
purple.penup()
purple.color("purple")
purple.setpos(-550,-30)
purple.pendown()
# Making the turtles, turtle 4
orange = turtle.Turtle()
orange.speed(0)
orange.shape("turtle")
orange.penup()
orange.color("orange")
orange.setpos(-550,-90)
orange.pendown()
race_step_count = 230
if race_step_count:
red.forward(randint(1,8))
blue.forward(randint(1,8))
purple.forward(randint(1,8))
orange.forward(randint(1,8))
race_step_count -= 1
next_step = Window.after(100, race) # call this function again after 100mS
else: # no more steps - the race is over!
Window.after_cancel(next_step) # stop calling the race function
def main_game():
run = True
screen = Screen()
screen.setup(width=600, height=400)
def exit_game():
exit()
canvas = screen.getcanvas()
button = Button(canvas.master, text="Exit Game",command = exit_game ,width= 10, height = 4, fg = "white", bg = "dodgerblue")
button.place(x=150, y=530)
canvas2 = screen.getcanvas()
button2 = Button(canvas2.master, text="Try Again",command = exit_game, width= 10, height = 4,fg = "white", bg = "dodgerblue" )
button2.place(x=50, y=530)
#Complete back canvas for the game
back_canvas()
# Making all the turtles
race()
main_game()
# Making my button do something when being clicked
# Making the turtles stop when hitting the finish line
time.sleep(1)
#Writing who won
def who_won():
for i in range(1):
if blue.xcor() > red.xcor() and blue.xcor() > purple.xcor() and blue.xcor() > orange.xcor():
time.sleep(1)
pen.write('Blue won!', align = "center", font =("Arial", 25, "bold"))
elif red.xcor() > blue.xcor() and red.xcor() > purple.xcor() and red.xcor() > orange.xcor():
time.sleep(1)
pen.write('Red won!', align = "center", font =("Arial", 25, "bold"))
elif purple.xcor() > blue.xcor() and purple.xcor() > red.xcor() and purple.xcor() > orange.xcor():
time.sleep(1)
pen.write('Purple won!', align = "center", font =("Arial", 25, "bold"))
elif orange.xcor() > blue.xcor() and orange.xcor() > red.xcor() and orange.xcor() > purple.xcor():
time.sleep(1)
pen.write('Orange won!', align = "center", font =("Arial", 25, "bold"))
else:
continue
# Window doesnt close on its own
Window.mainloop()
CodePudding user response:
What's happing is that your application is getting "hung up" on the long-running movement loop. Tkinter is registering your button press, but it can't do anything about it until it's done with the for loop. A quick solution to this is to define a function that handles the movements, and uses tkinter.after()
to call it periodically until the "race" is over, since the built-in after
method allows the UI's event loop to continue uninterrupted.
# I don't know what your imports look like, so this is a boilerplate example
import tkinter as tk
root = tk.Tk() # this is whatever you're calling 'mainloop()' on right now
race_step_count = 230 # define how 'long' the race is
def race():
global race_step_count
if race_step_count:
red.forward(randint(1,8))
blue.forward(randint(1,8))
purple.forward(randint(1,8))
orange.forward(randint(1,8))
race_step_count -= 1
next_step = root.after(100, race) # call this function again after 100mS
else: # no more steps - the race is over!
root.after_cancel(next_step) # stop calling the race function
To start the race, just call the function when you're ready: race()
CodePudding user response:
Looking at your code, I'm surprised it runs. Running your code, I find it doesn't. It bombs out with:
AttributeError: '_Screen' object has no attribute 'after'
Turtle works in two modes, standalone and embeded in a larger tkinter program. You're trying to embed a standalone turtle program. Below, I've taken apart and reassembled your turtle program to be embedded in tkinter and fully implement the functionality you describe. (It has a tkinter "Exit Game"
button.)
from random import randint
from turtle import TurtleScreen, RawTurtle
import tkinter as tk
import sys
def back_canvas():
# Landscape making
# Making the ground
pen.color('sienna')
pen.penup()
pen.setpos(-640, -162.5)
pen.pendown()
pen.begin_fill()
for _ in range(2):
pen.forward(1280)
pen.right(90)
pen.forward(162.5)
pen.right(90)
pen.end_fill()
# Making Racing Area
pen.color('lime')
pen.begin_fill()
for _ in range(2):
pen.forward(1280)
pen.left(90)
pen.forward(325)
pen.left(90)
pen.end_fill()
# Making Top Area
pen.color('dodgerblue')
pen.begin_fill()
pen.left(90)
pen.forward(325)
for _ in range(2):
pen.forward(162.5)
pen.right(90)
pen.forward(1280)
pen.right(90)
pen.end_fill()
pen.penup()
# Writing "Turtle Race Game"
pen.color('lime')
pen.setpos(0, 250)
pen.color('black')
pen.write("Turtle Race Game", align='center', font=('Arial', 27, 'normal'))
# Making the first finish line
pen.right(90)
pen.setpos(500, 143)
def flag():
pen.color('black')
pen.begin_fill()
for _ in range(4):
pen.forward(20)
pen.right(90)
pen.end_fill()
pen.forward(20)
pen.color('white')
pen.begin_fill()
for _ in range(4):
pen.forward(20)
pen.right(90)
pen.end_fill()
pen.forward(20)
for _ in range(7):
flag()
pen.right(90)
pen.forward(40)
pen.right(90)
flag()
pen.right(180)
# placing main pen to right place to say who won
pen.setpos(520, 180)
race_step_count = 230
def race():
global race_step_count
if race_step_count > 0:
red.forward(randint(1, 8))
blue.forward(randint(1, 8))
purple.forward(randint(1, 8))
orange.forward(randint(1, 8))
race_step_count -= 1
screen.ontimer(race, 100) # call this function again after 100mS
else:
who_won()
def who_won():
if blue.xcor() > red.xcor() and blue.xcor() > purple.xcor() and blue.xcor() > orange.xcor():
pen.write("Blue won!", align='center', font=('Arial', 25, 'bold'))
elif red.xcor() > blue.xcor() and red.xcor() > purple.xcor() and red.xcor() > orange.xcor():
pen.write("Red won!", align='center', font=('Arial', 25, 'bold'))
elif purple.xcor() > blue.xcor() and purple.xcor() > red.xcor() and purple.xcor() > orange.xcor():
pen.write("Purple won!", align='center', font=('Arial', 25, 'bold'))
elif orange.xcor() > blue.xcor() and orange.xcor() > red.xcor() and orange.xcor() > purple.xcor():
pen.write("Orange won!", align='center', font=('Arial', 25, 'bold'))
master = tk.Tk()
master.title("Turtle Race Game")
canvas = tk.Canvas(master, width=1280, height=650)
canvas.pack()
screen = TurtleScreen(canvas)
tk.Button(master, text="Exit Game", command=sys.exit, width=0, height=4, fg='gold', bg='dodgerblue').pack()
# Main drawing turtle
pen = RawTurtle(screen)
pen.hideturtle()
pen.speed('fastest')
back_canvas()
red = RawTurtle(screen)
red.speed('fastest')
red.shape('turtle')
red.penup()
red.color('red')
red.setpos(-550, 90)
blue = red.clone()
blue.color('blue')
blue.setpos(-550, 30)
purple = red.clone()
purple.color('purple')
purple.setpos(-550, -30)
orange = red.clone()
orange.color('orange')
orange.setpos(-550, -90)
race()
screen.mainloop()
Whenever you import
the same library multiple ways, you're probably in trouble. (When you import
mulitple libraries multiple ways, you're definitely in trouble.)