Python Forum
[PyGame] Button class still not working?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Button class still not working?
#1
Hi - me again haha,

I sorted out my button class somewhat, so it now works to an extent. However, I now have the issue that whenever I click one button, it seems to run the function for both buttons on the screen. Does anyone know how to fix this (but I have a limited understanding of Python so... not too complicated if possible lol!)

Thank you!

class Button:
    def __init__(self, rect, command, **kwargs):
        self.processOptions(kwargs)
        self.rect = pygame.Rect(rect)
        self.image = pygame.Surface(self.rect.size).convert()
        self.function = command
        self.text = self.font.render(self.text,True,self.fontColour)
        self.text_rect = self.text.get_rect(center=self.rect.center)
         
    def processOptions(self, kwargs):
        settings = {
            'colour'         :YELLOW,
            'text'          :'default',
            'font'          :pygame.font.SysFont(None, 80),
            'fontColour'    :BLACK,
        }
        for kwarg in kwargs:
            if kwarg in settings:
                settings[kwarg] = kwargs[kwarg]
            else:
                raise AttributeError("{} has no keyword: {}".format(self.__class__.__name__, kwarg))
        self.__dict__.update(settings)
 
    def getEvent(self):
        if event.type == pygame.MOUSEBUTTONDOWN:
            self.onClick()
 
    def onClick(self):
        self.function()

    def draw(self, surface):
        self.image.fill(self.colour)
        surface.blit(self.image, self.rect)
        surface.blit(self.text, self.text_rect)

def letterButton():     # setting buttons of each letter option & its location on the screen

    done = False
    while done == False:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            
        screen.fill(WHITE)
        title = lettersTitle.render('What letter does your name begin with?', False, BLACK)
        screen.blit(title, [0, 0])

def teacherChoice():

    loginButton = Button((100, 100, 400, 100), loginScreen, text = 'Login')
    signupButton = Button((100, 250, 400, 100), signupScreen, text = 'New Teacher', colour = BLUE)

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

        screen.fill(WHITE)

        loginButton.getEvent()
        signupButton.getEvent()
    
        teacherChoice = True
        signup = False
        login = False
        screen.fill(WHITE)
        loginButton.draw(screen)
        signupButton.draw(screen)

        pygame.display.update()

        if login:
            teacherChoice = False
            loginScreen()

        if signup:
            teacherChoice = False
            signupScreen()

    pygame.quit()

studentButton = Button((100, 100, 400, 100), letterButton, text = 'Student')
teacherButton = Button((100, 250, 400, 100), teacherChoice, colour = BLUE, text = 'Teacher')

while done == False:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
    studentButton.getEvent()
    teacherButton.getEvent()

    originalScreen = True

    #screen.fill(WHITE)
    screen.blit(loginMessage, [100, 10])
    mouse = pygame.mouse.get_pos()
    studentButton.draw(screen)
    teacherButton.draw(screen)
    pygame.display.update()

pygame.quit()
Reply
#2
You shouldnt have two main game loops like that. Here is an example using buttons between states. Notice there is only one while loop in the entire code regardless of the number of states. Classes are a fundamental part of python. I would learn that before trying to program games to be honest.

You are not using the button's event check correctly. It needs to be within the main loop's event check.

from this:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
    studentButton.getEvent()
    teacherButton.getEvent()
to this:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        studentButton.getEvent()
        teacherButton.getEvent()
Recommended Tutorials:
Reply
#3
1. Do you need to use pygame ?
Other gui like tkinter, gtk, wxpython, pyqt, and etc. Has all this built in.

Example pygame.
import pygame
pygame.init()

class SimpleButton:
    def __init__(self, text, rect, callback,
                 font = pygame.font.Font(None, 24),
                 font_color="black",
                 color="dodgerblue",
                 hover_color="lawngreen"):

        self.text = text
        self.callback = callback
        self.font = font

        if isinstance(rect, pygame.Rect):
            self.rect = rect
        else:
            self.rect = pygame.Rect(rect)

        if isinstance(font_color, str):
            self.font_color = pygame.Color(font_color)
        else:
            self.font_color = font_color

        if isinstance(color, str):
            self.color = pygame.Color(color)
        else:
            self.color = color

        if isinstance(hover_color, str):
            self.hover_color = pygame.Color(hover_color)
        else:
            self.hover_color = hover_color

        self.hover = False
        self.render()

    def draw(self, surface):
        if self.hover:
            surface.fill(self.hover_color, self.rect)
        else:
            surface.fill(self.color, self.rect)

        surface.blit(self.image, self.position)

    def mouse_motion(self, event):
        self.hover = self.rect.collidepoint(event.pos)

    def mouse_button_up(self, event):
        if self.rect.collidepoint(event.pos):
            if self.callback:
                self.callback()

    def render(self):
        self.image = self.font.render(self.text, 1, self.font_color)
        self.position = self.image.get_rect()
        self.position.center = self.rect.center

class State:
    def __init__(self, engine):
        self.engine = engine

    def on_draw(self, surface): pass
    def on_event(self, event): pass

class SimpleStateMachine:
    def __init__(self):
        # basic pygame setup
        pygame.display.set_caption('Button & State Example')
        self.rect = pygame.Rect(0, 0, 800, 600)
        self.surface = pygame.display.set_mode(self.rect.size)
        self.clock = pygame.time.Clock()

        self.state = None

    # main loop
    def loop(self):
        self.running = True
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False

                if self.state:
                    self.state.on_event(event)

            if self.state:
                self.state.on_draw(self.surface)

            pygame.display.flip()
            self.clock.tick(30)

class FirstState(State):
    def __init__(self, engine):
        State.__init__(self, engine)

        self.buttons = [
            SimpleButton('Student', (100, 100, 400, 40), self.button_student),
            SimpleButton('Teacher', (100, 150, 400, 40), self.button_teacher)
        ]

    def button_student(self):
        self.engine.state = StudentState(self.engine)

    def button_teacher(self):
        self.engine.state = TeacherState(self.engine)

    def on_draw(self, surface):
        surface.fill(pygame.Color('white'))
        for button in self.buttons:
            button.draw(surface)

    def on_event(self, event):
        if event.type == pygame.MOUSEBUTTONUP:
            for button in self.buttons:
                button.mouse_button_up(event)
        elif event.type == pygame.MOUSEMOTION:
            for button in self.buttons:
                button.mouse_motion(event)

class StudentState(State):
    def __init__(self, engine):
        State.__init__(self, engine)

        self.buttons = [
            SimpleButton('Back', (100, 100, 400, 40), self.button_back)
        ]

    def button_back(self):
        self.engine.state = FirstState(self.engine)

    def on_draw(self, surface):
        surface.fill(pygame.Color('white'))
        for button in self.buttons:
            button.draw(surface)

    def on_event(self, event):
        if event.type == pygame.MOUSEBUTTONUP:
            for button in self.buttons:
                button.mouse_button_up(event)
        elif event.type == pygame.MOUSEMOTION:
            for button in self.buttons:
                button.mouse_motion(event)

class TeacherState(State):
    def __init__(self, engine):
        State.__init__(self, engine)

        self.buttons = [
            SimpleButton('Login', (100, 100, 400, 40), self.button_login),
            SimpleButton('New Teacher', (100, 150, 400, 40), self.button_teacher),
            SimpleButton('Back', (100, 200, 400, 40), self.button_back),
        ]

    def button_login(self):
        print('login')

    def button_teacher(self):
        print('teacher')

    def button_back(self):
        self.engine.state = FirstState(self.engine)

    def on_draw(self, surface):
        surface.fill(pygame.Color('white'))
        for button in self.buttons:
            button.draw(surface)

    def on_event(self, event):
        if event.type == pygame.MOUSEBUTTONUP:
            for button in self.buttons:
                button.mouse_button_up(event)
        elif event.type == pygame.MOUSEMOTION:
            for button in self.buttons:
                button.mouse_motion(event)

def main():
    engine = SimpleStateMachine()
    engine.state = FirstState(engine)
    engine.loop()
    pygame.quit()

if __name__ == '__main__':
    main()
Example. Poor quality tkinter code.
import tkinter as tk

class App:
    def __init__(self):
        self.root = tk.Tk()
        self.frame = tk.Frame(self.root)
        self.frame.pack(side='top', fill='both', expand=True)
        self.frame.grid_rowconfigure(0, weight=1)
        self.frame.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for page in (StartPage, TeacherPage):
            page_name = page.__name__
            frame = page(self.frame, self)
            self.frames[page_name] = frame
            frame.frame.grid(row=0, column=0, sticky='nsew')

        self.show_frame('StartPage')
        self.root.mainloop()

    def show_frame(self, page_name):
        self.frames[page_name].frame.tkraise()

class StartPage:
    def __init__(self, parent, controller):
        self.controller = controller
        self.frame = tk.Frame(parent)

        self.student = tk.Button(self.frame, text='Student')
        self.student.pack()

        self.teacher = tk.Button(self.frame, text='Teacher')
        self.teacher.bind("<Button-1>", self.call_teacher)
        self.teacher.pack()

    def call_teacher(self, event):
        self.controller.show_frame('TeacherPage')

class TeacherPage:
    def __init__(self, parent, controller):
        self.controller = controller

        self.frame = tk.Frame(parent)

        self.login = tk.Button(self.frame, text='Login')
        self.login.pack()
        self.new_teacher = tk.Button(self.frame, text="New Teacher")
        self.new_teacher.pack()

App()
99 percent of computer problems exists between chair and keyboard.
Reply
#4
(Jan-16-2019, 04:10 PM)Windspar Wrote: . Do you need to use pygame ?
@OP
yeah if your just making buttons then your better off just using a GUI. If you are eventually turning this into a game, or just pygame learning, then its fine.
Recommended Tutorials:
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] Can't get button class to work mzmingle 3 3,236 Nov-26-2018, 10:06 PM
Last Post: metulburr

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020