3

I try to write a program in python that lets the user to put a bet on one of the turtles and after the race tells him if he was correct or not. I want to let the user choose if he wants to restart the race by clicking 'r' - ( i used the turtle.onkey method) and it worked fine until I put this line of code: bet = screen.textinput("Choose your bet", "Enter blue or green: "). now the program lets the user choose his bet like I wanted but then it wont react when 'r' is pressed.. I want to clarify that after the textinput line of code the program won't react to any of the .onkey methods. anyone has idea why it happens?

THE CODE:

import turtle
import random
import ctypes

speed = random.randint(0, 2)

myTurtle = turtle.Turtle()
myTurtle2 = turtle.Turtle()
screen = turtle.Screen()
def up():
    myTurtle.setheading(90)
    myTurtle.forward(10)

def down():
    myTurtle.setheading(270)
    myTurtle.forward(10)

def left():
    myTurtle.setheading(180)
    myTurtle.forward(10)

def right():
    myTurtle.setheading(0)
    myTurtle.forward(10)

def up2():
    myTurtle2.setheading(90)
    myTurtle2.forward(10)

def down2():
    myTurtle2.setheading(270)
    myTurtle2.forward(10)

def left2():
    myTurtle2.setheading(180)
    myTurtle2.forward(10)

def right2():
    myTurtle2.setheading(0)
    myTurtle2.forward(10)

def restart():
    myTurtle.setposition(400, -300)
    myTurtle.setheading(90)
    myTurtle2.setposition(-400, -300)
    myTurtle2.setheading(90)
    speed = random.randint(0, 2)
    bet = screen.textinput("Choose your bet", "Enter blue or green: ")
    while myTurtle.ycor() < 300 and myTurtle2.ycor() < 300:
        if speed == 1:
            up()
        elif speed == 2:
            up2()
        speed = random.randint(0, 2)
    if myTurtle.ycor() == 300:
        if bet == "blue":
            ctypes.windll.user32.MessageBoxW(0, "Blue is the winner", "You win!", 0x00010000)
        else:
            ctypes.windll.user32.MessageBoxW(0, "Blue is the winner", "You lost", 0x00010000)
    else:
        if bet == "green":
            ctypes.windll.user32.MessageBoxW(0, "Green is the winner", "You win!", 0x00010000)
        else:
            ctypes.windll.user32.MessageBoxW(0, "Green is the winner", "You lost", 0x00010000)

screen.title("Turtle race")
turtle.listen()
myTurtle.shape("turtle")
myTurtle2.shape("turtle")
myTurtle.setposition(400,-300)
myTurtle.setheading(90)
myTurtle2.setposition(-400,-300)
myTurtle2.setheading(90)
myTurtle.dot(10, "blue")
myTurtle2.dot(10, "green")
myTurtle.pencolor("blue")
myTurtle2.pencolor("green")

myTurtle.speed(0)
myTurtle2.speed(0)
bet = screen.textinput("Choose your bet", "Enter blue or green: ")
while myTurtle.ycor() < 300 and myTurtle2.ycor() < 300:
    if speed == 1:
        up()
    elif speed == 2:
        up2()
    speed = random.randint(0, 2)

if myTurtle.ycor() == 300:
    if bet == "blue":
        ctypes.windll.user32.MessageBoxW(0, "Blue is the winner", "You win!", 0x00010000)
    else:
        ctypes.windll.user32.MessageBoxW(0, "Blue is the winner", "You lost", 0x00010000)
else:
    if bet == "green":
        ctypes.windll.user32.MessageBoxW(0, "Green is the winner", "You win!", 0x00010000)
    else:
        ctypes.windll.user32.MessageBoxW(0, "Green is the winner", "You lost", 0x00010000)



turtle.onkey(up, 'Up')
turtle.onkey(down, 'Down')
turtle.onkey(left, 'Left')
turtle.onkey(right, 'Right')

turtle.onkey(restart, 'r')

turtle.onkey(up2, 'w')
turtle.onkey(down2, 's')
turtle.onkey(left2, 'a')
turtle.onkey(right2, 'd')
turtle.mainloop()
Ron Gerbi
  • 31
  • 2
  • as for me `textinput` has to react on all keys so it may remove all `onkey`. Did you try to assing again `onkey` after `textinput` ? – furas Jul 20 '20 at 08:40
  • there can be other problem - `while`-loop which runs all time and it blocks `mainloop()` which gets key/mouse events from system and check if you clicked `r` and executes assigned function. It may need [ontimer](https://docs.python.org/3/library/turtle.html#turtle.ontimer) instead of `while` to repeat function periodically and not block `mainloop()` – furas Jul 20 '20 at 08:48
  • see doc for [key](https://docs.python.org/3/library/turtle.html#turtle.onkeyrelease) - it needs `listen()` after `onkey()` to access keys from system. – furas Jul 20 '20 at 09:01

1 Answers1

1

Problem is that onkey needs focus on main window to get keys from system but when you execute textinput then this focus is lost and it needs screen.listen() again.


Minimal working code.

Because you repeate some code inside restart() and outside restart() so I run restart() instead of external code.

Because I don't use Windows so I used tkinter.messagebox to display messages. turtle already uses tkinter to display main window with canvas and textinput.

import turtle
import random
#import tkinter  as tk
from tkinter import messagebox

# functions ---

def up():
    myTurtle.setheading(90)
    myTurtle.forward(10)

def down():
    myTurtle.setheading(270)
    myTurtle.forward(10)

def left():
    myTurtle.setheading(180)
    myTurtle.forward(10)

def right():
    myTurtle.setheading(0)
    myTurtle.forward(10)

def up2():
    myTurtle2.setheading(90)
    myTurtle2.forward(10)

def down2():
    myTurtle2.setheading(270)
    myTurtle2.forward(10)

def left2():
    myTurtle2.setheading(180)
    myTurtle2.forward(10)

def right2():
    myTurtle2.setheading(0)
    myTurtle2.forward(10)

def restart():
    myTurtle.setposition(400, -300)
    myTurtle.setheading(90)
    myTurtle2.setposition(-400, -300)
    myTurtle2.setheading(90)
    
    speed = random.randint(0, 2)
    bet = screen.textinput("Choose your bet", "Enter blue or green: ")

    screen.listen()  # <--- set focus on main window after `textinput`
    
    while myTurtle.ycor() < 300 and myTurtle2.ycor() < 300:
        if speed == 1:
            up()
        elif speed == 2:
            up2()
        speed = random.randint(0, 2)

    if myTurtle.ycor() == 300:
        if bet == "blue":
            messagebox.showinfo("Result", "Blue is the winner\n\nYou win!")
        else:
            messagebox.showinfo("Result", "Blue is the winner\n\nYou lost")
    else:
        if bet == "green":
            messagebox.showinfo("Result", "Green is the winner\n\nYou win!")
        else:
            messagebox.showinfo("Result", "Green is the winner\n\nYou lost")
    
# --- main ---

speed = random.randint(0, 2)

myTurtle  = turtle.Turtle()
myTurtle2 = turtle.Turtle()

screen = turtle.Screen()
screen.title("Turtle race")

myTurtle.shape("turtle")
myTurtle2.shape("turtle")

turtle.onkey(up, 'Up')
turtle.onkey(down, 'Down')
turtle.onkey(left, 'Left')
turtle.onkey(right, 'Right')

turtle.onkey(restart, 'r')

turtle.onkey(up2, 'w')
turtle.onkey(down2, 's')
turtle.onkey(left2, 'a')
turtle.onkey(right2, 'd')

restart()

turtle.mainloop()

EDIT:

Reduced code - I keep turtles on list and later I can use myTurtles[number] and I can use one function up(number) instead two functions up() and up2()

import turtle
import random
from tkinter import messagebox

# functions ---

def up(number):
    myTurtles[number].setheading(90)
    myTurtles[number].forward(10)

def down(number):
    myTurtles[number].setheading(270)
    myTurtles[number].forward(10)

def left(number):
    myTurtles[number].setheading(180)
    myTurtles[number].forward(10)

def right(number):
    myTurtles[number].setheading(0)
    myTurtles[number].forward(10)

def restart():
   
    myTurtles[0].setposition(400, -300)
    myTurtles[0].setheading(90)
    myTurtles[1].setposition(-400, -300)
    myTurtles[1].setheading(90)
    
    bet = screen.textinput("Choose your bet", "Enter blue or green: ")
    screen.listen()
    
    speed = random.randint(0, 2)
    while myTurtles[0].ycor() < 300 and myTurtles[0].ycor() < 300:
        if speed < 2: # randint(0, 2) can gives 0, 1, 2  
            up(speed)
        speed = random.randint(0, 2)

    if myTurtles[0].ycor() == 300:
        if bet == "blue":
            messagebox.showinfo("Result", "Blue is the winner\n\nYou win!")
        else:
            messagebox.showinfo("Result", "Blue is the winner\n\nYou lost")
    else:
        if bet == "green":
            messagebox.showinfo("Result", "Green is the winner\n\nYou win!")
        else:
            messagebox.showinfo("Result", "Green is the winner\n\nYou lost")
    
# --- main ---

screen = turtle.Screen()
screen.title("Turtle race")

myTurtles = [
    turtle.Turtle(),
    turtle.Turtle(),
]    

myTurtles[0].shape("turtle")
myTurtles[1].shape("turtle")

turtle.onkey(lambda: up(0),    'Up')
turtle.onkey(lambda: down(0),  'Down')
turtle.onkey(lambda: left(0),  'Left')
turtle.onkey(lambda: right(0), 'Right')

turtle.onkey(lambda: up(1),    'w')
turtle.onkey(lambda: down(1),  's')
turtle.onkey(lambda: left(1),  'a')
turtle.onkey(lambda: right(1), 'd')

turtle.onkey(restart, 'r')

restart()

turtle.mainloop()
furas
  • 134,197
  • 12
  • 106
  • 148
  • I actually tried to findout how to make a list of turtles as well:) thx! I didnt understand though why you used lambda in the .onkey methods – Ron Gerbi Jul 20 '20 at 12:49
  • 1
    `onkey` needs function's name without `()` and arguments - it is called "callback". I use `lambda` to put function with `()` and arguments. Normally I would have to create function `def up0(): up(0)` or `up0 = lambda:up(0)` and then use `onkey(up0, 'Up')` – furas Jul 20 '20 at 12:58