How do i make a text box pop up when i hover over a button in pygame

Viewed 321

I want to be able to make a text box pop up when I hover over the button, now the button changes colour when I hover over it but no text box is displayed how can I make that happen or do I need to something entirely different like make a new screen pop up when I hover over either way the hovering over isnt working and I dont know how to fix it and id like some help please

import pygame
pygame.init()
pygame.font.init()
colours = {"White" : (255, 255, 255), "Black" : (0, 0, 0), "Red" : (255, 0, 0), "Blue" : (0, 0, 255), "Green" : (0, 255, 0)}
orighpamt = 10
currentGoblin = 0

class Screen():
    def __init__(self, title, width=400, height=600, fill=colours["White"]):
        self.title=title
        self.width=width
        self.height = height
        self.fill = fill
        self.current = False

    def makeCurrent(self):
        pygame.display.set_caption(self.title)
        self.current = True
        self.screen = pygame.display.set_mode((self.width, self.height))

    def endCurrent(self):
        self.current = False

    def checkUpdate(self):
        return self.current
    def screenUpdate(self):
        if(self.current):
            self.screen.fill(self.fill)

    def returnTitle(self):
        return self.screen

class Button():
        def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text):
            self.x = x
            self.y = y
            self.sx = sx
            self.sy = sy
            self.bcolour = bcolour
            self.fbcolour = fbcolour
            self.fcolour = fcolour
            self.fontsize = fontsize
            self.text = text
            self.current = False
            self.buttonf = pygame.font.SysFont(font, fontsize)
        def showButton(self, display):
            if(self.current):
                pygame.draw.rect(display, self.fbcolour, (self.x, self.y, self.sx, self.sy))
            else:
                pygame.draw.rect(display, self.bcolour, (self.x, self.y, self.sx, self.sy))

            textsurface = self.buttonf.render(self.text, False, self.fcolour)
            display.blit(textsurface, ((self.x + (self.sx/2) - (self.fontsize/2)*(len(self.text)/2) - 5,(self.y + (self.sy/2) -(self.fontsize/2) - 4))))
        def focusCheck(self, mousepos, mouseclick):
            if(mousepos[0] >= self.x and mousepos[0] <= self.x + self.sx and mousepos[1] >= self.y and mousepos[1] <= self.y + self.sy):
                self.current = True
                return mouseclick
            else:
                self.current = False
                return False
class Enemy(pygame.sprite.Sprite):

    def __init__(self, dx, dy, filename):
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.image.load(filename).convert()

        self.rect = self.image.get_rect()
        self.rect.x = dx
        self.rect.y = dy



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



menuScreen = Screen("Menu Screen")
screen2 = Screen("Screen 2")

win = menuScreen.makeCurrent()


done = False
font = pygame.font.Font('freesansbold.ttf', 32)
clickdamage = 1
hitpoints = orighpamt
DungeonButton = Button(125, 500, 150, 50, colours["Black"], colours["Blue"], "arial", 20, colours["White"], "Dungeon")
hitboxButton = Button(80, 50, 280, 400, colours["White"], colours["Red"], "arial", 20, colours["White"], "")
hitpointamount = Button(100, 0, 200, 50, colours["White"], colours["Black"], "arial", 20, colours["Black"], str(hitpoints))
shopButton = Button(125, 500, 150, 50, colours["Black"], colours["Red"], "arial", 20, colours["White"], "Shop")
Assassin_Text = Button(50, 350, 400, 50, colours["White"], colours["Red"], "arial", 18, colours["Black"], "Sharpen your sword and increase your damage per click")
Assassin_Upgrade = Button(10, 300, 125, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], "Assassin")
goblin = Enemy(0 , 20, "images\goblin-resized.png")
goblin2 = Enemy(80 , 50, "images\monster2.png")
toggle = False
while not done:
    menuScreen.screenUpdate()
    screen2.screenUpdate()
    mouse_pos = pygame.mouse.get_pos()
    keys = pygame.key.get_pressed()

    mouse_click = False
    for event in pygame.event.get():
        if(event.type == pygame.QUIT):
            done = True
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_click = True

    if menuScreen.checkUpdate():
        
        screen2button = shopButton.focusCheck(mouse_pos, mouse_click)
        shopButton.showButton(menuScreen.returnTitle())
        if screen2button:
            win = screen2.makeCurrent()
            
            menuScreen.endCurrent()    
    elif screen2.checkUpdate():
        returnm = DungeonButton.focusCheck(mouse_pos, mouse_click)
        Assassin_Upgrade.showButton(screen2.returnTitle())
        if Assassin_Upgrade.focusCheck(mouse_pos):
            Assassin_Text.showButton(screen2.returnTitle())
        if returnm:
            win = menuScreen.makeCurrent()
            screen2.endCurrent()

    pygame.display.update()

pygame.quit()
2 Answers

Add another text attribute to your class. e.g.:

class Button():
    def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text, tiptext):
        # [...]

        self.tiptextsurface = self.buttonf.render(tiptext, False, (0, 0, 0), (255, 255, 0))

Draw the text in a Button.showTip method, when self.current is set:

class Button():
    # [...]

    def showTip(self, display):
        if self.current:
            mouse_pos = pygame.mouse.get_pos()
            display.blit(self.tiptextsurface, (mouse_pos[0]+16, mouse_pos[1]))


Minimal example based on your code:

I've simplified the Button class, but it behaves exactly like the class in your question.

import pygame
pygame.init()

class Button():
    def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text, tiptext):
        self.rect = pygame.Rect(x, y, sx, sy)
        self.bcolour = bcolour
        self.fbcolour = fbcolour
        self.fcolour = fcolour
        self.fontsize = fontsize
        self.current = False
        self.buttonf = pygame.font.SysFont(font, fontsize)
        self.textsurface = self.buttonf.render(text, False, self.fcolour)
        self.tiptextsurface = self.buttonf.render(tiptext, False, (0, 0, 0), (255, 255, 0))
        
    def showButton(self, display):
        color = self.fbcolour if self.current else self.bcolour
        pygame.draw.rect(display, color, self.rect)
        display.blit(self.textsurface, self.textsurface.get_rect(center = self.rect.center))

    def showTip(self, display):
        if self.current:
            mouse_pos = pygame.mouse.get_pos()
            display.blit(self.tiptextsurface, (mouse_pos[0]+16, mouse_pos[1]))

    def focusCheck(self, mousepos, mouseclick):
        self.current = self.rect.collidepoint(mousepos)
        return mouseclick if self.current else True
                
screen = pygame.display.set_mode((400, 600))
clock = pygame.time.Clock()
font = pygame.font.Font('freesansbold.ttf', 32)
shopButton = Button(125, 500, 150, 50, "black", "red", "arial", 20, "white", "Shop", "Enter the shop")

done = False
while not done:    
    clock.tick(60)
    for event in pygame.event.get():
        if(event.type == pygame.QUIT):
            done = True
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_click = True

    mouse_pos = pygame.mouse.get_pos()
    mouse_click = False
    screen2button = shopButton.focusCheck(mouse_pos, mouse_click)

    screen.fill((255, 255, 255))
    shopButton.showButton(screen)
    shopButton.showTip(screen)
    pygame.display.update()

pygame.quit()

It's easier to answer questions that provide a Minimal, Reproducible Example. Your sample has external image and font dependencies that will need to be overcome for someone to debug your code.

If your button had an associated rect, then you can tell if the mouse cursor is hovering over the button using Rect.colliderect(position).

Here's a minimal example showing some hovering text when the mouse is in a rectangle:

import pygame
WIDTH = 640
HEIGHT = 480
FPS = 30

pygame.init()
pygame.font.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(f"Color: purple")
clock = pygame.time.Clock()

# create a centered rectangle to fill with color
color_rect = pygame.Rect(100, 100, WIDTH - 200, HEIGHT - 200)  

font = pygame.font.SysFont(None, 50, True)
msg = "  Hovering Message  "
hover_surface = font.render(msg, True, pygame.Color("chartreuse"), pygame.Color("firebrick"))

hovering = False
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    # update game elements - Get mouse position
    mx, my = pygame.mouse.get_pos()
    # draw surface - fill background
    screen.fill(pygame.Color("grey"))
    ## draw central rect
    screen.fill(pygame.Color("purple"), color_rect)
    ## display the hovering text if the mouse is over the rectangle
    if color_rect.collidepoint((mx, my)):
        screen.blit(hover_surface, (mx, my))
    # show surface
    pygame.display.update()
    # limit frames
    clock.tick(FPS)
pygame.quit()

hovering text

Your code uses a lot of raw geometric calculations where it might be easier in the long run to use some of the native pygame elements. For example, you can make your buttons immobile Sprites, and keep them in a Sprite group to make managing them easier.

Related