Posts: 30
Threads: 14
Joined: Aug 2017
Hi,
I'm trying to create a screen which will create buttons with each letter of the alphabet and then, when clicked, show a list of buttons with people with names beginning with that letter (from the database). I got the letters screen to run and show the different letters, but I haven't actually made them buttons yet - will this be possible without me creating a buttons class? What's the simplest way I can do this in?
def letterButton():
runlettersButton = True
screen.fill(WHITE)
title = lettersTitle.render('What letter does your name begin with?', False, BLACK)
screen.blit(title, [0, 0])
lettersDict = {'a': [5, 55],
'b': [90, 55],
'c': [175, 55],
'd': [260, 55],
'e': [345, 55],
'f': [430, 55],
'g': [515, 55],
'h': [5, 150],
'i': [90, 150],
'j': [175, 150],
'k': [260, 150],
'l': [345, 150],
'm': [430, 150],
'n': [515, 150],
'o': [5, 250],
'p': [90, 250],
'q': [175, 250],
'r': [260, 250],
's': [345, 250],
't': [430, 250],
'u': [515, 250],
'v': [5, 350],
'w': [90, 350],
'x': [175, 350],
'y': [260, 350],
'z': [345, 350]}
lettersList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
letterX = 5
letterY = 55
for i in range(0, len(lettersList)):
currentLetter = lettersList[i]
if i % 2 == 1:
colour = YELLOW
elif i % 2 == 0:
colour = BLUE
x = lettersDict[currentLetter][0]
y = lettersDict[currentLetter][1]
currentLetter = pygame.draw.rect(screen, colour, [x, y, 80, 95])
letterSurface = letters.render(lettersList[i], False, BLACK)
screen.blit(letterSurface, currentLetter)
Posts: 5,151
Threads: 396
Joined: Sep 2016
Nov-15-2018, 12:56 PM
(This post was last modified: Nov-15-2018, 12:56 PM by metulburr.)
A button class is ideal. Once you have a button class setup for one button you just have to loop the letters to insert the text to each button, and position each button. If you want them in odd weird positions, your going to have to set a dictionary with each position. Its much easier to increment them the same.
I modified our tutorial button class to include something like this.
This code creates a button and sets the y axis (top) of each button. enumerate allows you to increment a number for each button which can be used as a multiplier for the y axis.
lambda l=letter:print_on_press(l) passes the letter to the callback function so you can do something with each button uniquely. It requires lambda because you are passing something to the callback function. In addition it also requires letter to be passed into the lambda function as a local variable l . If you only did lambda:print_on_press(letter) then z would be the only thing that shows up because that is the last letter in the loop. letter would then be changed to what the next loop letter value is, instead of retaining it for each iteration.
Quote: for position, letter in enumerate(string.ascii_lowercase):
btn_height = 15
spacer = 5
top = position*btn_height + spacer
b = Button(rect=(10,top,105,btn_height), command=lambda l=letter:print_on_press(l), text=letter, **settings)
btns.append(b)
Here is the full code including the button class
import pygame as pg
class Button(object):
def __init__(self,rect,command,**kwargs):
self.rect = pg.Rect(rect)
self.command = command
self.clicked = False
self.hovered = False
self.hover_text = None
self.clicked_text = None
self.process_kwargs(kwargs)
self.render_text()
def process_kwargs(self,kwargs):
settings = {
"color" : pg.Color('red'),
"text" : None,
"font" : None, #pg.font.Font(None,16),
"call_on_release" : True,
"hover_color" : None,
"clicked_color" : None,
"font_color" : pg.Color("white"),
"hover_font_color" : None,
"clicked_font_color": None,
"click_sound" : None,
"hover_sound" : None,
'border_color' : pg.Color('black'),
'border_hover_color': pg.Color('yellow'),
'disabled' : False,
'disabled_color' : pg.Color('grey'),
'radius' : 3,
}
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 render_text(self):
if self.text:
if self.hover_font_color:
color = self.hover_font_color
self.hover_text = self.font.render(self.text,True,color)
if self.clicked_font_color:
color = self.clicked_font_color
self.clicked_text = self.font.render(self.text,True,color)
self.text = self.font.render(self.text,True,self.font_color)
def get_event(self,event):
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
self.on_click(event)
elif event.type == pg.MOUSEBUTTONUP and event.button == 1:
self.on_release(event)
def on_click(self,event):
if self.rect.collidepoint(event.pos):
self.clicked = True
if not self.call_on_release:
self.function()
def on_release(self,event):
if self.clicked and self.call_on_release:
#if user is still within button rect upon mouse release
if self.rect.collidepoint(pg.mouse.get_pos()):
self.command()
self.clicked = False
def check_hover(self):
if self.rect.collidepoint(pg.mouse.get_pos()):
if not self.hovered:
self.hovered = True
if self.hover_sound:
self.hover_sound.play()
else:
self.hovered = False
def draw(self,surface):
color = self.color
text = self.text
border = self.border_color
self.check_hover()
if not self.disabled:
if self.clicked and self.clicked_color:
color = self.clicked_color
if self.clicked_font_color:
text = self.clicked_text
elif self.hovered and self.hover_color:
color = self.hover_color
if self.hover_font_color:
text = self.hover_text
if self.hovered and not self.clicked:
border = self.border_hover_color
else:
color = self.disabled_color
#if not self.rounded:
# surface.fill(border,self.rect)
# surface.fill(color,self.rect.inflate(-4,-4))
#else:
if self.radius:
rad = self.radius
else:
rad = 0
self.round_rect(surface, self.rect , border, rad, 1, color)
if self.text:
text_rect = text.get_rect(center=self.rect.center)
surface.blit(text,text_rect)
def round_rect(self, surface, rect, color, rad=20, border=0, inside=(0,0,0,0)):
rect = pg.Rect(rect)
zeroed_rect = rect.copy()
zeroed_rect.topleft = 0,0
image = pg.Surface(rect.size).convert_alpha()
image.fill((0,0,0,0))
self._render_region(image, zeroed_rect, color, rad)
if border:
zeroed_rect.inflate_ip(-2*border, -2*border)
self._render_region(image, zeroed_rect, inside, rad)
surface.blit(image, rect)
def _render_region(self, image, rect, color, rad):
corners = rect.inflate(-2*rad, -2*rad)
for attribute in ("topleft", "topright", "bottomleft", "bottomright"):
pg.draw.circle(image, color, getattr(corners,attribute), rad)
image.fill(color, rect.inflate(-2*rad,0))
image.fill(color, rect.inflate(0,-2*rad))
def update(self):
#for completeness
pass
if __name__ == '__main__':
#code pertaining to the main program not in the button module
import string
pg.init()
screen = pg.display.set_mode((600,400))
screen_rect = screen.get_rect()
done = False
def print_on_press(letter):
print('{} pressed'.format(letter))
settings = {
"clicked_font_color" : (0,0,0),
"hover_font_color" : (205,195, 100),
'font' : pg.font.Font(None,16),
'font_color' : (255,255,255),
'border_color' : (0,0,0),
}
btns = []
for position, letter in enumerate(string.ascii_lowercase):
btn_height = 15
spacer = 5
top = position*btn_height + spacer
b = Button(rect=(10,top,105,btn_height), command=lambda l=letter:print_on_press(l), text=letter, **settings)
btns.append(b)
while not done:
mouse = pg.mouse.get_pos()
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for btn in btns:
btn.get_event(event)
for btn in btns:
btn.draw(screen)
pg.display.update() and here is a modified version of the callback function comparing with a database that only has a . You would just need to modify it accordingly to how your database is.
if __name__ == '__main__':
#code pertaining to the main program not in the button module
import string
pg.init()
screen = pg.display.set_mode((600,400))
screen_rect = screen.get_rect()
done = False
NAMES = {
'a':['Alex','Andy']
}
def print_on_press(letter):
print('{} pressed'.format(letter))
try:
print(NAMES[letter])
except KeyError:
print('There are no names in the database starting with {}'.format(letter))
settings = {
"clicked_font_color" : (0,0,0),
"hover_font_color" : (205,195, 100),
'font' : pg.font.Font(None,16),
'font_color' : (255,255,255),
'border_color' : (0,0,0),
}
btns = []
for position, letter in enumerate(string.ascii_lowercase):
btn_height = 15
spacer = 5
top = position*btn_height + spacer
b = Button(rect=(10,top,105,btn_height), command=lambda l=letter:print_on_press(l), text=letter, **settings)
btns.append(b)
while not done:
mouse = pg.mouse.get_pos()
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for btn in btns:
btn.get_event(event)
for btn in btns:
btn.draw(screen)
pg.display.update()
Recommended Tutorials:
Posts: 544
Threads: 15
Joined: Oct 2016
@ mzmingle
What I see so far. This would be easier done in tkinter, gtk, wxpython, or qt.
pygame is not a widget toolkit. I wish there was a widget toolkit for SDL that pygame could inherit from.
PyGizmo is my 10th attempt. It's a work in progress.
example.
import pygame
import string
class SimpleButton:
def __init__(self, caption, position, callback, font, color, bg_color, hover_color):
self.image = font.render(caption, 1, color)
self.rect = self.image.get_rect()
self.rect.topleft = position
self.bg_color = bg_color
self.hover_color = hover_color
self.mouse_hovering = False
self.callback = callback
self.caption = caption
def draw(self, surface):
if self.mouse_hovering:
surface.fill(self.hover_color, self.rect)
else:
surface.fill(self.bg_color, self.rect)
surface.blit(self.image, self.rect)
def on_mousemotion(self, event):
self.mouse_hovering = self.rect.collidepoint(event.pos)
def on_click(self, event):
if self.mouse_hovering:
self.callback(self)
def button_callback(button):
print(button.caption)
def main():
pygame.init()
pygame.display.set_caption('Simple Button Example')
surface = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
buttons = []
button_layout = (pygame.font.Font(None, 24),
pygame.Color('white'),
pygame.Color('navy'),
pygame.Color('dodgerblue'))
for enum, letter in enumerate(string.ascii_lowercase):
x = enum % 13
pos = x * 20 + 100, enum // 13 * 30 + 40
buttons.append(SimpleButton(letter, pos, button_callback, *button_layout))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEMOTION:
for button in buttons:
button.on_mousemotion(event)
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
for button in buttons:
button.on_click(event)
surface.fill(pygame.Color('black'))
for button in buttons:
button.draw(surface)
pygame.display.flip()
clock.tick(20)
pygame.quit()
main()
99 percent of computer problems exists between chair and keyboard.
|