pythonbuttonpygamesdl2d-games

hovering only works on first button


In my code:

class text:
    def __init__(self, size, message, color, position, button = False, action = None):
        self.size = size
        self.message = message
        self.color = color
        self.position = position
        self.text_size = pygame.font.SysFont(None, int(size*displaywidth))
        self.Textsurface = self.text_size.render(self.message, True, self.color)
        Textrect = self.Textsurface.get_rect()
        self.Textrect = Textrect
        self.Textwidth = Textrect[2]
        self.Textheight = Textrect[3]
        self.second_x_pos = Textrect[2] + position[0]
        self.second_y_pos = Textrect[3] + position[1]
        self.button = button
        self.action = action



    def display(self):
        self.Textrect.topleft = (self.position)
        gameWindow.blit(self.Textsurface, self.Textrect)




        if self.button == True:
            self.Textrect.topleft = (self.position)
            gameWindow.blit(self.Textsurface, self.Textrect)
            for event in pygame.event.get():
                if event.type == pygame.MOUSEMOTION: 
                    if   self.position[0] < event.pos[0] < self.second_x_pos and self.position[1] < event.pos[1] < self.second_y_pos:
                            print("yee")
                            self.color = white
                            self.Textsurface = self.text_size.render(self.message, True, self.color)
                            gameWindow.blit(self.Textsurface, self.Textrect)
                    else:

                            self.Textsurface = self.text_size.render(self.message, True, self.color)


                    if event.type == pygame.MOUSEBUTTONUP :

                        self.action()  

# menu screen
    def menu_screen():
        global wine 
        global purple 
        menu = True
        global displaywidth
        global displayheight
        global gameWindow
        global compltely_red 
        global brown 
        global red 




    # Texts

    menu_txt = text(0.2,"Timm", red, (displaywidth/2,displayheight/9))
    Play_txt = text(0.04, "Play ", wine, (displaywidth/7, displayheight/1.5), True, game_loop)
    parallel_button = displaywidth - (displaywidth/7) - Play_txt.Textwidth
    Quit_txt = text(0.04, "Quit ", compltely_red, (parallel_button, displayheight/1.5), True, quit_Everything)




    #loop

    while menu == True:


        #the loop
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit_Everything()


            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    quit_Everything()
                if event.key == pygame.K_f:
                        displaywidth = 1920
                        displayheight = 1080
                        gameWindow = pygame.display.set_mode((displaywidth,displayheight), pygame.FULLSCREEN)
                if event.key == pygame.K_g:
                        displaywidth = 960
                        displayheight = 960
                        gameWindow = pygame.display.set_mode((displaywidth,displayheight))
        gameWindow.fill(green)

        menu_txt.display()
        Play_txt.display()
        Quit_txt.display()
        pygame.display.update()

The first button that I call (The "Play" button) works perfectly (I am not yet concerned about the actions, what matters to me at the moment is the hovering). But the second button is very glitchy (when I inverted the order of how I called the buttons, the first one being called always works perfectly)


Solution

  • First thing there: there is no pause between the "frames" - i.e. the checking of events in your program - so everything is done as fast as the computer can carry it, using 100% of CPU - worse choice. The correct thing is to wait some tens of milliseconds after the call to pygame.display.update - place a call like pygame.time.delay(30) immediately after that one.

    The code you get there, though having a bad design, almost works, but for one thing - when you call pygame.event.get it fetches all events on the queue. So, there is some race condition n which the call to get on your mainloop pick all events - but due to the no-pausing nature of your code, some MOUSEMOTION events are picked by the second call, in the display method for the first button.

    When you place the pause I've advised above, even the hovering for the first button should stop - as it is works just by chance how it is, and the pause between frames will fix that chance.

    Now, you have two options - if you want to keep checking events in the code in the objects, they should not call pygame.events.get themselves - instead, make a single call to this on each frame, and assign the result to a variable - them pass that variable as parameter to all places events should be handled:

    On the menu screen code do:

    while menu:  #
        #the loop
        events = pygame.event.get()
        for event in events:
             ...
        gameWindow.fill(green)
        menu_txt.display(events)
        Play_txt.display(events)
        Quit_txt.display(events)
    

    And on the text class:

    def display(self, events):
        ...
        if self.button:  # Please, just stop checking truthiness doing "x == True" 
            self.Textrect.topleft = (self.position)
            gameWindow.blit(self.Textsurface, self.Textrect)
            for event in events:
                if event.type == pygame.MOUSEMOTION: 
    

    That should fix your problem and a lot of things on your design. However, note that these will only highlight your text if the mouse actually moves - if it is hovering, but without moving, no MOUSEMOTION events will be generated.

    So you might check if the mouse position itself is inside the button rect, instead of checking for events - you get the mouse position directly, instead of depending on an event. Also, while at it, you might prefer to use Pygame's rectangle calculation functions instead of the unpleasant to the eyes self.position[0] < event.pos[0] < self.second_x_pos and self.position[1] < event.pos[1] < self.second_y_pos expression.
    On __init__:

    self.rect = pygame.Rect(self.pos + tuple(Textrect[2:])
    

    And on the if to check if the pointer is inside the rect:

    def display(self, events):
       if self.button:
          if self.rect.contains(pygame.mouse.get_pos() + (1,1)):
               print("yee")
               ...