How to catch two key events in Tkinter - python

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

Related

Why does my object copy its screenpos attribute from viewed_screen_pos after running once?

#Dashman_Classes
import pygame
class Platform(pygame.sprite.Sprite):
def __init__(self,xbound,ybound,center):
super().__init__()
self.xbound=xbound
self.ybound=ybound
self.center=center
self.surf = pygame.Surface((self.xbound, self.ybound))
self.colour=((255,0,0))
self.rect = self.surf.get_rect(center = self.center)
def change_size(self):
self.surf = pygame.Surface((self.xbound, self.ybound))
self.surf.fill(self.colour)
self.rect = self.surf.get_rect(center = self.center)
class Screen():
def __init__(self,platform_list,screenpos,respawn_point):
self.platform_list=list(platform_list)
self.screenpos=screenpos
self.respawn_point=respawn_point
self.level_platforms_list=pygame.sprite.Group()
class Level():
def __init__(self,screen_array,level_num,level_name):
self.screen_array=screen_array
self.level_num=level_num
self.level_name=level_name
#Level Editor
import pygame
from Dashman_Classes import *
pygame.init()
xbound=1000
ybound=1000
window = pygame.display.set_mode([xbound, ybound])
pygame.display.set_caption("Level Editor")
running = True
FPS = pygame.time.Clock()
screen_1=Screen([],[0,0],[500,500])
current_screen=screen_1
viewed_screen_pos=[0,0]
platform=Platform(0,0,(0,0))
platform_toggle=False
height_mode=False #False is width True is height
spawn_place_mode=False #True is placing a spawn False is not
spawn_point_indicator=pygame.Rect(0,0,0,0)
level=Level([screen_1],0,"Name")
screen_found=False#Used for screen traversal
repeated_screen=False#Checks if screen that is on during screen traversal is new
while running:
#Mouse positioning and detecting inputs
mouse_position=pygame.mouse.get_pos()
left,middle,right=pygame.mouse.get_pressed()
#Event Handling
keys=pygame.key.get_pressed()
for event in pygame.event.get():
if event.type==pygame.QUIT:
running = False
if event.type==pygame.KEYDOWN:
#Platform Placing
if event.key==pygame.K_j:
if platform_toggle==False:
platform=Platform(100,100,mouse_position)
platform_toggle=True
height_mode=False
else:
platform=Platform(0,0,mouse_position)
platform_toggle=False
#Platform resize mode
if event.key==pygame.K_k:
height_mode=not height_mode
#Screen spawn placement
if event.key==pygame.K_l:
if not spawn_place_mode:
spawn_point_indicator.width=45
spawn_point_indicator.height=45
else:
spawn_point_indicator.width=0
spawn_point_indicator.height=0
spawn_place_mode = not spawn_place_mode
#Platform resizing horizontally
if not height_mode:
if event.key==pygame.K_EQUALS:
if keys[pygame.K_LSHIFT]:
if platform.xbound<1000:
platform.xbound+=100
else:
if platform.xbound>10:
platform.xbound+=10
platform.xbound+=10
elif event.key==pygame.K_MINUS:
if keys[pygame.K_LSHIFT]:
if platform.xbound>100:
platform.xbound-=100
else:
if platform.xbound>10:
platform.xbound-=10
#Platform resizing vertically
else:
if event.key==pygame.K_EQUALS:
if keys[pygame.K_LSHIFT]:
if platform.ybound<1000:
platform.ybound+=100
else:
if platform.ybound>10:
platform.ybound+=10
platform.xboundy=10
elif event.key==pygame.K_MINUS:
if keys[pygame.K_LSHIFT]:
if platform.ybound>100:
platform.ybound-=100
else:
if platform.ybound>10:
platform.ybound-=10
#Screen Traversal
if event.key==pygame.K_UP:
pass
if event.key==pygame.K_RIGHT:
viewed_screen_pos[0]+=1
for screen in level.screen_array:
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos)
if viewed_screen_pos == screen.screenpos:
current_screen=screen
screen_found=True
print("Screen found true")
break
if not screen_found:
print("second code is running")
current_screen=Screen([],viewed_screen_pos,[500,500])
level.screen_array.append(current_screen)
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos,"\n")
screen_found=False
if event.key==pygame.K_DOWN:
pass
if event.key==pygame.K_LEFT:
viewed_screen_pos[0]-=1
for screen in level.screen_array:
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos)
if viewed_screen_pos == screen.screenpos:
current_screen=screen
screen_found=True
print("Screen found true")
break
if not screen_found:
print("second code is running")
current_screen=Screen([],viewed_screen_pos,[500,500])
level.screen_array.append(current_screen)
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos,"\n")
screen_found=False
platform.change_size()
#Places down platform and saves it to the current screens array
if left and platform_toggle:
current_screen.platform_list.append(platform)
current_screen.level_platforms_list.add(platform)
platform=Platform(0,0,mouse_position)
platform_toggle=False
if right:
for screen in level.screen_array:
print(level.screen_array)
#Setting spawn for screen to indicator and placing indicator
if spawn_place_mode:
spawn_point_indicator.center=mouse_position
if left:
current_screen.respawn_point=spawn_point_indicator
spawn_place_mode=False
#print(current_screen.screenpos)
#Platform following mouse
platform.center=mouse_position
#window and drawing initial platform
window.fill((0, 0, 0))
pygame.draw.rect(window,(255,0,0),platform.rect)
pygame.draw.rect(window,(0,150,0),spawn_point_indicator)
for entity in current_screen.level_platforms_list:
window.blit(entity.surf, entity.rect)
pygame.display.update()
FPS.tick(144)
pygame.quit()
I am trying to make a level editor for my game in pygame, each level is a series of screens, and the level editor is supposed to make it so that when i move screens and a screen hasn't been created in that position yet to create a new blank screen however instead it will create 1 new screen and then it will change that new screens position (screenpos) instead of making a new one,
Code snippet for traversing screens (i've only done left and right so far)
#Screen Traversal
if event.key==pygame.K_UP:
pass
if event.key==pygame.K_RIGHT:
viewed_screen_pos[0]+=1
for screen in level.screen_array:
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos)
if viewed_screen_pos == screen.screenpos:
current_screen=screen
screen_found=True
print("Screen found true")
break
if not screen_found:
print("second code is running")
current_screen=Screen([],viewed_screen_pos,[500,500])
level.screen_array.append(current_screen)
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos,"\n")
screen_found=False
if event.key==pygame.K_DOWN:
pass
if event.key==pygame.K_LEFT:
viewed_screen_pos[0]-=1
for screen in level.screen_array:
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos)
if viewed_screen_pos == screen.screenpos:
current_screen=screen
screen_found=True
print("Screen found true")
break
if not screen_found:
print("second code is running")
current_screen=Screen([],viewed_screen_pos,[500,500])
level.screen_array.append(current_screen)
print(viewed_screen_pos,screen.screenpos,current_screen.screenpos,"\n")
screen_found=False
You'll need to replace every instance of
current_screen=Screen([],viewed_screen_pos,[500,500])
to
current_screen=Screen([],viewed_screen_pos.copy(),[500,500])
The reason for this is because when you pass the original viewed_screen_pos variable to the Screen object, when the initialization meets self.screenpos=screenpos, they are actually pointing at the same piece of data.
You see, variables only point to a piece of memory, so altering that piece of memory at any variable will affect the rest of the variables that point to that piece of memory.
For more information, see: List changes unexpectedly after assignment. Why is this and how can I prevent it?

Pygame class spawns rect and then it dissapears, how do I get it to stay?

So I'm trying to implement a class for a basic game. It woked without the class but now instead of spawning the "coin" it pops up and then immediatly dissapears. No idea as it's in the main loop. I have a moving "player" that works fine.
Here's my code:
class Coin_Class:
def __init__(self):
coin_1 = pygame.Rect(425, 30, 40, 40)
pygame.draw.rect(WIN, YELLOW, coin_1)
pygame.display.update()
# def coin_collect():
# if player.colliderect():
# coin_1.x = random.randint(0, 800)
# coin_1.y = random.randint(0, 250)
# pygame.event.post(pygame.event.Event(coin_collected))
# global score
# score += 1
# print(score)
coin_class = Coin_Class()
# main function loop
def main():
score_check = 0
clock = pygame.time.Clock()
run = True
while run:
# game speed
clock.tick(FPS)
# initialise pygame
pygame.init()
# checking all the events in pygame and looping them
for event in pygame.event.get():
# checking quit function is pressed
if event.type == pygame.QUIT:
run = False
pygame.quit()
exit()
keys_pressed = pygame.key.get_pressed() # recognise key presses
player_movement(keys_pressed, player) # movement function
draw_window() # create window function
coin_class
main()
# runs the main file
if __name__ == "__main__":
main()
Add a draw method to the coin class (also class names per PEP 8 should be in CapitalCase not Capital_Snake_Case so CoinClass):
class Coin_Class:
def __init__(self):
self.coin_1 = pygame.Rect(425, 30, 40, 40)
...
def draw(self):
pygame.draw.rect(WIN, YELLOW, self.coin_1)
And in the loop instead of using
coin_class
you would now use
coin_class.draw()
The rest can stay the same, except remove pygame.init() from the loop and put it somewhere at the start of the code after imports

algorithm writer of tkinter

I'm writing an algorithm that provides a tkinter program as output (I hope I explained myself) the program works by dragging the combobox, the text ... into a space and memorizing the coordinates.
I decided to do it with pygame, (I took the skeleton of the program from the internet and then modified it).
I have encountered several problems:
I have a problem inserting multiple images (for now I have only
inserted one),
I can't memorize the coordinates, I tried with print (event) but it doesn't work,
I can not insert the image in a specific point of the root.
here is my code:
import os,sys
import pygame as pg
pg.init()
a=0
b=0
c=200
d=200
event = pg.event.poll()
Sfondo = pg.image.load("Sfondo.jpg")
def DisegnaBackground (a,b):
Screen.blit(Sfondo,(a,b))
class Character:
def __init__(self,rect):
self.rect = pg.Rect(rect)
self.click = False
self.image = pg.image.load("ComboBox.png").convert()
Screen.blit(self.image, (c,d))
def update(self,surface):
if self.click:
self.rect.center = pg.mouse.get_pos()
surface.blit(self.image,self.rect)
def main(Surface,Player):
game_event_loop(Player)
DisegnaBackground (a,b)
Player.update(Surface)
def game_event_loop(Player):
for event in pg.event.get():
if event.type == pg.MOUSEBUTTONDOWN:
if Player.rect.collidepoint(event.pos):
Player.click = True
elif event.type == pg.MOUSEBUTTONUP:
Player.click = False
elif event.type == pg.QUIT:
pg.quit(); sys.exit()
if __name__ == "__main__":
os.environ['SDL_VIDEO_CENTERED'] = '1'
pg.init()
Screen = pg.display.set_mode((1500,800))
MyClock = pg.time.Clock()
MyPlayer = Character((0,0,200,24))
MyPlayer.rect.center = Screen.get_rect().center
while 1:
main(Screen,MyPlayer)
pg.display.update()
MyClock.tick(60)
print(event)
There are a number of problems. First, try to make sure not to mix global and local variables. Try re-writing the function like this:
def DisegnaBackground (surface, a, b):
surface.blit(Sfondo, (a, b))
For this to work, you have to update your main too:
def main(Surface,Player):
game_event_loop(Player)
DisegnaBackground (Surface, a, b)
Player.update(Surface)
Finally, you do not need to display your character immediately after the creation, so you can leave the init function as
def __init__(self,rect):
self.rect = pg.Rect(rect)
self.click = False
self.image = pg.image.load("ComboBox.png").convert()
I hope it helps.

Code beyond the Onclick and Listen doesn't get executed

this is my first time posting here.
I'm trying to make a Title Screen before getting the user in my game. When I hit enter, I want to break out of the While loop into a the main game loop, or go into the next section of the While loop. However, when I hit enter and the onkey use my Enter function which sets the In_title to False, it doesn't seem to respond with anything. (this is in python with turtle, also I'm working using replit.)
I've created a variable named in_title = True.
I've tried breaking out of the while loop using if in_title == false then break.
import turtle
import random
import math
import time
#set up for the screen title.
screen_size = [500,500]
screen_color = "#1a2645"
t = turtle.Turtle()
screen = t.getscreen()
screen.setup(screen_size[0],screen_size[1])
screen.bgcolor(screen_color)
screen.tracer(0)
t.ht()
game_state = 1 #to see if the game is still runninng. #0, 1 are no, yes.
in_title = True #to see if the game is in title or not.
select = turtle.Turtle()
select.color("yellow")
select.speed(0)
select.up()
select.setx(-80) #putting it in the middle of the text.
select.sety(30)
#all of the entity here:
player = {"p": turtle.Turtle(), "lives": 3, "color": "white", "rad": 15}
player["p"].ht()
harm = {"color": "red", "rad": 10}
num_harm = 10
good = {"g": turtle.Turtle(), "color": "white", "rad": 8}
good["g"].ht()
#universal movement function.
def moving_harm(h,rad): #function to move the harm objects.
if h.ycor() == (250 + rad/2):
h.up()
h.setx(random.randrange(-250,250))
h.sety(h.ycor() - 5)
screen.update()
if (h.ycor() != 250 + rad/2) and (h.ycor() != -250 - rad/2):
h.sety(h.ycor() - 5)
if (h.ycor() == (-250 - rad/2)):
h.sety(250 + rad/2)
screen.update()
def up():
if game_state == 1:
select.sety(30) #hard coded the number so it's in the middle.
print(select.ycor())
screen.update()
# def down():
# def left():
# def right():
def enter():
global in_title
if (game_state == 1):
if(select.ycor() == 30):
select.down()
select.forward(150)
select.setheading(180)
select.color("white")
select.forward(150)
screen.update()
time.sleep(0.2)
screen.clear()
screen.update()
time.sleep(0.1)
screen.setup(screen_size[0],screen_size[1])
screen.bgcolor(screen_color)
in_title = False
#main menu operation:
def main_game():
global game_state
#writing out the tittle screen.
menu = turtle.Turtle()
menu.up()
menu.sety(150)
menu.color("white")
menu.write("Sorta a Side Scroller Game", align="center", font=("Arial", 22, "normal"))
menu.sety(100)
menu.write("By Silver", align="center", font=("Arial", 15, "normal"))
menu.ht()
menu.sety(25)
menu.write("Start game", align="center", font=("Arial", 15, "normal"))
#create all the harm turtles:
for i in range(num_harm):
h = "h"+str(i+1)
harm[str(h)] = turtle.Turtle()
harm[str(h)].up()
harm[str(h)].ht()
#handle control at the title screen.
screen.onkey(enter, "Enter")
if in_title == False:
print("got passed that")
main_game()
screen.listen()
screen.update()
screen.mainloop()
no error message where given.
The way you have it, main_game() only is called once, right before screen's main loop begins. That means that this fragment
if in_title == False:
print("got passed that")
happens once and then control flow leaves main_game() forever, and the screen main loop begins.
Since the fragment only exists in main_game(), and main_game() isn't set to be called on any button press or called from anywhere else in the code, that fragment will never be called ever again. So, the if in_title == False: will never be encountered and print("got passed that") will never happen, even when in_title is False.
In this case, there is a place you already know is going to have control flow when in_title == False, and that is at the end of enter. So, you could put the code you have in mind there. This is the solution I suggested in the comments:
def enter():
global in_title
if (game_state == 1):
if(select.ycor() == 30):
# ...
in_title = False
print("got passed that")
#main menu operation:
def main_game():
# ...
screen.onkey(enter, "Enter")

Tkinter getting key pressed event from a function

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

Categories

Resources