Issue using num pad keys for Python turtle graphics onkey function - python

I am having an issue creating turtle.onkey commands using the numpad 1-9 keys as input.
I looked at the source and documentation, it appears the keys taken as arguments come from tkinker. I found a list of keys from the documentation there as well as this list and from what I can gather the argument should be "KP_4" for the number '4' on the numpad, but my code will not take it. I have tried more traditional keys like "Left" for the left arrow and these seem to work fine. I also looked into a document on here about pygame thinking maybe it was similar, but the one they list for the numpad 4 did not work either. (it was K_KP4)
def player_move_left():
x = player_char.xcor()
x -= player_max_move
player_char.setx(x)
turtle.onkey(player_move_left,"K_P4")
This should take the x coordinate, and subtract the movement amount then apply that number to the player variable's x-coordinate.
* Solution provided in the first answer*

My (OS X) system doesn't distinguish between "4" on the main keys and "4" on the keypad, both moved the turtle. However, it does distinguish between "Return" on the main keys and "KP_Enter" on the keypad so I'll use that in my example code:
from turtle import Screen, Turtle
player_max_move = 10
def player_move_left():
x = player_char.xcor() - player_max_move
player_char.setx(x)
screen = Screen()
player_char = Turtle()
screen.onkey(player_move_left, "KP_Enter") # vs "Return"
screen.listen()
screen.mainloop()
Experiment with the above to see if you can get insight into your problem (e.g. is there any step you left out?)

Related

Using onscreen.click() in turtle but only works at the end of the programme

This is the programme I wrote but I'm not sure what's wrong:-
import turtle
import random
bob = turtle.Turtle()
screen = turtle.Screen()
def coord(x,y):
print(x,y)
turtle.onscreenclick(None)
turtle.onscreenclick(coord)
turtle.listen()
print('hello')
turtle.done()
The programme works fine except that the print('hello') part happens first, followed by the on screen event. How do I make the onscreen event occur first before the rest of my programme?
You can simply make the rest of your code part of the coord function:
def coord():
print(x,y)
turtle.onscreenclick(None)
print("Hello")
# The rest of your program goes here.
However, a few things to note:
This isn't an amazing elegant solution, especially if you intend to set up other events further into your code. It can become quite hard to keep track of.
It's imperative that you remove the event binding (in this case the binding of coord to onscreenclick) as soon as it's been used, otherwise you could end up with multiple instances of the same code running at once if someone double-clicked the screen, for example. In your case you've already done this (with turtle.onscreenclick(None)), but it is something to keep in mind.
If you choose to go this route, don't forget to rename the coord function something more representative of what that section of your code will do.

Is there a way to wait for a condition to be true?

I'm attempting to log coordinates 3 separate times when a user clicks on the turtle screen, then continue running other commands once that is completed. Clicking 3 times does nothing, and the shell keeps printing that it's waiting, while one additional click causes the whole thing to not work and I get a "not Responding" message from the turtle graphics window.
import turtle as t
import time
canvas=t.getcanvas()
xlist=[]
ylist=[]
listcomplete=False
def getPos(x,y):
xlist.append(canvas.winfo_pointerx()) ##Logs the x and y coords when mouse is clicked
ylist.append(canvas.winfo_pointery())
print('appended the lists.')
if len(xlist)==3:
listcomplete=True
t.onscreenclick(getPos)
def main():
while listcomplete==False:
time.sleep(1)
print('waiting...') ##Prints periodically just to let me know it's still running
main()
print('list complete.') ##Prints to alert the list has been finished
print(xlist)
(Insert rest of code to follow)
listcomplete=True within getPos() will not change the global variable, instead it will create a new varable of the same name within the local scope.
To change the global variable, you have to tell python to use it from the global scope:
def getPos(x,y):
global listcomplete # tell python to use the variable from the global scope
xlist.append(canvas.winfo_pointerx()) ##Logs the x and y coords when mouse is clicked
ylist.append(canvas.winfo_pointery())
print('appended the lists.')
if len(xlist)==3:
listcomplete=True
That's due to the default behavior of the assignment operator (=).
Other operators, such as the comparision operator (==) will lookup the variable from the enclosing scope(s) if it's not found within the local scope, thus you may use while listcomplete==False: within main() w/o telling pyton to use the variable from the global scope.
But ideally, you do not even have to use that global variable. Instead run the turtle main loop and exit the turtle window when your condition is met:
import turtle as t
canvas=t.getcanvas()
xlist=[]
ylist=[]
def getPos(x,y):
xlist.append(canvas.winfo_pointerx()) ##Logs the x and y coords when mouse is clicked
ylist.append(canvas.winfo_pointery())
print('appended the lists.')
if len(xlist)==3:
t.bye() # exit turtle window
t.onscreenclick(getPos)
t.Screen().mainloop() # will wait until turtle window is closed
print('list complete.') ##Prints to alert the list has been finished
print(xlist)
Is it possible to continue running the turtle window after the lists
have been created?
Things get difficult in turtle when you fight it's event-based model as you're trying to do. Work with the model, and things get easier. The code below presents a blank window, after you click on it in three places, it will connect your points to make a triangle:
from turtle import Screen, Turtle, mainloop
def getPosition(x, y):
screen.onscreenclick(None) # disable the handler inside the handler
positions.append((x, y))
if len(positions) == 3:
screen.ontimer(listComplete) # sometime after this function completes
else:
screen.onscreenclick(getPosition) # restore the handler
def listComplete():
for position in positions:
turtle.goto(position)
turtle.pendown()
turtle.goto(positions[0]) # close our triangle
# (Insert rest of code to follow)
positions = []
turtle = Turtle()
turtle.hideturtle()
turtle.penup()
screen = Screen()
screen.onscreenclick(getPosition)
mainloop() # invoke as function to make Python 2 friendly as well
The key is that "rest of code to follow" will be in functions, not top level code.
def handle_click(mouse_x,mouse_y): # <== I think thats what ya need right dere, I dont think it knows you're clicking
newpin = [[mouse_x,mouse_y], [0,0], 0,0,20, 1000000]
I tried putting a print statement after the click I couldn't get it to even print a test click. That may be me not trying hard enough tho ;) I just remember using something like above to handle a mouse click. (in my situation it created a pin for a pinball game) if you look up the turtle api for circle you can see the [0,0],0,0,20, 100000] means.
But ultimately that last number the 10000 whatever is "mass" so the more of it the less it moves. again my situation. turtle.onscreenclick(handle_click). Thats at least an idea :) also yes u can do a wait after an if. Throw in print statements.

Randomly chose an action (definition) with random.choice()

Code doesn't work like it should. It is just a small thing i would like to learn as a new coder:
import turtle
import random
me=turtle.Turtle()
def up_right():
me.right(90)
me.forward(100)
def down_right():
me.right(90)
me.forward(100)
choose = (up_right(), down_right)
random.choice(chose)
It should pick one and do it but it picks them both.
I've tried random.sample and random.choice but cant get them to work.
Besides the typo of choose... My suggestion is that after you create a tuple of functions and choose the function using random.choice(), you should make a call to the function chosen by the random.choice().
# Notice I removed the () after up_right so it doesn't make the function call on this line
choose = (up_right, down_right)
# random.choice will return one of the two, and then the () will call whatever function was chosen
random.choice(choose)()
I see three issues with your code. The first two folks have pointed out already, the choose vs chose typo and leaving the parens () on upright when refering to it as a function (up_right(), down_right).
The third is that up_right and down_right both implement the same motion, so even if the rest of your code worked, you wouldn't see any difference! Below's a rewrite that fixes this issue:
from turtle import Screen, Turtle
from random import choice
def up_right(turtle):
turtle.setheading(90)
turtle.forward(100)
def down_right(turtle):
turtle.setheading(270)
turtle.forward(100)
choices = [up_right, down_right]
screen = Screen()
me = Turtle('turtle')
choice(choices)(me)
screen.mainloop()
Run it several times and you'll see sometimes the turtle heads up the screen, sometimes it heads down.

Turtle gives error: AttributeError: 'Turtle' object has no attribute 'onkeyrelease'

I am trying to add 1 every time I release a key:
from turtle import *
import turtle
turtle1 = Turtle()
screen = turtle1.getscreen()
goPressed = False
imported Turtle...
currentWatts=0
def onaclicked():
global currentWatts
currentWatts+=1
print (currentWatts)
defined my function to be run when the key: 1, is released
turtle.onkeyrelease(onaclicked, "1")
for some reason onkeyrelease isn't there even though I imported Turtle and checked in Python documentation. It SHOULD work, shouldn't it? Did I improperly import? Can you please help me?
The reason I want it to be onkeyrelease instead of onkey, is because it is for a game. With onkey, when you hold your finger on the key, it adds 1 to currentWatts every around 0.25 seconds. You could cheat by placing something on the key so I want it only to add 1 when you release the key.
You've several problems with your code: you import turtle two different ways which confuses things; onkeyrelease() is really a method of the screen/window, not a turtle; you didn't call listen() which allows keystrokes to be processed. The following should work in Python 3:
from turtle import Turtle, Screen, mainloop
def onaclicked():
global currentWatts
currentWatts += 1
print(currentWatts)
currentWatts = 0
screen = Screen()
screen.onkeyrelease(onaclicked, "1")
screen.listen()
mainloop()
Make sure to click on the window once before you start typing to make it active.
If you're using Python 2, which I suspect from the error message you got, then replace the Python 3 alias onkeyrelease with onkey:
The method Screen.onkeypress() has been added as a complement to
Screen.onkey() which in fact binds actions to the keyrelease event.
Accordingly the latter has got an alias: Screen.onkeyrelease().
This change should work the same in both versions. Using onkeyrelease instead of onkey wasn't going fix your holding a finger on the key issue.
when you hold your finger on the key, it adds 1 to
currentWatts every around 0.25 seconds. You could cheat by placing
something on the key so I want it only to add 1 when you release
It appears that automatic key repeats are handled by the operating system and may need to be disabled external to Python, depending on the OS. Some example links:
Apple OSX: Set how quickly a key
repeats
Ubuntu: Turn off repeated key
presses
X-Windows from Python: key repeat in
tkinter

Python Turtle Graphics - Bring A Turtle To The Front

I have two turtles in my program. An animation happened where they collide together, but I would like one turtle to be on top of the other like this:
So, my question is - how can I make this happen - is there a simple line of code such as: turtle.front(), if not what is it?
I discuss this briefly in my response to Make one turtle object always above another where the rule of thumb for this simple situation is:
last to arrive is on top
Since Python turtle doesn't optimize away zero motion, a simple approach is to move the turtle you want on top by a zero amount:
import turtle
def tofront(t):
t.forward(0)
gold = turtle.Turtle("square")
gold.turtlesize(5)
gold.color("gold")
gold.setx(-10)
blue = turtle.Turtle("square")
blue.turtlesize(5)
blue.color("blue")
blue.setx(10)
turtle.onscreenclick(lambda x, y: tofront(gold))
turtle.done()
The blue turtle overlaps the gold one. Click anywhere and the situation will be reversed.
Although #Bally's solution works, my issue with it is that it creates a new turtle everytime you adjust the layering. And these turtles don't go away. Watch turtle.turtles() grow. Even my solution leaves footprints in the turtle undo buffer but I have to believe it consumes less resources.
Just add this to turtle.py file
def tofront(self, tobring):
newfront = tobring.clone()
tobring.ht()
return newfront
works correct,
so method - sould return you new clone of turtle, on the top of all other turtles.
parameter tobring - it's your current turtle (the one you want to bring to front)
you can use this def in your program or put it into turtle.py file
if so, don't forget to add it to list of commands, - _tg_turtle_functions
_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'tofront', 'color',
I did it after clone command
Hope this will help you

Categories

Resources