Search code examples
pythonpygamepygame-menu

How do I use pygame-menu as a settings menu for a pygame program?


I am making a pygame program and decided to use the pygame-menu library for some easy gui for the settings menu. Every example I can find uses the menu as the whole program, making it hard to figure out how to add a menu to a pygame program.

Every time I run the following code, I get an error:

import pygame
import pygame_menu

pygame.init()

clock = pygame.time.Clock()
scrn_size = pygame.display.Info()
scrn = pygame.display.set_mode((scrn_size.current_w, scrn_size.current_h), pygame.FULLSCREEN)

rotation_menu = pygame_menu.Menu(
    'Rotation',
    1000, 800,
    onclose=pygame_menu.events.BACK
)

speed_values_discrete = {0: 'Stopped', 2: '', 5: '', 10: '', 20: '', 30: 'Fastest'}
rotate_speed_range = rotation_menu.add.range_slider('Speed', 5, list(speed_values_discrete.keys()),
    slider_text_value_enabled=False,
    value_format=lambda x: speed_values_discrete[x]
)


menu = pygame_menu.Menu(
    'Configuration',
    1000, 800,
    enabled=False,
    onclose=pygame_menu.events.CLOSE
)

menu.add.button('Rotation', rotation_menu)
menu.add.button('Colors')


def main():
    rotation = 0
    rpf = 5
    move = False
    x, y = 0, 0
    pos = (0, 0)
    img_size = 300
    img = pygame.transform.scale(pygame.image.load('img.png'), (img_size, img_size))

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT or (e.type == pygame.KEYDOWN and e.key == pygame.K_q):
                pygame.quit()
                exit(1)
            elif e.type == pygame.MOUSEBUTTONDOWN:
                menu.enable()
            elif e.type == pygame.MOUSEMOTION and move:
                pos = e.pos
                x = pos[0] - (img_size / 2)
                y = pos[1] - (img_size / 2)

        scrn.fill((0, 0, 0))
        rotated = pygame.transform.rotate(img, rotation)
        rotation += rpf
        new_rect = rotated.get_rect(center=img.get_rect(topleft=(x, y)).center)
        scrn.blit(rotated, new_rect)

        if menu.is_enabled():
            menu.update(events)
            menu.draw(scrn)

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


if __name__ == '__main__':
    main()

Error:

Traceback (most recent call last):
  File "C:\Users\Zer0\Documents\Spinner\main.py", line 80, in <module>
    main()
  File "C:\Users\Zer0\Documents\Spinner\main.py", line 73, in main
    menu.draw(scrn)
  File "C:\Users\Zer0\AppData\Local\Programs\Python\Python311\Lib\site-packages\pygame_menu\menu.py", line 2053, in draw
    self._current._runtime_errors.throw(self._current._runtime_errors.draw, 'menu is not enabled')
  File "C:\Users\Zer0\AppData\Local\Programs\Python\Python311\Lib\site-packages\pygame_menu\menu.py", line 4090, in throw
    raise RuntimeError(msg)
RuntimeError: menu is not enabled

The program should spin an image. When you click the screen, a settings menu opens. You can go to sub-menus like rotation settings and change those. A problem I have is how do I change the rotation speed variable (rpf) with the RangeSlider in the rotation settings menu? (This Issue Is Not Solved)

Another problem I have is when I close out of the menus, I get the error above, stating that the menu is not enabled. This is confusing because the function has to pass through a check to see if it is enabled, which it did. Then it instantly has a problem because it is not enabled! This is frustrating. (This Issue Is Solved)


Solution

  • Calling menu.update() might disable the menu. This happens when you click on the cross at the top right of the menu. Therefore you have to check if the menu is enabled with menu.is_enabled() before drawing the menu with menu.draw(scrn):

    while True:
        events = pygame.event.get()
        for e in events:
            # [...]
    
            elif e.type == pygame.MOUSEBUTTONDOWN:
                menu.enable()
    
        # [...]
    
        if menu.is_enabled():
            menu.update(events)
    
        if menu.is_enabled():
            menu.draw(scrn)