-1

I'm quite new to Python/pygame and we've been developing a game in class.

Currently, I'm working on a basic menu screen, and I've tried to implement a music button that pauses/plays the music when pressed. The problem is that I can only click on one button, e.g. if I pause the music I can't click on the instructions or quit game buttons.

import pygame,random,time
pygame.init()

width,height = 1024,768
screen = pygame.display.set_mode((width,height))

instructionsBackground = pygame.image.load("instructions.png")
instructionsBackground = pygame.transform.scale(instructionsBackground,(width,height))
musicImage= pygame.image.load("musicIcon.png")

pygame.mixer.music.load("menuMusic.ogg")
pygame.mixer.music.play(-1)

white = [255,255,255]
black = [0,0,0]

clock = pygame.time.Clock()

def button(msg,x,y,w,h,c,action=None):
    global screen
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    if x+w > mouse[0] > x and y+h > mouse[1] > y:
        pygame.draw.rect(screen, c, (x,y,w,h))
        if click[0] == 1 and action != None:
            action()

    smallText = pygame.font.SysFont("comicsansms", 20)
    textSurf, textRect = textObjects(msg, smallText)
    textRect.center = ((x + (w/2)), (y + (h/2)))
    screen.blit(textSurf, textRect)

def textObjects(text, font):
    textSurface = font.render(text, True, black)
    return textSurface, textSurface.get_rect()    

def quitGame():
    pygame.quit()
    raise SystemExit
    quit()

def menuLoop():
    menuShowing = True
    while menuShowing == True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quitGame()
            screen.fill(white)
            button("Play",        400,175,150,50, white,None)
            button("Instructions",400,225,150,50, white,instructions) 
            button("HighScores",  400,275,150,50, white,None)
            button("Quit",        400,325,150,50, white,quitGame)
            button("Music",       400,375,150,50, white,music)
            pygame.display.update()
            clock.tick(60)
        
def instructions():
    instructionsShowing = True
    while instructionsShowing == True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quitGame()    
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if (event.pos[0] > 400 and event.pos[0] < 550) and (event.pos[1]  > 225 and event.pos[1] < 275):        
                    screen.blit(instructionsBackground,(0,0))
                    button("Go Back",25,600,150,60, white,menuLoop)
                    pygame.display.update()
                    clock.tick(60)


def music():
    music_playing = True
    running = True
    while running == True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quitGame()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if (event.pos[0] > 400 and event.pos[0] < 550) and (event.pos[1]  > 375 and event.pos[1] < 425):  
                    if music_playing == True: 
                        pygame.mixer.music.pause()
                        music_playing = False
                    else:
                        pygame.mixer.music.unpause()
                        music_playing = True
                
menuLoop()
2
  • I'm not that familiar with pygame but it seems wrong to me that you have a for event in pygame.event.get(): loop in multiple places. Presumably that removes the events from the queue, so you would only see a given event once inside of one of those functions. And if the event doesn't match your expected criteria for processing, it will be lost before the other functions get a chance to check it. Commented Mar 18, 2021 at 20:05
  • ah ok that makes sense, how would i work around that ? kinda new to this so a lil bamboozled lol Commented Mar 18, 2021 at 21:54

1 Answer 1

1

When you click the button, the program gets stuck in an infinite loop, and the GUI freezes. You have to run the long-running function in a new thread, or even process.

Using multiprocessing, you can do something like that:

import time
from multiprocessing import Process

def main():
    # Main Loop.
    # Do GUI stuff and so on.

    # To call the blocking function:
    p = Process(target=my_blocking_function, args=("Param 1",))
    p.start()

    # It'll create a new process. You can get the PID with:
    print(p.pid) # something like 16543
    
    # Now you do other stuff, let's just wait a little bit
    time.sleep(3)
    print("The main is untouched!")

    # When you're done, you can kill the subprocess:
    p.terminate()

    # The main process is still running!
    print("Hey, I'm alive!")

def my_blocking_function(argument:str):
    while True:
        time.sleep(1)
        print(f"Hey, I wanna block everything!")

if __name__ == '__main__':
    main()

Read the documentation to know the details. In general, be careful to pass a Tuple to args (so the trailing comma is needed when you need only one argument).

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

2 Comments

Ah ok, we havent really covered that , so how would one go about implementing something like that ? Sorry for the big ask, just trying to wrap my head around it atm
I edited the answer to include an example. Feel free to ask more.

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.