Home > Mobile >  Buttons don't do their intended command at click
Buttons don't do their intended command at click

Time:12-03

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.)

  • Related