2

I feel like I'm making a dumb error, but I can't seem to figure it out. Here is the code:

class menu:
    hover = False
    def __init__(self, text, pos):
        self.text = text
        self.pos = pos
        self.set_rect()
        self.draw()

    def draw(self):
        self.set_render()
        screen.blit(self.render, self.rect)

    def set_render(self):
        self.render = subFont.render(self.text, True, self.get_color())

    def get_color(self):
        if self.hover:
            return (BLACK)
        else:
            return (GREEN)

    def set_rect(self):
        self.set_render()
        self.rect = self.render.get_rect()
        self.rect.topleft = self.pos

select = [menu("Computer Virus", (100, 200)),
          menu("Computer Crime", (100, 300)),
          menu("QUIT", (100, 400))]
running = True
while running:
    for evnt in event.get():  
        if evnt.type == QUIT:
            running = False
    screen.fill(WHITE)    
    title()
    for menu in select:
        if menu.rect.collidepoint(mouse.get_pos()):
            menu.hover = True               
        else:
            menu.hover = False
        menu.draw()
    pointer()
    display.update()

but the program would just crash. How can I make it so that I can quit the screen when clicked on quit?

skrx
  • 19,980
  • 5
  • 34
  • 48
ChewyCarrot
  • 222
  • 2
  • 12
  • I added the full source code up above, I didn't want to spam the question with code. [link](https://github.com/ChewyCarrot/menu) (HomePage>"Untitled-1.py") – ChewyCarrot May 05 '18 at 03:38
  • Please don't post links to your projects, because they could disappear or be changed in the future. Add the relevant code to your question instead. – skrx May 05 '18 at 03:56
  • 1
    I've already prepared an example for you, but there are some differences to your program (it shouldn't be a problem to adjust it to your liking). BTW, I called the `menu` class `Button` because it's really a button not a menu. Also, take a look at the [PEP 8 naming conventions](https://www.python.org/dev/peps/pep-0008/#prescriptive-naming-conventions). – skrx May 05 '18 at 03:57

1 Answers1

2

To quit your game, you can define a function or method in which you set the running variable to False and then pass it to one of the button instances as the callback function.

Here's a more object-oriented example with an App class that has a self.running attribute and a quit_game method. If the user clicks on the button, the quit_game method gets called, self.running is set to False and the main while loop will stop.

import pygame


pygame.init()
# Global constants.
WHITE = pygame.Color('white')
GREEN = pygame.Color('green')
BLACK = pygame.Color('black')
FONT = pygame.font.Font(None, 40)


class Button:

    def __init__(self, text, pos, callback):
        self.text = text
        self.callback = callback
        self.image = FONT.render(self.text, True, GREEN)
        self.rect = self.image.get_rect(topleft=pos)

    def draw(self, screen):
        screen.blit(self.image, self.rect)

    def handle_event(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                if self.rect.collidepoint(event.pos):
                    self.callback()
        elif event.type == pygame.MOUSEMOTION:
            if self.rect.collidepoint(event.pos):
                self.image = FONT.render(self.text, True, BLACK)
            else:
                self.image = FONT.render(self.text, True, GREEN)


class App:

    def __init__(self):
        self.screen = pygame.display.set_mode((640, 480))
        self.clock = pygame.time.Clock()
        self.select = [
            Button("Computer Virus", (100, 200), lambda: print("Computer Virus")),
            Button("Computer Crime", (100, 300), lambda: print("Computer Crime")),
            Button("QUIT", (100, 400), self.quit_game),
            ]

        self.running = True

    def quit_game(self):
        self.running = False

    def main_loop(self):
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False
                for button in self.select:
                    button.handle_event(event)

            self.screen.fill(WHITE)

            for button in self.select:
                button.draw(self.screen)

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


app = App()
app.main_loop()
pygame.quit()

If you don't want to use the App class, you can also write a function which changes the global running variable. However, note that global variables are usually frowned upon because they make code harder to read and more error prone (in this case it's not that dramatic).

def quit_game():
    global running
    running = False

# Pass it as the `callback` argument.
Button("QUIT", (100, 400), quit_game)
skrx
  • 19,980
  • 5
  • 34
  • 48
  • The main loop is inside of the class, so I'm assuming that I would have to add all the other events in that loop (which is in the class)? – ChewyCarrot May 05 '18 at 04:03
  • Could you elaborate the question? I'm not sure what you mean. I use the `for button in self.select:` loop in the event loop to pass all events to the `handle_event` methods of the button instances. – skrx May 05 '18 at 04:12
  • BTW, here's a similar [answer (first addendum)](https://stackoverflow.com/a/47664205/6220679) with a slightly more complex `Button` that demonstrates how to use pygame sprites and sprite groups to create buttons. – skrx May 05 '18 at 04:22