I’m not sure if I’m using classes correctly. I’m attempting to build a simple menu using pygame. This is my first foray into gui stuff. I have no idea how to structure my code.
I found that I could make a general Button class to handle all of the mouse over/mouse click stuff, and then I can subclass that for each button, and override the do_action method to give each button a specific action.
class Button(pygame.sprite.Sprite):
global screen_flags
def __init__(self, images, pos):
pygame.sprite.Sprite.__init__(self)
self.images = images
self.image = images[0]
self.rect = self.image.get_rect()
self.rect.move_ip(pos)
def update(self, events, surface):
# if not screen_flags['config']:
for event in events:
if event.type == MOUSEMOTION:
if self.rect.collidepoint(event.pos):
self.image = self.images[1]
else:
self.image = self.images[0]
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1 and self.rect.collidepoint(event.pos):
self.image = self.images[-1]
screen_flags['config'] = 1
self.do_action()
self.set_flag()
elif event.type == MOUSEBUTTONUP:
self.image = self.images[0]
screen.blit(self.image, self.rect)
def do_action(self):
pass
def set_flag(self):
pass
class CheckBox(Button):
def __init__(self, images, pos):
Button.__init__(self, images, pos)
self.is_clicked = False
def update(self, events, surface):
for event in events:
if event.type == MOUSEMOTION:
if not self.is_clicked:
if self.rect.collidepoint(event.pos):
self.image = self.images[1]
else:
self.image = self.images[0]
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1 and self.rect.collidepoint(event.pos):
if not self.is_clicked:
self.image = self.images[-1]
self.is_clicked = True
else:
self.image = self.images[0]
self.is_clicked = False
screen.blit(self.image, self.rect)
class Cancel(Button):
def do_action(self):
screen_flags['config'] = 0
So, as you can see,they don’t really do anything yet. I can toggle the check box on and off, and open and close one ‘config’ window, but that’s as far as I’ve gotten. The rest of the code:
global count
global sTime
config_button_img = load_sliced_images(25, 25, c_buttons)
config_button = Button(config_button_img, (608,4))
input_bar = load_sliced_images(351,33, inpt_bar)
text_box = Textbox(input_bar, (144,155))
s_button = load_sliced_images(110,32, sbmt_bttn)
submit = Button(s_button, (241,301))
c_button = load_sliced_images(110,32, cncl_bttn)
cancel = Cancel(c_button, (385, 301))
c_boxes = load_sliced_images(20,19, chk_box)
check_box = CheckBox(c_boxes, (200,200))
try:
while True:
# ****************************
# Pygame section
events = pygame.event.get()
for event in events:
if event.type == QUIT:
screen_flags['alert'] = 1
ding.play(0,1000)
elif event.type == MOUSEBUTTONDOWN:
text_box.set_focus(event.button, event.pos)
screen.blit(background, (0,0))
screen.blit(client_logo, (0,30))
screen.blit(tag, (174,462))
if screen_flags['config']:
screen.blit(config_window_img, (0,0))
submit.update(events, screen)
cancel.update(events, screen)
check_box.update(events, screen)
if screen_flags['alert']:
screen.blit(alert_dialog, (0,0))
config_button.update(events, screen)
pygame.display.update()
except KeyboardInterrupt:
try:
pygame.quit()
except:
pass
So this works kind of as expected. I’m unsure where to go from here. Do I continue to wrap up logic inside of the classes? For instance, the next thing I’m attempting to do is make it so that when the “cancel” button is clicked it unchecks the check box.
I tried changing the Cancel class to do just that.
class Cancel(Button):
def do_action(self):
screen_flags['config'] = 0
check_box.is_clicked=False
However, this is giving me a GlobalName Error. How do I target an instance method from within a different class? Is this even the correct way to go about it? Or have only some logic, like the update() stuff to take care of the mouse, in then handle what the classes do by passing variables to and from the different classes fro main()? Should I have all of the classes use global variables?
Is there any good articles on gui practices. Things like how to structure code etc..?
Hopefully the above makes sense.
Personally, I would make it so that each of my classes accepted
screen_flagsas an argument to the constructor (__init__). Then each of your classes would have a handle on the “global” data that they need. A real easy way to do this is…Of course, depending on what the shared data looks like (how many different variables you have, etc), you might want to pass a dictionary, or other object to make it so you don’t need to pass 5000 different pieces of shared data around.
Another way of dealing with this is to define your “global” data in a class as “class variables” and then inherit from that class.
I hope this helps. There was a lot in you post, so sorting through it all was a little difficult.
EDIT
In case you don’t understand how class variables are handled, see the comments in
do_action. Basically, you need to be careful that you don’t lose a handle on your data…