NoRt9001
NoRt9001

Reputation: 100

Pygame responds incorrectly to button clicks

I'm having an issue with pygame. I've set up a window that randomly places circles across the screen very quickly, just for testing purposes. There are also three buttons: play/pause (switches back and forth, stops circles from appearing) and an increase speed and decrease speed button. I'm not very experienced with python or pygame, but I've come up with this function to create a clickable button on the screen:

def makeButton(rect, color, hovercolor, text, textsize, textcolor):
    clicked = False
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN: 
            clicked = True

    mouse = pygame.mouse.get_pos()
    rect = pygame.Rect(rect)
    displaycolor = color
    if rect.collidepoint(mouse):
        displaycolor = hovercolor

    buttonSurface = pygame.draw.rect(gameDisplay, displaycolor, rect, 0)
    font = pygame.font.Font('freesansbold.ttf',textsize)
    TextSurf = font.render(text, True, textcolor)
    TextRect = TextSurf.get_rect()
    TextRect.center = rect.center
    gameDisplay.blit(TextSurf, TextRect)
    if clicked:
        return True        
    else:
        return False

This function can definitely be shortened and simplified, but it has worked for me, up until now. I took out a big chunk of code that I realized was useless (having a completely different block of code to render the button when hovered, instead of just changing the display color). Now, whenever I click any of the three previously-mentioned buttons, it seems to pick a random one and return True, messing up the rest of the program. For example, the play button will increase the speed one time, pressing decrease speed will pause, etc. Sometimes it does do what it is supposed to, but it seems to be random.

Some extra info, if it's useful:

-This function is called three times every tick. It's inside a loop, and if it returns true, its corresponding actions are supposed to be performed (pause or play the game, increase/decrease speed)

-The play/pause button is one button that toggles between green with an 'play' arrow, and red with a pause symbol. They are two separate buttons and functions, and only one of them is executed at a time.

-I have almost zero experience with classes, so they may be way better at handling this situation.

-The only explanation I can think of for this problem is that the returned booleans are getting mixed up between the different places this function is used. I'm pretty sure the problem is within this chunk of code, but ask me and I will post the places it is called too.

Upvotes: 0

Views: 781

Answers (1)

Lost Robot
Lost Robot

Reputation: 1321

"pygame.event.get()" takes one event at a time, and *clears it** from the list of events that need to be processed.

So, more specifically, pygame.event.get() returns each event only once.

Take a look at the following code:

clicked = False
for event in pygame.event.get():
    if event.type == pygame.MOUSEBUTTONDOWN: 
        clicked = True

After this is called, all of the events are removed. Here is an analysis of the code. Assume that there are currently two events that haven't been processed, the first being a key pressed down and the other being a mouse button that's been pressed down.

  1. The first event, event.KEYDOWN, is put into the variable "event".
  2. The program checks whether "event" (currently equal to event.KEYDOWN) is equal to event.MOUSEBUTTONDOWN. They are obviously not the same thing, so the next line is skipped.
  3. The second event, event.MOUSEBUTTONDOWN, is put into variable "event". This removes what was previously in the variable "event", removing the first event from existence.
  4. The program checks whether "event" (currently equal to event.MOUSEBUTTONDOWN) is equal to event.MOUSEBUTTONDOWN. It is, so it proceeds to the next line...
  5. "clicked" is set to True, and the for loop exits, because there are no event remaining.

You should now have a better understanding of how Pygame processes events.

There are also many problems with the function you gave (makeButton). You should find a python tutorial to learn the rest. I suggest a book called "Hello World", by Carter and Warren Sande. The book is kind of out of date (teaches Python 2.5), but its code still works with Python 2.7, and it is one of the few decent Python books I've been able to find.

I have included the code to do what you are trying to do. I don't use Rect objects, but if you want them you can change the code to include them. I also didn't include the text, because I am short on time. Instead of placing random circles, this prints text (to the shell) when buttons are clicked.

import pygame, sys
pygame.init()
screen = pygame.display.set_mode([640,480])
clock = pygame.time.Clock()

buttons = []
#buttons = [[rect, color, hovercolor, hovering, clicked, msg]]

def makeButton(rect, color, hovercolor, text):
    global buttons
    buttons.append([rect, color, hovercolor, False, False, text])

makeButton([0,0,50,50], [0,127,0], [0,255,0], "Clicked Green")
makeButton([50,0,50,50], [190,190,0], [255,255,0], "Clicked Yellow")
makeButton([100,0,50,50], [0,0,127], [0,0,255], "Clicked Blue")

while 1:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.MOUSEMOTION:
            mousepos = event.pos
            for a in range(len(buttons)):
                if mousepos[0] >= buttons[a][0][0] and mousepos[0] <= buttons[a][0][0]+buttons[a][0][2] and mousepos[1] >= buttons[a][0][1] and mousepos[1] <= buttons[a][0][1]+buttons[a][0][3]:
                    buttons[3] = True
                else:
                    buttons[3] = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            mousepos = event.pos
            for a in range(len(buttons)):
                if mousepos[0] >= buttons[a][0][0] and mousepos[0] <= buttons[a][0][0]+buttons[a][0][2] and mousepos[1] >= buttons[a][0][1] and mousepos[1] <= buttons[a][0][1]+buttons[a][0][3]:
                    buttons[4] = True
                else:
                    buttons[4] = False
    for a in range(len(buttons)):
        if buttons[3] == 0:
            pygame.draw.rect(screen, buttons[1], buttons[0])
        else:
            pygame.draw.rect(screen, buttons[2], buttons[0])
        if buttons[4] == 1:
            buttons[4] = 0
            print buttons[5]
    pygame.display.flip()

I haven't had the opportunity to test out the code I just typed (using school computer), but it should work. If there are any problems with the code, just leave a comment and I'll fix it.

Also leave a comment if you don't understand something. Don't give up, you can do it!

Upvotes: 1

Related Questions