I have the following code,
If I press 'Left Arrow Key', it only prints move player to left
But I need a functionality in which pressing the given arrow key moves the player in the given direction.
Is there a way in which I can detect key press event in my move_dir function
PS: fairly new to python
import Tkinter as tk
move = 1
pos = -1
def move_dir():
global move
global pos
while move ==1:
if pos == 0:
print 'move player to left'
elif pos == 1:
print 'move player to right'
elif pos == -1:
print 'stop moving!'
def kr(event):
global move
global pos
global root
if event.keysym == 'Right':
move = 1
pos = 0
move_dir()
print 'right ended'
elif event.keysym == 'Left':
move = 1
pos = 1
move_dir()
print 'left ended'
elif event.keysym == 'Space':
move = 0
move_dir()
elif event.keysym == 'Escape':
root.destroy()
root = tk.Tk()
print( "Press arrow key (Escape key to exit):" )
root.bind_all('<KeyRelease>', kr)
root.mainloop()
If you're wanting to animate something, you should use after to set up an animation loop. An animation loop looks like this:
def animate():
<draw one frame of your animation>
after(<delay>, animate)
You can put whatever code you want to draw a frame. In your case it sounds like you want to move something left or right. The <delay> parameter defines your frame rate. For example, to get 30FPS your delay would be about 33 (1000ms / 30). The only important thing to be aware of is that <draw one from of your animation> needs to run pretty quickly (10's of milliseconds or less) in order to not block the GUI.
For your particular problem, you can set a variable to define the direction when the user presses a key, then unset the variable when they release the key. Your event handler and animate function might look something like this:
def animate():
if direction is not None:
print "move player to the ", direction
after(33, animate)
def on_keypress(event):
global direction
if event.keysym == "Left":
direction = "left"
elif event.keysum == "right":
direction = "right"
def on_keyrelease(event):
global direction
direction = None
See how that works? When you press a key it defines the direction. Then, every 33 milliseconds you check for the direction, and move your player if the direction is defined. When the user releases the button, the direction becomes undefined and the movement stops.
Putting it all together, and using a class to avoid using global variables, it looks something like the following. This creates a ball on a canvas which you can move left, right, up and down:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.direction = None
self.canvas = tk.Canvas(width=400, height=400)
self.canvas.pack(fill="both", expand=True)
self.canvas.create_oval(190, 190, 210, 210,
tags=("ball",),
outline="red", fill="red")
self.canvas.bind("<Any-KeyPress>", self.on_press)
self.canvas.bind("<Any-KeyRelease>", self.on_release)
self.canvas.bind("<1>", lambda event: self.canvas.focus_set())
self.animate()
def on_press(self, event):
delta = {
"Right": (1,0),
"Left": (-1, 0),
"Up": (0,-1),
"Down": (0,1)
}
self.direction = delta.get(event.keysym, None)
def on_release(self, event):
self.direction = None
def animate(self):
if self.direction is not None:
self.canvas.move("ball", *self.direction)
self.after(50, self.animate)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
EDIT 4
You have a while loop that you want to combine with the Tkinter mainloop.
In this case you want to move when the key is pressed and stop moving when a key is released.
The code below allows you to do this:
import Tkinter as tk
from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951#file-guiloop-py
direction = 0
pos = 0 # the position should increase and decrease depending on left and right
# I assume pos can be ... -3 -2 -1 0 1 2 3 ...
#guiLoop
def move_dir():
global pos
while True: # edit 1: now looping always
print 'moving', direction
pos = pos + direction
yield 0.5 # move once every 0.5 seconds
def kp(event):
global direction # edit 2
if event.keysym == 'Right':
direction = 1 # right is positive
elif event.keysym == 'Left':
direction = -1
elif event.keysym == 'Space':
direction = 0 # 0 is do not move
elif event.keysym == 'Escape':
root.destroy()
def kr(event):
global direction
direction = 0
root = tk.Tk()
print( "Press arrow key (Escape key to exit):" )
root.bind_all('<KeyPress>', kp)
root.bind_all('<KeyRelease>', kr)
move_dir(root)
root.mainloop()
To see how this is implemented you can read the source code or read the second answer by Bryan Oakley.
EDIT 3
There is no way to detect a keypress in the move_dir function directly.
You can use root.update() in your move_dir function to make it possible for kr, kp to be executed when root.update is called. root.update() alse repaints the windows so that changes can be seen by the user.
root.mainloop() can be seen as while True: root.update()
Related
from tkinter import *
window = Tk()
canvas = Canvas(window, width=1280, height=720)
canvas.pack()
a = (0, 50), (50, 100) # coordinates of the rectangle
rect = canvas.create_rectangle(a, fill="red")
#rect = canvas.create_oval(a, fill="red")
speed = 5 # speed of the rectangle
jump = False
def keypress(event):
x = 0
y = 0
if event.char == "a": x-= speed
elif event.char == "d": x+= speed
elif event.char == "w": y-= speed
elif event.char == "s": y+= speed
elif event.char == " ":
y = 50
jump = True
while jump:
y = 0
jump = False
canvas.move(rect, x, y)
canvas.update()
canvas.after(1)
window.bind("<Key>", keypress)
window.mainloop()
This is my code, im trying to make the rectangle jump but whenever i add the jump code, everything stops working even the normal "asdw" movement that would work otherwise
In case you just want to do a simple jumping animation where the rectangle goes up and down again, just use physics: Define a gravitational pull and an initial vertical velocity and then loop until the ground is reached again:
elif event.char == " ":
diff = 0 ## Difference to initial level
y = -3 ## Initial speed in y direction
grav = .1 ## Gravitation
while diff >= 0: ## While it is still jumping (higher than initially)
canvas.move(rect, x, y)
canvas.update()
sleep(.01) ## Pause for 1/100 second
diff-=y ## Update current jumping height
y+=grav ## Update the speed in y direction
y = 0 ## Just so it is not moved again, afterwards
To make this code snippet work, you have to import time.sleep at the beginning of the program:
from time import sleep
I have to make a space invaders game for my intermediate python class using the Zelle graphics package. I have been having some issues with the projectile, it will run through the code, shoot the bullet, and continue to move the bullet, but while the bullet is moving, I can't move the character. I assume this is a pretty simple problem, but I'm not experienced enough with programing yet. Thanks.
from graphics import *
from time import *
game_over = False
win = GraphWin("skulls and bones", 500, 500)
skull = Image(Point(250, 450), "skull_2.gif")
skull.draw(win)
bullet = Circle(Point(250, 250), 10)
bullet.setFill("red")
def bone():
value = 0
key = win.getKey()
if key == "x":
bullet.draw(win)
return bullet
def space_ship_move():
key = win.getKey()
if key == "a":
skull.move(-10, 0)
if key == "d":
skull.move(10, 0)
if key == "x":
bone()
for i in range(100):
sleep(1)
bone().move(0, -5)
return key
while not game_over:
space_ship_move()'''
There are two problems: First, win.getKey() blocks until the user presses a key. You don't want to do that - use win.checkKey(). Second, you need to decouple animation and input handling because you want your bullet to also move if there is no input. You can do this with the following structure:
def space_ship_move():
handle_input()
animate()
sleep(0.1)
handle_input() is mostly what you already had in there:
bullet = None
bulletAnimStep = -1
def bone():
global bullet, bulletAnimStep
if bullet is not None:
bullet.undraw()
bullet = Circle(Point(250, 250), 10)
bullet.setFill("red")
bullet.draw(win)
bulletAnimStep = 0
def handle_input():
key = win.checkKey()
if key == "a":
skull.move(-10, 0)
if key == "d":
skull.move(10, 0)
if key == "x":
bone()
I assumed bone() was supposed to be responsible for creating a new bullet. This is what the current function does. It also sets a bulletAnimStep to 0, indicating that we are at the beginning of the bullet animation. Our animate() can use that information to decide when to stop the bullet animation. This is how it could look:
def animate():
global bulletAnimStep
#animate bullet
if bulletAnimStep >= 0:
bulletAnimStep += 1
bullet.move(0, -5)
if bulletAnimStep >= 100:
#the bullet has gone far enough
bullet.undraw()
bulletAnimStep = -1
I've just started learning Python (as of now the only other programming language I'm fluent in is C, so this is all very new to me), and I've decided to make some simple game with it just to get used to it. I'd like it to be an arrow keys controlled game and I'm using the graphics.py library which to my understanding is very beginner friendly.
This is what I came up with:
from graphics import *
win = GraphWin("Game", 800, 500)
def getXInput():
inputBuffer = win.checkKey()
inputResult = 0
if inputBuffer == "Right":
inputResult = 1
elif inputBuffer == "Left":
inputResult = -1
return inputResult
def getYInput():
inputBuffer = win.checkKey()
inputResult = 0
if inputBuffer == "Down":
inputResult = 1
elif inputBuffer == "Up":
inputResult = -1
return inputResult
class player:
def __init__(self):
self.x = 400
self.y = 250
self.speed = 3
self.canMove = True
self.txt = Text(Point(self.x, self.y), "test")
self.txt.draw(win)
def move(self):
while self.canMove == True:
xMove = getXInput() * self.speed
yMove = getYInput() * self.speed
self.txt.move(xMove, yMove)
def main():
pl = player()
pl.move()
win.close()
main()
Player input is managed through the getXInput and getYInput functions, which costantly checks if the arrow keys are pressed and return -1, 0 or 1 accordingly.
Then the result of those functions is costantly multiplied for the player's attribute "speed" and that's how the game is able to move the player character in the correct direction. This kinda works: it works perfectly on the x axis, but it acts weird on y axis, registering inputs only once in a while. Putting the yMove assignment before the xMove one within the move method reverses the problem, making vertical movement perfect and horizontal movement very sloppy. I really can't understand what's causing this, can anybody help?
I am trying to make a cube able to accelerate and decelerate in all 4 directions using the arrow keys. I was able to solve it for 1 direction (up), but I cannot figure out how to do it in the other 3. Basically I just need it to do what my current code does, just in all 4 directions.
from tkinter import *
SPEED = 1
def handle_key(event):
sym = event.keysym
print(sym)
cdx, cdy = 0, 0
if sym == 'Up':
dy.set(dy.get()-SPEED)
elif sym == 'Down':
dy.set(SPEED)
elif sym == 'Right':
cdx = SPEED
elif sym == 'Left':
cdx = -SPEED
dx.set(cdx)
def animate():
canvas.move(player, dx.get(), dy.get())
if dy.get() < 0:
dy.set(min(dy.get() + .12, 0))
canvas.after(1, animate)
root = Tk()
dx = DoubleVar()
dy = DoubleVar()
canvas = Canvas(root, width=800, height=600)
canvas.pack()
player = canvas.create_rectangle(395, 590, 405, 600, fill='red')
canvas.bind('<Key>', handle_key)
canvas.focus_set()
animate()
root.mainloop()
Your code have few issues like function animate() will run continuously even if there is no key press event. Also the code can be a lot better like you can use <KeyPress> to accelerate and <KeyRelease> to decelerate. I created an example of what you're trying to do.
I used after(1,..) to accelerate and decelerate smoothly as continuous key presses have some noticeable delay.
from tkinter import *
root = Tk()
root.geometry('1200x50+50+0')
speed = DoubleVar(value=1) # Speed of the item
multiplier = DoubleVar(value=0.05) # Increase / Decrease rate
acc_id = None # after id for acceleration
des_id = None # after id for deceleration
canvas = Canvas(root, bg='pink')
canvas.pack(expand=1, fill='both')
# Player item
player = canvas.create_rectangle(10,10,40,40, fill='grey', outline='black')
def direction(key):
"""Moves the item according to the arrow keys"""
if key == 'Left': canvas.move(player, -speed.get()*multiplier.get(), 0)
if key == 'Right': canvas.move(player, speed.get()*multiplier.get(), 0)
# if key == 'Up': canvas.move(player, 0, -speed.get()*multiplier.get())
# if key == 'Down': canvas.move(player, 0, speed.get()*multiplier.get())
def accelerate(evt=None):
"""Increase the speed."""
global des_id, acc_id
# set the values according to your needs
multiplier.set( multiplier.get() + 0.05 )
direction(evt.keysym)
if des_id: canvas.after_cancel(des_id)
acc_id = canvas.after(1, accelerate, evt)
def decelerate(evt=None):
"""Decrease the speed."""
global des_id, acc_id
if acc_id: canvas.after_cancel(acc_id)
# set the values according to your needs
multiplier.set( multiplier.get() - 0.05 )
if multiplier.get() >= 0:
direction(evt.keysym)
des_id = canvas.after(1, decelerate, evt)
root.bind('<KeyPress>', accelerate)
root.bind('<KeyRelease>', decelerate)
root.mainloop()
So I'm making small glow hokey game in tkinter and i faced wall.Compiler only catches the one key event and if second user presses key ,1st users movement ll stop. do you guys know how to slove this problem?
here is code:
from tkinter import*
w=600
h=300
padis_sigane=10
padis_sigrdze=75
padis_sichqare=5
root=Tk()
root.geometry("{}x{}".format(w,h))
root.resizable(False,False)
c=Canvas(root,width=w,height=h,bg="green")
c.create_line(w//2,0,w//2,h,width=10,fill="white")
c.create_line(padis_sigane,0,padis_sigane,h,width=2,fill="white")
c.create_line(w-padis_sigane,0,w-padis_sigane,h,width=2,fill="white")
c.create_oval(w//2-w//30,h//2-w//30,w//2+w//30,h//2+w//30,fill="white",outline="white")
class chogani:
def __init__(self,x,y):
self.x=x
self.y=y
self.pad=c.create_rectangle(self.x,self.y,self.x+padis_sigane,self.y+padis_sigrdze,fill="lightblue",outline="white")
def shxuili(self):
if c.coords(self.pad)[3]>=h:
c.coords(self.pad,self.x,h-padis_sigrdze,self.x+padis_sigane,h)
elif c.coords(self.pad)[1]<=0:
c.coords(self.pad,self.x,0,self.x+padis_sigane,padis_sigrdze)
x=0;y=0 #Momavalshi
pad1=chogani(0,1)
pad2=chogani(w-padis_sigane,1)
def K(event):
pad1.shxuili()
pad2.shxuili()
if event.keysym=='w':
c.move(pad1.pad,0,-padis_sichqare)
elif event.keysym=='s':
c.move(pad1.pad,0,padis_sichqare)
elif event.keysym=='Up':
c.move(pad2.pad,0,-padis_sichqare)
elif event.keysym=='Down':
c.move(pad2.pad,0,padis_sichqare)
def R(event):
print("shen aushvi ", event.char)
root.bind("<KeyPress>",K)
root.bind("<KeyRelease>",R)
root.focus_set()
c.pack()
root.mainloop()
In other modules - like PyGame - you use variables like w_pressed = True/False and up_pressed = True/False which you change when key is pressed or released. Next you create mainloop which checks this variables to move objects. Because tkinter has already mainloop so you can use after() to execute periodically own function which will check w_pressed/up_pressed and move objects.
Simple (working) example:
It checks w and up and displays True/False for both keys.
import tkinter as tk
# --- functions ---
def pressed(event):
global w_pressed
global up_pressed
if event.keysym == 'w':
w_pressed = True
elif event.keysym == 'Up':
up_pressed = True
def released(event):
global w_pressed
global up_pressed
if event.keysym == 'w':
w_pressed = False
elif event.keysym == 'Up':
up_pressed = False
def game_loop():
# use keys
print(w_pressed, up_pressed)
# run again after 500ms
root.after(500, game_loop)
# --- data ---
w_pressed = False
up_pressed = False
# --- main ---
root = tk.Tk()
root.bind("<KeyPress>", pressed)
root.bind("<KeyRelease>", released)
# start own loop
game_loop()
root.mainloop()