Getting constant error on Spyder, please advise - python

This is the error I am getting:
invalid command name "1771926755840move"
while executing
"1771926755840move"
("after" script)
invalid command name "1771922102464move"
while executing
"1771922102464move"
("after" script)
invalid command name "1771947147520move"
while executing
"1771947147520move"
("after" script)
Tcl_AsyncDelete: async handler deleted by the wrong thread
Below is my code:
from turtle import *
from random import *
from freegames import vector
from playsound import playsound
import winsound
from tkinter import *
from timeit import default_timer as timer
from datetime import timedelta
from threading import Event
"""
def paused():
Event.wait()
def unpaused():
Event.release()
"""
root = Tk()
root.title("OPTIONS")
def stop():
bye()
winsound.PlaySound(None, winsound.SND_PURGE)
def gamingarea():
bird = vector(0, 0)
balls = []
winsound.PlaySound('C:/Users/SONY/Desktop/cs.wav', winsound.SND_LOOP + winsound.SND_ASYNC)
start = timer()
def tap(x, y):
lift = vector(0, 30)
bird.move(lift)
def inside(point):
return -200 < point.x < 200 and -200 < point.y < 200
def draw(alive):
clear()
goto(bird.x, bird.y)
if alive:
dot(20, "green")
else:
dot(20, "red")
winsound.PlaySound(None, winsound.SND_PURGE)#when bird is red, music stops
end = timer()
color("orange")
write(str(timedelta(seconds=end-start)), align = "center", font=("Comic Sans MS", 20, "normal", "bold"))
for ball in balls:
goto(ball.x, ball.y)
dot(20, "black")
update()
def move():
bird.y -= 5
for ball in balls:
ball.x -= 3
if randrange(10) == 0:
y = randrange(-199, 199)
ball = vector(199, y)
balls.append(ball)
while len(balls) > 0 and not inside(balls[0]):
balls.pop(0)
if not inside(bird):
draw(False)
return
for ball in balls:
if abs(ball - bird) < 15:
draw(False)
return
draw(True)
ontimer(move, 50)
title("Dodgy Ball")
setup(420, 420)
hideturtle()#turtle is hidden
up()
tracer(False)#Used to turn off animations. When false, animations are turned off. It also turns off automatic updates
onscreenclick(tap)#calling the tap function when touchpad is touched
move()
bgcolor("blue")
done()
startbutton = Button(root, text = "START", padx=100,pady=100, command=gamingarea)
endbutton = Button(root, text = "KILL!", padx=100,pady=100, command=stop)
#pausebutton = Button(root, text = "PAUSE!", padx = 100, pady = 100, command=paused)
#unpausebutton = Button(root, text = "UNPAUSE", padx = 100, pady = 100, command = unpaused)
startbutton.pack()
endbutton.pack()
#pausebutton.pack()
#unpausebutton.pack()
root.mainloop()
PS: It's a game I am designing with my friend
Thanks

Problem is only when game is running (bird is alive) and you try to kill game.
Whey you press kill it then it runs turtle.bye() which deletes all objects and it removes window but it doesn't stop ontimer() which still want to run move() and draw objects (but they don't exists any more and this gives message invalid command name "1771926755840move").
When you press kill it then it should set variable ie. running = False
def stop():
global running
running = False
which should be checked in move to stop loop and close window
if running:
# repeate loop and run `move` again
turtle.ontimer(move, 50)
else:
# stop loop and close window
turtle.bye()
#winsound.PlaySound(None, winsound.SND_PURGE)
My version with reduced modules.
I create own class vector so I don't need freegames which I don't know.
I use datetime instead of timeit because then end-start gives me directly timedelta.
I commented winsound because it works only with Windows but I uses Linux.
I added code for paused, unpased and it doesn't need Threading.
#from turtle import * # PEP8: `import *` is not preferred
#from random import * # PEP8: `import *` is not preferred
#from tkinter import * # PEP8: `import *` is not preferred
import turtle
import tkinter as tk
import random
import datetime
#import winsound
# --- classes ---
class vector():
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, other):
self.x += other.x
self.y += other.y
def __sub__(self, other):
return vector(self.x - other.x, self.y - other.y)
def __abs__(self):
return (self.x**2 + self.y**2)**0.5
# --- functions ---
def paused():
global pause
pause = True
def unpaused():
global pause
pause = False
def stop():
global running
if running:
# stop loop `ontimer(move, 50)`
running = False
else:
# exit when bird is dead
turtle.bye()
#winsound.PlaySound(None, winsound.SND_PURGE)
def gamingarea():
global running
global pause
running = True
pause = False
bird = vector(0, 0)
balls = []
#winsound.PlaySound('C:/Users/SONY/Desktop/cs.wav', winsound.SND_LOOP + winsound.SND_ASYNC)
start = datetime.datetime.now()
def tap(x, y):
lift = vector(0, 30)
bird.move(lift)
def inside(point):
return -200 < point.x < 200 and -200 < point.y < 200
def draw(alive):
global running
turtle.clear()
turtle.goto(bird.x, bird.y)
if alive:
turtle.dot(20, "green")
else:
turtle.dot(20, "red")
#winsound.PlaySound(None, winsound.SND_PURGE)#when bird is red, music stops
turtle.color("orange")
end = datetime.datetime.now()
diff = end-start
turtle.write(str(diff.seconds), align = "center", font=("Comic Sans MS", 20, "normal", "bold"))
running = False
for ball in balls:
turtle.goto(ball.x, ball.y)
turtle.dot(20, "black")
turtle.update()
def move():
if not pause:
bird.y -= 5
for ball in balls:
ball.x -= 3
if random.randrange(10) == 0:
y = random.randrange(-199, 199)
ball = vector(199, y)
balls.append(ball)
while len(balls) > 0 and not inside(balls[0]):
balls.pop(0)
if not inside(bird):
draw(False)
return
for ball in balls:
if abs(ball - bird) < 15:
draw(False)
return
draw(True)
if running:
# repeate loop `ontimer(move, 50)`
turtle.ontimer(move, 50)
else:
# stop loop `ontimer(move, 50)` and exit
turtle.bye()
#winsound.PlaySound(None, winsound.SND_PURGE)
turtle.title("Dodgy Ball")
turtle.setup(420, 420)
turtle.hideturtle() # turtle is hidden
turtle.up()
turtle.tracer(False) # Used to turn off animations. When false, animations are turned off. It also turns off automatic updates
turtle.onscreenclick(tap) # calling the tap function when touchpad is touched
move()
turtle.bgcolor("blue")
turtle.done()
# --- main ---
root = tk.Tk()
root.title("OPTIONS")
start_button = tk.Button(root, text="START", padx=100,pady=100, command=gamingarea)
end_button = tk.Button(root, text="KILL!", padx=100,pady=100, command=stop)
pause_button = tk.Button(root, text="PAUSE!", padx=100, pady=100, command=paused)
unpause_button = tk.Button(root, text="UNPAUSE", padx=100, pady=100, command=unpaused)
start_button.pack(fill='both')
end_button.pack(fill='both')
pause_button.pack(fill='both')
unpause_button.pack(fill='both')
root.mainloop()
Frankly, if you use tkinter to display buttons then you could use tkinter.Canvasto draw objects, andtkinter.bind()to runtap, and root.after()instead ofontimer()to run loop - and then you don't needturtle`

Related

Time and keypress events with Python's Turtle

I am trying to create a simple game with turtle:
A turtle controlled by the user can shoot bullets when space is pressed
An enemy turtle is created at a random position
If the enemy is hit by the bullet, the enemy is destroyed and moves to a new position.
If the bullet leaves the screen, it disappears.
While the bullet is moving, the player should still be able to move.
If the enemy is not destroyed in 20 seconds, the player looses.
Therefore, I need some events to be controlled with the keyboard and others that get triggered after a certain time. I cannot think about a way of doing this without a loop inside which I check the distance between the bullet and the enemy, but if I do it like that, I cannot control the main turtle during the loop.
import turtle as trtl
from random import randint
import time
class Game():
def __init__(self):
self.scr = trtl.Screen()
self.scr.update()
self.player = trtl.Turtle()
self.player.shape('turtle')
self.player.penup()
trtl.onkeypress(self.forward,'w')
trtl.onkeypress(self.backwards,'s')
trtl.onkeypress(self.left,'a')
trtl.onkeypress(self.right,'d')
trtl.onkeypress(self.shoot,'space')
trtl.listen()
self.enemy = trtl.Turtle()
self.enemy.shape('square')
self.enemy.penup()
self.enemy.speed(0)
self.move_enemy()
self.bullet = trtl.Turtle()
self.bullet.penup()
self.bullet.hideturtle()
self.bulletShot = False
def forward(self):
self.player.forward(5)
def backwards(self):
self.player.back(5)
def left(self):
self.player.left(6)
def right(self):
self.player.right(6)
def shoot(self):
if self.bulletShot == False:
self.bullet.speed(0)
self.bullet.goto(self.player.pos())
self.bullet.seth(self.player.heading())
self.bullet.showturtle()
self.bulletShot = True
def move_enemy(self):
x = randint(-300,300)
y = randint(-300,300)
self.enemy.hideturtle()
self.enemy.goto(x,y)
self.enemy.showturtle()
def play(self):
startTime = time.time()
print(time.time() - startTime)
while time.time() - startTime < 20:
if self.bulletShot:
self.bullet.forward(1)
collision = self.bullet.distance(self.enemy.pos()) < 10
isIn = (self.bullet.pos()[0] <= 300 and
self.bullet.pos()[0] >= -300 and
self.bullet.pos()[1] <= 300 and
self.bullet.pos()[1] >= -300)
if not(isIn):
self.bullet.hideturtle()
self.bulletShot = False
elif collision:
self.bullet.hideturtle()
self.bulletShot = False
self.move_enemy()
startTime = time.time()
self.player.write('You loose')
self.scr.exitonclick()
game = Game()
game.play()
I tried a simpler version in which one turtle moves automatically in a loop and another turtle is controlled with the keyboard, and it works well.
import turtle as trtl
def up():
jane.sety(jane.pos()[1] + 10)
def down():
jane.sety(jane.pos()[1] - 10)
scr = trtl.Screen()
scr.update()
bob = trtl.Turtle()
bob.penup()
bob.seth(180)
bob.setx(300)
bob.speed(1)
jane = trtl.Turtle()
jane.penup()
trtl.onkeypress(up,'w')
trtl.onkeypress(down,'s')
trtl.listen()
while True:
if bob.pos()[0] > -300:
bob.forward(10)
else:
break
scr.exitonclick()
Is there a way of fixing this with turtle?
Let's redesign the game to work with turtle's event system by using a timer event to control the action while still using your timer to limit play time:
from turtle import Screen, Turtle
from random import randint
import time
class Game():
def __init__(self):
self.startTime = -1
self.screen = Screen()
self.screen.tracer(False)
self.player = Turtle()
self.player.shape('turtle')
self.player.penup()
self.enemy = Turtle()
self.enemy.shape('square')
self.enemy.penup()
self.move_enemy()
self.bullet = Turtle()
self.bullet.hideturtle()
self.bullet.penup()
self.bulletShot = False
self.screen.onkeypress(self.forward, 'w')
self.screen.onkeypress(self.backwards, 's')
self.screen.onkeypress(self.left, 'a')
self.screen.onkeypress(self.right, 'd')
self.screen.onkeypress(self.shoot, 'space')
self.screen.listen()
def forward(self):
self.player.forward(5)
self.screen.update()
def backwards(self):
self.player.back(5)
self.screen.update()
def left(self):
self.player.left(6)
self.screen.update()
def right(self):
self.player.right(6)
self.screen.update()
def shoot(self):
if not self.bulletShot:
self.bullet.setposition(self.player.position())
self.bullet.setheading(self.player.heading())
self.bullet.showturtle()
self.bulletShot = True
self.screen.update()
def move_enemy(self):
x = randint(-300, 300)
y = randint(-300, 300)
self.enemy.goto(x, y)
self.screen.update()
def play(self):
if self.startTime == -1:
self.startTime = time.time()
if self.bulletShot:
self.bullet.forward(1)
x, y = self.bullet.position()
if not(-300 <= x <= 300 and -300 <= y <= 300):
self.bullet.hideturtle()
self.bulletShot = False
elif self.bullet.distance(self.enemy.pos()) < 10:
self.bullet.hideturtle()
self.bulletShot = False
self.move_enemy()
self.startTime = time.time()
self.screen.update()
if time.time() - self.startTime > 20:
self.player.write('You loose!')
self.screen.update()
else:
self.screen.ontimer(self.play, 10)
screen = Screen()
game = Game()
game.play()
screen.mainloop()
Still bit crude but should be playable. You had a self.scr.update() call in your original code but without an initial call to tracer(), it does nothing. Here we're using tracer() and update() to speed up and smooth out the motion by manually controlling all screen updates.
I managed to solve it without changing much the code and without making the function play() recursive. The problem was that if the if inside de while needs an else, otherwise, the key press is not recorded. So I gave it something to do and now it works as I want.
while time.time() - startTime < 20:
if self.bulletShot:
self.bullet.forward(3)
...
else:
self.scr.update()

Couldn't get rid of (_tkinter.TclError: bad event type or keysym "UP") problem

I am running a Linux mint for the first time . I tried coding a python problem but for two days I am continiously facing problems due to Linux interface please
This is my code:-
import turtle
import time
boxsize=200
caught=False
score=0
#function that are called in keypress
def up():
mouse.forward(10)
checkbound()
def left():
move.left(45)
def right():
move.right(45)
def back():
mouse.backward(10)
checkbound()
def quitTurtles():
window.bye()
#stop the mouse from leaving the square set size
def checkbound():
global boxsize
if mouse.xcor()>boxsize:
mouse.goto(boxsize, mouse.ycor())
if mouse.xcor()<-boxsize:
mouse.goto(-boxsize, mouse.ycor())
if mouse.ycor()>boxsize:
mouse.goto(mouse.xcor(),boxsize)
if mouse.ycor()<-boxsize:
mouse.goto(mouse.xcor(),-boxsize)
#set up screen
window=turtle.Screen()
mouse=turtle.Turtle()
cat=turtle.Turtle()
mouse.penup()
mouse.penup()
mouse.goto(100,100)
#add key listeners
window.onkeypress(up ,'UP')
window.onkeypress(right ,'left')
window.onkeypress(left ,'Right')
window.onkeypress(back ,'DOWN')
window.onkeypress(quitTurtles, "Escape")
difficulty=window.numinput("difficulty","Enter a difficulty from 1 to 5",minval=1,maxval=5)
window.listen()
#main loop
#note how it changes with difficulty
while not caught:
cat.setheading(cat.towards(mouse))
cat.forward(8+diffficulty)
score=score+1
if cat.distance(mouse)<5:
caught=true
time.sleep(0.2-(0.1*difficulty))
window.textinput("GAME OVER","WELL DONE YOU SCORED:"+str(score*difficulty))
window.bye()
This code has several problems, many of which will keep it from running correctly:
Substituted move for mouse:
def up():
mouse.forward(10)
checkbound()
def left():
move.left(45)
Unnecessary global declaration as boxsize is not assigned:
def checkbound():
global boxsize
In code copy-and-paste, didn't change mouse to cat:
mouse=turtle.Turtle()
cat=turtle.Turtle()
mouse.penup()
mouse.penup()
The difficulty variable not spelled consistently:
cat.forward(8+diffficulty)
time.sleep(0.2-(0.1*difficulty))
Incorrect case for boolean:
caught=true
As noted in comments, total inconsistency in key naming case:
window.onkeypress(right ,'left')
window.onkeypress(left ,'Right')
window.onkeypress(back ,'DOWN')
Bigger picture issues are use of sleep() in an event-driven environment and lack of drawn boundaries so player knows the limits. Rather than address these issues one by one in SO questions, let's rework this code to work within the turtle event environment and be playable as a game:
from turtle import Screen, Turtle
BOX_SIZE = 600
# functions that are called in keypress
def up():
mouse.forward(15)
checkbound()
def left():
mouse.left(45)
def right():
mouse.right(45)
def back():
mouse.backward(15)
checkbound()
def checkbound():
''' stop the mouse from leaving the square set size '''
if mouse.xcor() > BOX_SIZE/2:
mouse.goto(BOX_SIZE/2, mouse.ycor())
elif mouse.xcor() < -BOX_SIZE/2:
mouse.goto(-BOX_SIZE/2, mouse.ycor())
if mouse.ycor() > BOX_SIZE/2:
mouse.goto(mouse.xcor(), BOX_SIZE/2)
elif mouse.ycor() < -BOX_SIZE/2:
mouse.goto(mouse.xcor(), -BOX_SIZE/2)
def move():
global score
cat.setheading(cat.towards(mouse))
cat.forward(2 * difficulty)
score += 1
if cat.distance(mouse) < 5:
screen.textinput("GAME OVER", "WELL DONE YOU SCORED: {}".format(score * difficulty))
screen.bye()
else:
screen.ontimer(move, 200 - 100 * difficulty)
score = 0
# set up screen
screen = Screen()
marker = Turtle()
marker.hideturtle()
marker.penup()
marker.goto(-BOX_SIZE/2, -BOX_SIZE/2)
marker.pendown()
for _ in range(4):
marker.forward(BOX_SIZE)
marker.left(90)
difficulty = int(screen.numinput("difficulty", "Enter a difficulty from 1 to 5", minval=1, maxval=5))
cat = Turtle()
cat.shapesize(2)
cat.penup()
mouse = Turtle()
mouse.penup()
mouse.goto(200, 200)
# add key listeners
screen.onkeypress(up, 'Up')
screen.onkeypress(right, 'Left')
screen.onkeypress(left, 'Right')
screen.onkeypress(back, 'Down')
screen.onkeypress(screen.bye, 'Escape')
screen.listen()
screen.ontimer(move, 1000) # give player a chance to move hand from keyboard to mouse
screen.mainloop()

How to launch a turtle program with a tkinter messagebox?

I am trying to launch a turtle game stored in one file with a message box from tkinter stored in another py file but both in the same directory. However, I receive a
_tkinter.TclError: image "pyimage2" doesn't exist error.
I have a function inside the game code that is called upon in the launcher code. This function runs all of the code inside the main game program apart from importing modules which I have included in the function but makes no difference.
Sorry for showing all but as I don't know what the problem is I thought this was best(removed as much code irrelevant to problem as I could(Assume anything unnamed that is called is deleted as irrelevant code)
def start():
import turtle
import math
import time
counter = 0
wn = turtle.Screen()
wn.setup(800, 600)
wn.bgcolor("black")
wn.title("Trooper shooter")
wn.bgpic("background1.png")
wn.update()
turtle.register_shape("invader.gif")
turtle.register_shape("plane.gif")
turtle.register_shape("troop.gif")
turtle.register_shape("player1.gif")
turtle.register_shape("player2.gif")
turtle.register_shape("bomb.gif")
turtle.register_shape("boom.gif")
#player
soldier = turtle.Turtle()
soldier.color("blue")
soldier.shape("player1.gif")
soldier.penup()
soldier.speed(0)
soldier.setposition(0,-200)
soldier.setheading(90)
soldierspd = 20
#Bomb
Numbombs = 2
bombs = []
for i in range(Numbombs):
bombs.append(turtle.Turtle())
for bomb in bombs:
bomb.shape("bomb.gif")
bomb.penup()
bomb.speed(0)
bomb.ht()
x = -1700
y = 2700
bomb.setposition(x+30,y)
bomb.setheading(270)
bombspeed = 10
#Plane
plane = turtle.Turtle()
plane.color("red")
plane.shape("plane.gif")
plane.penup()
plane.speed(0)
plane.setposition(-270,200)
plane.setheading(360)
planespd = 20
#Plane2
plane2 = turtle.Turtle()
plane2.ht()
plane2.color("red")
plane2.shape("plane.gif")
plane2.penup()
plane2.speed(0)
plane2.setposition(-270,200)
plane2.setheading(360)
planespd2 = 20
#ParaTroopers
num = 5
Troopers = []
for i in range(num):
Troopers.append(turtle.Turtle())
for Troop in Troopers:
Troop.color("Purple")
Troop.shape("troop.gif")
Troop.penup()
Troop.speed(0)
Troop.ht()
x = -170
y = 270
Troop.setposition(x+30,y)
Troop.setheading(270)
Troopspeed = 3
#Bullet
bullet = turtle.Turtle()
bullet.color("yellow")
bullet.shape("triangle")
bullet.penup()
bullet.speed(0)
bullet.setheading(90)
bullet.shapesize(0.5, 0.5)
bullet.ht()
bulletspeed = 30
#Define bullet state
#ready = ready to fire
#fire = bullet is firing
state = "ready"
#Move player left
def Left():
x = soldier.xcor()
x -= soldierspd
soldier.setx(x)
soldier.shape("player1.gif")
if x < -280:
x = -280
soldier.setx(x)
#Move player right
def Right():
x = soldier.xcor()
x += soldierspd
soldier.setx(x)
soldier.shape("player2.gif")
if x > 280:
x = 280
soldier.setx(x)
#Shoot bullet
def Shoot():
#Declare bullet state as global
global state
if state == "ready":
state = "fire"
#Move bullet above player
x = soldier.xcor()
y = soldier.ycor()
bullet.setposition(x, y +10)
bullet.showturtle()
#HitBoxes
def checkCollision(A,B):
distance = math.sqrt(math.pow(A.xcor() - B.xcor(), 2) +
math.pow(A.ycor() - B.ycor(), 2))
if distance < 30:
return True
else:
return False
#Move plane
def MovePlane():
x = plane.xcor()
x += planespd
plane.speed(1)
plane.setx(x)
if plane.xcor() > 280:
plane.ht()
turtle.listen()
turtle.onkey(Left,"Left")
turtle.onkey(Right,"Right")
turtle.onkey(Shoot,"space")
while True:
#Move Plane
MovePlane()
#Move count
incCount()
#Show Paratrooper
x = -200
for Troop in Troopers:
if plane.xcor() > x+50:
x += 80
Troop.setx(x)
Troop.showturtle()
y = Troop.ycor()
y -= Troopspeed
Troop.sety(y)
if y < -280:
Troop.ht()
if checkCollision(bullet, Troop):
# Reset bullet
bullet.ht()
state = "ready"
bullet.setposition(0, -400)
Troop.shape("boom.gif")
# Update Scoreboard
score += 10
scorestring = "Score: %s" % score
score_pen.clear()
score_pen.write(scorestring, False, align="left", font=("Arial", 14, "normal"))
time.sleep(0.1)
Troop.setposition(1000, 1000)
#update background
if counter == 30:
wn.bgpic("background2.png")
wn.update()
if counter == 60:
wn.bgpic("background3.png")
wn.update()
if counter == 90:
wn.bgpic("background4.png")
wn.update()
if counter == 120:
wn.bgpic("background5.png")
wn.update()
if counter == 150:
wn.bgpic("background6.png")
wn.update()
wn.mainloop()
from tkinter import *
from tkinter import messagebox
import TrooperShooter
root = Tk()
launch = messagebox.askquestion("launcher","Play game?")
if launch == "yes":
TrooperShooter.start()
When the launcher is run a messagebox should appear saying do you wish to play
and when "yes" is clicked the function to start the game is launched. However I receive the error:
Traceback (most recent call last):
File "C:/Users/marco/Desktop/Trooper shooter/launcher.py", line 10, in <module>
TrooperShooter.start()
File "C:\Users\marco\Desktop\Trooper shooter\TrooperShooter.py", line 18, in start
wn.bgpic("background1.png")
File "C:\Program Files (x86)\Python37-32\lib\turtle.py", line 1482, in bgpic
self._setbgpic(self._bgpic, self._bgpics[picname])
File "C:\Program Files (x86)\Python37-32\lib\turtle.py", line 738, in _setbgpic
self.cv.itemconfig(item, image=image)
File "<string>", line 1, in itemconfig
File "C:\Program Files (x86)\Python37-32\lib\tkinter\__init__.py", line 2578, in itemconfigure
return self._configure(('itemconfigure', tagOrId), cnf, kw)
File "C:\Program Files (x86)\Python37-32\lib\tkinter\__init__.py", line 1476, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: image "pyimage2" doesn't exist
Process finished with exit code 1
My first suggestion is that you use the turtle textinput() method (new in Python 3) instead of dragging tkinter and messagebox into the picture.
But if you're going to combine tkinter and turtle, you need to do it correctly. Turtle is designed to run either standalone with its own Tk window or embedded in an existing tkinter window. You're attempting to run it standalone, embedded in a tkinter window. I.e. instead of the standalone Turtle & Screen interface, you should be working with the embedded TurtleScreen, RawTurtle and ScrolledCanvas interface.
Besides this issue, your code is generally buggy. You've lots of unfinished code. Your use of global has to become nonlocal when you move the entire program inside the start() function. You're missing pieces of code necessary to run. Finally, you have a while True: loop which has no business being in an event-driven environment like turtle. I've repaced it with a timer event.
I've reworked your code below to run as you intended. I've tossed incomplete and/or broken features to simplify the example. The startup code:
from tkinter import Tk
from tkinter import messagebox
import TrooperShooter
root = Tk()
launch = messagebox.askquestion("launcher", "Play game?")
if launch == "yes":
TrooperShooter.start(root)
TrooperShooter.py
import tkinter as tk
from turtle import TurtleScreen, ScrolledCanvas, RawTurtle
def start(root):
# Move player left
def Left():
soldier.shape("player1.gif")
x = soldier.xcor() - soldierspd
soldier.setx(x)
if x < -280:
soldier.setx(-280)
# Move player right
def Right():
soldier.shape("player2.gif")
x = soldier.xcor() + soldierspd
soldier.setx(x)
if x > 280:
soldier.setx(280)
# Shoot bullet
def Shoot():
# Declare bullet state as global
nonlocal state
if state == "ready":
state = "fire"
# Move bullet above player
x, y = soldier.position()
bullet.setposition(x, y + 10)
bullet.showturtle()
# HitBoxes
def checkCollision(a, b):
return a.distance(b) < 30
# Move plane
def MovePlane():
x = plane.xcor() + plane_speed
plane.setx(x)
if plane.xcor() > 280:
plane.hideturtle()
def action():
nonlocal state, score
# Move Plane
MovePlane()
# Show Paratrooper
x = -200
for troop in troopers:
if plane.xcor() > x + 50:
x += 80
troop.setx(x)
troop.showturtle()
y = troop.ycor() - troop_speed
troop.sety(y)
if y < -280:
troop.hideturtle()
if checkCollision(bullet, troop):
# Reset bullet
bullet.hideturtle()
state = "ready"
bullet.setposition(0, -400)
troop.shape("boom.gif")
# Update Scoreboard
score += 10
scorestring = "Score: %s" % score
score_pen.clear()
score_pen.write(scorestring, align="left", font=("Arial", 14, "normal"))
troop.setposition(1000, 1000)
if state == "fire":
y = bullet.ycor()
if y > 300:
state = 'ready'
bullet.hideturtle()
else:
bullet.sety(y+ bullet_speed)
screen.ontimer(action, 100)
score = 0
# Define bullet state
# ready = ready to fire
# fire = bullet is firing
state = "ready"
root.geometry("800x600")
root.title("Trooper shooter")
canvas = ScrolledCanvas(root)
canvas.pack(fill=tk.BOTH, expand=tk.YES)
screen = TurtleScreen(canvas)
screen.bgcolor("black")
screen.bgpic("background1.png")
screen.register_shape("invader.gif")
screen.register_shape("plane.gif")
screen.register_shape("troop.gif")
screen.register_shape("player1.gif")
screen.register_shape("player2.gif")
screen.register_shape("boom.gif")
# Player
soldier = RawTurtle(screen)
soldier.color("blue")
soldier.shape("player1.gif")
soldier.penup()
soldier.speed('fastest')
soldier.setposition(0, -200)
soldier.setheading(90)
soldierspd = 20
# Score
score_pen = RawTurtle(screen)
score_pen.speed('fastest')
score_pen.hideturtle()
# Plane
plane = RawTurtle(screen)
plane.color("red")
plane.shape("plane.gif")
plane.penup()
plane.speed('fastest')
plane.setposition(-270, 200)
plane.setheading(360)
plane_speed = 20
# ParaTroopers
number_troops = 5
troopers = []
for _ in range(number_troops):
troop = RawTurtle(screen)
troop.color("Purple")
troop.shape("troop.gif")
troop.penup()
troop.speed('fastest')
troop.hideturtle()
x, y = -170, 270
troop.setposition(x + 30, y)
troop.setheading(270)
troopers.append(troop)
troop_speed = 3
# Bullet
bullet = RawTurtle(screen)
bullet.color("yellow")
bullet.shape("triangle")
bullet.penup()
bullet.speed('fastest')
bullet.setheading(90)
bullet.shapesize(0.5)
bullet.hideturtle()
bullet_speed = 30
screen.onkey(Left, "Left")
screen.onkey(Right, "Right")
screen.onkey(Shoot, "space")
screen.listen()
action()
screen.mainloop()

Error with the command onkey()

I'm trying to create a shooter-like game in Python turtle (it's basically a copy of the game Boom dots). But I'm having a lot of issues, because I'm kind of new to programming. This time the command onkey() doesn't work. I tried everything but nothing seems to be of help.
I don't get any traceback errors. It's just that defined command doesn't work when I press the button which is assigned to the command.
Part of code in which I suspect the problem is:
def cannon_left():
cannon_x = cannon_x - 10
cannon.goto(cannon_x, 0)
def cannon_right():
cannon_x = cannon_x + 10
cannon.goto(cannon_x, 0)
def reset1():
live_score = 0
The whole code:
import random
import turtle
#images
image_coconut = "Coconut.png"
image_banana = "Banana.png"
image_pineapple = "Pineapple.png"
image_cannon = "Cannon.png"
#definitions
live_score = 0
screen = turtle.Screen()
wn = turtle.Screen()
cannon = turtle.Turtle()
enemy = turtle.Turtle()
score = turtle.Turtle()
background = turtle.Turtle()
reset = turtle.Turtle()
bullet = turtle.Turtle()
enemy_x = enemy.xcor()
enemy_y = enemy.ycor()
cannon_x = 0
move_speed = 2
enemy1 = 0
def cannon_shooting(x, y):
bullet.showturtle()
bullet.forward(280)
if bullet.ycor() == enemy_y - 10:
if not bullet.xcor() == enemy_x - 10:
if live_score == 0:
live_score = 0
else:
live_score = live_score + 1
if bullet.xcor() == enemy_x - 10:
live_score = live_score + 1
enemy1 = random.randint(1, 3)
bullet.hideturtle()
#image adding
screen.addshape(image_coconut)
screen.addshape(image_banana)
screen.addshape(image_pineapple)
screen.addshape(image_cannon)
def cannon_left():
cannon_x = cannon_x - 10
cannon.goto(cannon_x, 0)
def cannon_right():
cannon_x = cannon_x + 10
cannon.goto(cannon_x, 0)
def reset1():
live_score = 0
#setup
bullet.hideturtle()
bullet.speed(50)
bullet.penup()
bullet.shape('circle')
bullet.goto(0, -140)
bullet.left(90)
enemy.speed(0)
enemy.penup()
enemy.hideturtle()
enemy.goto(0, 140)
screen.addshape(image_coconut)
enemy.shape(image_coconut)
enemy.showturtle()
cannon.speed(0)
cannon.penup()
cannon.hideturtle()
cannon.goto(0, -140)
screen.addshape(image_cannon)
cannon.shape(image_cannon)
cannon.showturtle()
cannon.left(90)
score.speed(0)
score.penup()
score.hideturtle()
score.goto(90, -190)
score.color('white')
score.write("Your score: %s" % live_score, font=(None, 11, "bold"))
reset.speed(0)
reset.penup()
reset.hideturtle()
reset.goto(-185, -190)
reset.color('white')
reset.write("Reset (R)", font=(None, 11, "bold"))
#movement
while True:
enemy.forward(move_speed)
if enemy.xcor() == 140:
enemy.left(180)
enemy.forward(move_speed)
if enemy.xcor() == -140:
enemy.right(180)
enemy.forward(move_speed)
if enemy1 == 1:
screen.addshape(image_banana)
enemy.shape(image_banana)
if enemy1 == 2:
screen.addshape(image_pineapple)
enemy.shape(image_pineapple)
if enemy1 == 3:
enemy.shape(image_coconut)
#key presses
wn.onkey(cannon_right, "D")
wn.onkey(cannon_left, "A")
wn.onkey(cannon_right, "Right")
wn.onkey(cannon_left, "Left")
wn.onkey(cannon_shooting, "SPACE")
wn.onkey(reset1, "R")
#others
wn.listen()
wn.mainloop()
Note: I'm creating the game in Trinket.io. Click here to go to the Trinket.io version.
Python is an imperative programming language. This means that order matters. What appears to be the main logic of your game is declared before the onkey initialization part as an infinite loop:
#movement
while True:
enemy.forward(move_speed)
...
As this loop runs forever, it means that will start executing and the code will never reach the part where you set up the key mapping.
You need the code that is in the loop put this code in a function, and decide when exactly it needs to be called by Turtle. You should not put the while True as part of the function, as there already exists a main loop managed by Turtle.

Tkinter events outside of the mainloop?

The program I am writing has a tkinter window that is constantly being fed with data manually rather than being part of a mainloop. It also needs to track mouse location. I havn't found a workaround for tracking the mouse outside of mainloop yet, but if you have one please do tell.
from Tkinter import *
import random
import time
def getCoords(event):
xm, ym = event.x, event.y
str1 = "mouse at x=%d y=%d" % (xm, ym)
print str1
class iciclePhysics(object):
def __init__(self, fallrange, speed=5):
self.speed = speed
self.xpos = random.choice(range(0,fallrange))
self.ypos = 0
def draw(self,canvas):
try:
self.id = canvas.create_polygon(self.xpos-10, self.ypos, self.xpos+10, self.ypos, self.xpos, self.ypos+25, fill = 'lightblue')
except:
pass
def fall(self,canvas):
self.ypos+=self.speed
canvas.move(self.id, 0, self.ypos)
root = Tk()
mainFrame = Frame(root, bg= 'yellow', width=300, height=200)
mainFrame.pack()
mainCanvas = Canvas(mainFrame, bg = 'black', height = 500, width = 500, cursor = 'circle')
mainCanvas.bind("<Motion>", getCoords)
mainCanvas.pack()
root.resizable(0, 0)
difficulty = 1500
#root.mainloop()
currentIcicles = [iciclePhysics(difficulty)]
root.update()
currentIcicles[0].draw(mainCanvas)
root.update_idletasks()
time.sleep(0.1)
currentIcicles[0].fall(mainCanvas)
root.update_idletasks()
tracker = 0
sleeptime = 0.04
while True:
tracker+=1
time.sleep(sleeptime)
if tracker % 3 == 0 and difficulty > 500:
difficulty -= 1
elif difficulty <= 500:
sleeptime-=.00002
currentIcicles.append(iciclePhysics(difficulty))
currentIcicles[len(currentIcicles)-1].draw(mainCanvas)
for i in range(len(currentIcicles)):
currentIcicles[i].fall(mainCanvas)
root.update_idletasks()
for i in currentIcicles:
if i.ypos >= 90:
currentIcicles.remove(i)
root.update_idletasks()
There is no way. Mouse movement is presented to the GUI as a series of events. In order to process events, the event loop must be running.
Also, you should pretty much never do a sleep inside a GUI application. All that does is freeze the GUI during the sleep.
Another hint: you only need to create an icicle once; to make it fall you can use the move method of the canvas.
If you are having problems understanding event based programming, the solution isn't to avoid the event loop, the solution is to learn how event loops work. You pretty much can't create a GUI without it.

Categories