2

The code below is supposed to create a green button that makes a score text appear. unfortunately the button does nothing, and the only way I've managed to get it to work is by putting the function call for makeText in the while loop instead of in the clickButton function, but if I do that it's no longer dynamic. Can someone explain why the text isn't showing up when I press the button and fix my code so it does show up?

import pygame
import sys
#game stuff
pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()

#functions
def makeText(title,text,posx,posy):
    font=pygame.font.Font(None,30)
    scoretext=font.render(str(title)+ ": " +str(text), 1,(0,0,0))
    screen.blit(scoretext, (posx, posy))
def clickButton(name,x,y,width,height):
    if x + width > cur[0] > x and y + height > cur[1] > y:
        if click == (1,0,0):
            makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))

while True:
    screen.fill((255,255,255))
    screen.fill((55,155,0), button1)
#update display
    pygame.display.update()
    clock.tick(60)
#event handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            cur = event.pos
            click = pygame.mouse.get_pressed()
            clickButton("button1",button1.left,button1.top,button1.width,button1.height)

2 Answers 2

1

The problem is that once you created the text, your main loop keeps going and calls screen.fill, overdrawing the text even before pygame.display.update() is called.


You could change it to:

...
def clickButton(name,x,y,width,height):
    print x + width > cur[0] > x and y + height > cur[1] > y
    if x + width > cur[0] > x and y + height > cur[1] > y:
        if click == (1,0,0):
            makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))

while True:
    screen.fill((255,255,255))
    screen.fill((55,155,0), button1)

#event handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            cur = event.pos
            click = pygame.mouse.get_pressed()
            clickButton("button1",button1.left,button1.top,button1.width,button1.height)
...

so the text is created after filling the screen with the background color and before pygame.display.update() is called, but that does not solve the problem of the screen being filled again the next iteration of the while loop.


So the solution is to keep track of the fact that the button was pressed, a.k.a. keeping track of a state.

Here's an example of a different approach, using classes for the buttons and a dict for the global state (so you don't need global variables, which should you avoid most of the time, because it can get very confusing fast if your game starts becoming more complex).

Click the first button to show or hide the score, and click the second button to change the background color and earn 100 points.

See how easy it becomes to create new buttons; it's just adding a simple function.

import pygame
import sys
import random

pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()

# create font only once
font = pygame.font.Font(None,30)

# it's always a good idea to cache all text surfaces, since calling 'Font.render' is
# an expensive function. You'll start to notice once your game becomes more complex
# and uses more text. Also, use python naming conventions
text_cache = {}
def make_text(title, text):
    key = "{title}: {text}".format(title=title, text=text)
    if not key in text_cache:
        text = font.render(key, 1,(0,0,0))
        text_cache[key] = text
        return text
    else:
        return text_cache[key]

# we use the 'Sprite' class because that makes drawing easy
class Button(pygame.sprite.Sprite):
    def __init__(self, rect, color, on_click):
        pygame.sprite.Sprite.__init__(self)
        self.rect = rect
        self.image = pygame.Surface((rect.w, rect.h))
        self.image.fill(color)
        self.on_click = on_click

# this happens when the first button is pressed
def toggle_score_handler(state):
    state['show_score'] = not state['show_score']

# this happens when the second button is pressed
def toggle_backcolor_handler(state):
    state['backcolor'] = random.choice(pygame.color.THECOLORS.values())
    state['score'] += 100

# here we create the buttons and keep them in a 'Group'
buttons = pygame.sprite.Group(Button(pygame.Rect(30, 30, 32, 32), (55, 155 ,0), toggle_score_handler),
                              Button(pygame.Rect(250, 250, 32, 32), (155, 0, 55), toggle_backcolor_handler))

# here's our game state. In a real 
# game you probably have a custom class
state = {'show_score': False,
         'score': 0,
         'backcolor': pygame.color.Color('White')}

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        # you can check for the first mouse button with 'event.button == 1'
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            # to check if the mouse is inside the button, you 
            # can simple use the 'Rect.collidepoint' function
            for button in (b for b in buttons if b.rect.collidepoint(event.pos)):
                button.on_click(state)

    screen.fill(state['backcolor'])
    # draw all buttons by simple calling 'Group.draw'
    buttons.draw(screen)

    if state['show_score']:
        screen.blit(make_text("score", state['score']), (100, 30))

    pygame.display.update()
    clock.tick(60)

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

this is working perfectly for me, but now I'm trying to add a third button that changes the score from an integer to a string and I can't seem to get it to work without the two texts writing over each other and becoming a jumbled mess. If you have any suggestions I'd love to hear them, and thanks for all your help!
1

You are checking the value of "click" in the clickButton function, but I don't see click defined anywhere that clickButton would have access to it. Perhaps you should pass click as an argument in the clickButton function, which would then possibly make the if condition true?

2 Comments

I'm not sure why now that you mention it, but the if condition is running. typing "print click" after the if condition prints the tuple every time the button is pressed, so somehow the function is getting click as a variable.
click is defined in the second last line. It's a global variable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.