My program does only two things :
1) It's printing word 'hello' every time i hover mouse on one of two images (anyone,in any order)
2) It's closing , when i close window by pressing ESC , or by mouse
import pygame,sys ,os,random,math
from pygame.locals import *
pygame.init()
white=(255,255,255)
black=(0,0,0)
WINDOWWIDTH = 1680
WINDOWHEIGHT =1050
mousex, mousey=0,0
screen=pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
ticket=pygame.image.load(os.path.join('imag','ticket.png'))
ticket2=pygame.image.load(os.path.join('imag','ticket2.png'))
def terminate():
pygame.quit()
sys.exit()
def checkForQuit():
for event in pygame.event.get(QUIT): # get all the QUIT events
terminate() # terminate if any QUIT events are present
for event in pygame.event.get(KEYUP): # get all the KEYUP events
if event.key == K_ESCAPE:
terminate() # terminate if the KEYUP event was for the ESc key
pygame.event.post(event)
tic1=ticket.get_rect(topleft=(50,770))
tic2=ticket2.get_rect(topleft=(220,770))
List_of_tickets=[tic1,tic2]
while 1:
screen.fill(white)
checkForQuit()
screen.blit(ticket,(50,770))
screen.blit(ticket2,(220,770))
for tic in List_of_tickets:
if tic.collidepoint(pygame.mouse.get_pos()):
print 'Hello !'
pygame.display.update()
The problem is that second part is not working !
So if i hover mouse on one ticket - everything fine . I got my 'Hello' word and i can close window if i need to .
But if i hover mouse on one ticket , and THEN on second ticket (so i got many more of my 'Hello' - and that's good) - i cannot quit program nor with ESC , nor with mouse.
It seems like quitting event ceased to be handled.
The questions are :
1) What i do not understand in handling of events in Pygame ?
2) What should i do to make my code quittin in any case ?
From the docs:
"If you are only taking specific events from the queue, be aware that the queue could eventually fill up with the events you are not interested."
Toss pygame.event.clear() in there and see what happens.
(If so, the reason it only fails if you go to both tickets is that all mouse movements add an event; moving to one ticket doesn't completely fill the queue, but moving to both does. Slightly wiggling the mouse would have the same effect.)
Related
I've been trying to develop a "text box" class for pygame as a little personal project, and I've come across an issue that's really stumped me. I'm trying to expand on the pygame text input class found here, wrapping it in a text box class that supports multiple lines and, hopefully, scrolling capabilities.
My problem comes when trying to move the blinker up and down between the lines of text. Basically, hitting the "up" arrow once moves the blinker all the way to the top, and then it stops being responsive to move it down.
Here's the code for how I give the pygame_textbox class the events:
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
test_box.update(events)
test_box_2.update(events)
test_box.draw(screen, (100, 100))
test_box_2.draw(screen, (200, 250))
pygame.display.update()
Here's the code for the text box class (event comes from the above code):
if event.type == pl.KEYDOWN:
if self.is_active:
print("reading in text box: {}".format(event))
if event.key == pl.K_UP and self.cursor_line > 0:
self.cursor_line -= 1
print("cursor going UP to {}".format(self.cursor_line))
if event.key == pl.K_DOWN and self.cursor_line < len(self.text_input)-1:
self.cursor_line += 1
print("cursor going DOWN to {}".format(self.cursor_line))
if event.key == pl.K_RETURN:
self.text_input.insert(self.cursor_line+1, pygame_textinput.TextInput())
self.cursor_line += 1
for line in self.text_input:
print(line.input_string)
Trying to debug it seems to show that the pygame.event.get() queue is taking in way more KEYDOWN events than it's supposed to; one press of the button sends multiple (and sometimes ongoing) events. I'm new to pygame, but I'm pretty sure that's not supposed to happen with KEYDOWN events, right? There's only supposed to be one event triggered every time a key is pressed. What am I doing wrong here? Is this a bug with pygame itself?
Thanks for any help you can provide. I'm fairly new at this and I hope I formatted the question right.
Just take a look at the pygame_textinput module you linked in your question:
# Update key counters:
for key in self.keyrepeat_counters:
self.keyrepeat_counters[key][0] += self.clock.get_time() # Update clock
# Generate new key events if enough time has passed:
if self.keyrepeat_counters[key][0] >= self.keyrepeat_intial_interval_ms:
self.keyrepeat_counters[key][0] = (
self.keyrepeat_intial_interval_ms
- self.keyrepeat_interval_ms
)
event_key, event_unicode = key, self.keyrepeat_counters[key][1]
pygame.event.post(pygame.event.Event(pl.KEYDOWN, key=event_key, unicode=event_unicode))
As you can see, it's the update method of the TextInput that repeats the key events by posting them again to pygame's event queue.
I have a pygame window embedded in a a frame in tkinter. In another frame I have a button which calls the following function when clicked:
def setStart():
global start
# set start position
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP:
start = event.pos
print("start",start)
break
I am intending for the program to print the position of the place where the user clicked on the pygame surface after the button is clicked. However on the first click of the button and the following click of the pygame surface there is no output. It is on the second click of the button before the corresponding second click on the pygame surface that python prints out an output like :
('start', (166, 115))
How can I get it to give me a result right after the click on the pygame surface? I had the same problem when I had two seperate tkinter and pygame windows so the embedding of pygame into tkinter is unlikely to be the cause of the problem.
EDIT: after further testing it appears that if the button is pressed and then the pygame surface is clicked on multiple times, upon a second click of the button the coordinates of all of these clicks are printed out as a batch.
In the most basic form here's how you do it in pygame:
import pygame
pygame.init()
screen = pygame.display.set_mode((100, 100))
clock = pygame.time.Clock()
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP: # or MOUSEBUTTONDOWN depending on what you want.
print(event.pos)
elif event.type == pygame.QUIT:
quit()
pygame.display.update()
More information on how to handle pygame events can be found in the docs.
And here's how you do it in tkinter:
try:
import tkinter as tk # Python 3
except ImportError:
import Tkinter as tk # Python 2
root = tk.Tk()
def print_pos(event):
print(event.x, event.y)
root.bind("<Button-1>", print_pos)
root.mainloop()
More information on tkinter events can be found in effbots documentation.
I would suggest not putting break in an event loop in pygame. Doing so makes all other events go un-handled, meaning that it's possible for the program to not respond to certain events.
Your "EDIT: [...]" is unfortunately wrong, given the code you've given us. I had no problem with the code; it printed the position of where I released the mouse button and always printed just one position. So there have to be a logical error somewhere else in your code.
First I have to say that I don't know anything about pygame. However, if you are already using Tkinter, I could help you maybe: I would define a new function (let's call it mouse_click). In the button-click-function I would bind the new function to the game's surface. In the new function I print out the current mouse position:
def button_click(self):
game_surface.bind("<Button-1>", self.mouse_click)
def mouse_click(self, event):
print "X:", event.x
print "Y:", event.y
I hope this is helpful. Please notice that you should modify this code to make it work in your program (using the correct widget names and so on).
By the way, "Button-1" is the event identifier of the left mouse button.
I am trying to implement a game using mouse position to see if the user clicks a button. Somehow the mouse position does not update for a couple seconds, and changes to a new position for another couple seconds, and repeat. I moved and pressed the mouse at different location in the screen, but the mouse position did not change at all. (Working on python3.5.1 and pygame 1.9.2, using IDE PyCharm)Any idea?
Here is my code:
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if click[0]==1:
print(mouse)
pygame.display.update()
The call
mouse = pygame.mouse.get_pos()
doesn't update the position unless the event MouseMotion is executed.
If you are executing the program in a window on a MAC, the mouse must be pressed, held, and moved (if you were to press, hold, then move the mouse, pygame.mouse.get_pos() would return the current mouse position).
There is two ways of handling input events in pygame :
State Checking
Event Handling
For better understanding of how it works :
http://www.pygame.org/docs/tut/newbieguide.html#managing-the-event-subsystem
You use both in your code, state checking :
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
Event handling :
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
If you still want to use state checking to get the mouse position you can add:
clock=pygame.time.Clock()
clock.tick(60) # 60 frames per second
So the update of the position should be better.
If the button has a rect then you can use the rect.collidepoint() method with event checking like this:
mouse_pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0] and button.rect.collidepoint(mouse_pos):
This drove me crazy for hours. I had a similar problem.
Using pygame in:
Mac OSX 10.13 (High Sierra)
pygame 1.9.3
python 3.6
in a virtualenv
In this setup (specifically in the virtualenv), window focus is not properly tracked. Click and dragging will generate a MOUSEMOTION event, but simply moving the mouse around will not. If the MOUSEMOTION event is not generated, calling:
pos = pygame.mouse.get_pos()
will continue report the same value until another MOUSEMOTION event occurs.
Installing pygame outside of the virtualenv, all works as expected. Not really the answer I was hoping for, but at least it explains the behavior I was seeing.
Your
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
only get the states at the time of the call. As per the documentation http://www.pygame.org/docs/ref/mouse.html:
to get all of the mouse events it is better to use either
pygame.event.wait() or pygame.event.get() and check all of those events
That is you are missing the clicks because your program isn't storing them to process. You only see it when you get lucky and the program calls the function when the button is actually down.
This shows a basic pygame mouse getting program. Simply click anywhere in the window and the mouse coordinate are printed:
import pygame as py
py.init()
white = (255,255,255)
window = (400,400)
screen = py.display.set_mode(window)
clock = py.time.Clock()
done = False
while not done:
for event in py.event.get():
if event.type == py.QUIT:
done = True
elif event.type == py.MOUSEBUTTONDOWN:
print py.mouse.get_pos()
screen.fill(white)
py.display.flip()
clock.tick(30)
py.quit()
hope this helps :)
Here's my check_for_pause() function:
#Check if the user is trying to pause the game
def check_for_pause():
keys=pygame.key.get_pressed() #Get status of all keys
if keys[K_SPACE]: #The space bar is held down
global paused #Make global so it can be edited
if paused==True: #It was paused, so unpause it
paused=False
elif paused==False: #It was playing, so pause it
paused=True
#Don't let the main loop continue until the space bar has been released again, otherwise the variable will flicker between True and False where the loop runs so fast!
space_bar_pressed=keys[K_SPACE]
while space_bar_pressed: #Repeat this loop until space_bar_pressed is False
keys=pygame.key.get_pressed()
if not keys[K_SPACE]: #Space bar has been released so set space_bar_pressed to False
space_bar_pressed=False
however this keeps making my program become unresponsive whenever I try to pause it! Basically, I want the variable "paused" to be either True or False. When the space bar is pressed, it should change to whichever one it isn't currently. Because I'm using check_for_pause() in another never-ending loop, I need to make it so that the function only stops executing when the space bar is released, otherwise if the user holds the space bar for more than a split second it will keep switching between True and False.
Any ideas why my program becomes unresponsive when I run this? I know it's to do with the bit that waits until the space bar's been released because when I remove that bit of the code then my program runs fine (but obviously the pause feature then doesn't work).
You have a main loop that probably looks something like this...
while True:
check_for_pause()
# Update the game
# Draw the game
When you check for pause, and the space key is pressed down, paused gets set to True. Then, you have this loop...
space_bar_pressed=keys[K_SPACE]
while space_bar_pressed: #Repeat this loop until space_bar_pressed is False
keys=pygame.key.get_pressed()
if not keys[K_SPACE]: #Space bar has been released so set space_bar_pressed to False
space_bar_pressed=False
The problem with this loop is that you assume that pygame.key.get_pressed() will continue to return up-to-date information. However, looking at the pygame source code, it appears that it uses SDL_GetKeyState, which says as part of its documentation..
Note: Use SDL_PumpEvents to update the state array.
In other words, repeatedly calling pygame.key.get_pressed() will NOT give you the updated key state if you are not additionally calling something like pygame.event.pump(), which actually updates pygame with the new key states. So, you could quickly fix this by introducing that pump function into the loop, as currently it is just running forever.
That being said: DON'T DO THIS. If you do it this way, your game will not be able to do ANYTHING when paused: this includes showing a "paused" screen, continuing to play the music in the background, etc. What you want to do instead is keep track of if the game is paused, and if so, change how the game is updating.
In the part of your game where you update things, some items should only happen if the game is paused. Something like...
paused = False
while True:
# This function should just return True or False, not have a loop inside of it.
paused = check_for_paused()
if not paused:
# This function moves your enemies, do physics, etc.
update_game()
draw_game()
In this case, the main loop will still happen, the game will continue to be drawn, and the input will continue to be handled. However, the enemies and players will not be moving, therefore the game can be said to be "paused".
Finally, there is also the fact that you're relying on get_key_pressed(), which you probably don't want to do. See this other similar answer I've given for reasons why you should be instead using the event queue.
You should never stop running your game loop in a game even if it paused. Also pauses are generally handled through events. For example look at this code:
import pygame, sys
from pygame.locals import *
pygame.init()
pygame.display.set_mode((400,400))
paused = False # global
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_SPACE:
paused = not paused
if paused:
continue # skip this iteration if paused
# add your game code here
print 'game code running'
In the above code, I toggle paused every time I press spacebar. If you want to pause only while holding spacebar do this:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type in (KEYDOWN, KEYUP): # checks membership in tuple
if event.key == K_SPACE:
paused = not paused
if paused:
continue # skip this iteration if paused
# add your game code here
print 'game code running'
As a general note, you should always be handling events from your event queue or Pygame will just cry and whine and do nothing (be unresponsive). Thus, never stop spinning on your game loop even if you have paused the game.
EDIT: Alternatively, as abarnert pointed out in the comments you can do a trick with equality comparisons to assure that you never get conflicts between KEYDOWN and KEYUP events:
paused = event.type == KEYDOWN
This way you won't have "syncing problems" where the code accidentally sets paused to True whenever you actually release the spacebar. This can happen if 2 KEYDOWN events happen in a row or if 2 KEYUP events happen in a row (instead of a smooth alternating sequence like KEYDOWN, KEYUP, KEYDOWN, KEYUP, etc.). It's best to not assume that all event queues feed events that are 100 percent accurate.
This question already has answers here:
Pygame window not responding after a few seconds
(3 answers)
Pygame unresponsive display
(1 answer)
Closed 1 year ago.
Basically I have a loop (tick, set_caption, screen_fill, event.get(), send_frame_event, flip, repeat)
When I drag the window around on windows 7, the loop stops looping, I ended up stuck in pygame.event.get(), I have tried to define certain events only for get e.g. get([pygame.QUIT]) to no avail.
Simply calling pygame.event.clear() has the same freeze effect when dragging/moving the window.
Is there a workaround?
Not full code, but should be enough:
def start(self):
self.running = True
Clock = pygame.time.Clock()
while self.running:
self.p += 25
tickFPS = Clock.tick(self.fps)
pygame.display.set_caption("Press Esc to quit. FPS: %.2f" % (Clock.get_fps()))
self.screen.fill([self.p&0xFF,(255-self.p)&0xFF,255])
self.handleEvents()
self.raiseEvent("updateFrame")
pygame.display.flip()
def handleEvents(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.running = False
full code at: http://pastie.org/private/wm5vqq3f7xe0xlffy1fq
Try placing a call to pygame.event.pump() inside your mainloop (or handleEvents function)
No idea if this will help or not:
I realize the problem is with moving the window window, not with re-sizing the window.
Perhaps there is some similarity between moving and re-sizing?
I found this in the documentation on re-sizing:
Then the display mode is set, several events are placed on the pygame event queue. pygame.QUIT is sent when the user has requested the program to shutdown. The window will receive pygame.ACTIVEEVENT events as the display gains and loses input focus. If the display is set with the pygame.RESIZABLE flag, pygame.VIDEORESIZE events will be sent when the user adjusts the window dimensions. Hardware displays that draw direct to the screen will get pygame.VIDEOEXPOSE events when portions of the window must be redrawn.
and:
Note that when the user resizes the game window, pygame does not automatically update its internal screen surface. You must call set_mode() every time VIDEORESIZE is sent. This really should be more clear in the documentation.
Perhaps when moving the game window, something similar happens?