Change/ update text in a textobject in pygame [duplicate] - python

What am I doing wrong here?
I want to update the text of the label to accommodate a player score.
I've looked at other examples and added the update method but the text still stays the same.
class Label():
def __init__(self, txt, location, size=(160,30), bg=WHITE, fg=BLACK, font_name="Segoe Print", font_size=12):
self.bg = bg
self.fg = fg
self.size = size
self.font = pygame.font.Font(font_name, font_size)
self.txt = txt
self.txt_surf = self.font.render(self.txt, 1, self.fg)
self.txt_rect = self.txt_surf.get_rect(center=[s//2 for s in self.size])
self.surface = pygame.surface.Surface(size)
self.rect = self.surface.get_rect(topleft=location)
def draw(self):
self.surface.fill(self.bg)
self.surface.blit(self.txt_surf, self.txt_rect)
screen.blit(self.surface, self.rect)
def update(self):
self.txt_surf = self.font.render(self.txt, 1, self.fg)
self.surface.blit(self.txt_surf, self.txt_rect)

You can simply assign the current score (convert it into a string first) to the .txt attribute of your label object and then call its update method.
# In the main while loop.
score += 1
label.txt = str(score)
label.update()
I would also just blit the surface in the draw method and update it in the update method.
def draw(self, screen):
screen.blit(self.surface, self.rect)
def update(self):
self.surface.fill(self.bg)
self.txt_surf = self.font.render(self.txt, True, self.fg)
self.surface.blit(self.txt_surf, self.txt_rect)

Related

Optimal Numpad in Pygame

I am trying to create a numpad.
The way I try to making the numpad is like:
class One:
def __init__(self,c,msg):
self.screen = c.calculator
self.screen_rect = self.screen.get_rect()
self.settings = c.settings
self.width,self.height = 60,60
self.button_color = (230,230,230)
self.text_color = (100,100,100)
self.rect = pygame.Rect(30,600,self.width,self.height)
self.font = pygame.font.SysFont('Comic Sans MS',20,bold=pygame.font.Font.bold)
self._prep_msg(msg)
def _prep_msg(self,msg):
self.msg_image = self.font.render(msg,True,self.text_color,self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
self.screen.fill(self.button_color,self.rect)
self.screen.blit(self.msg)
This way is requires a lot of code, making one class each for like 9 numbers.
I hoping if there was more optimal way of making this numpad.
I think of making sprites but I don't know how I will change the text:
self.One = One(self,"1")
If there is something you can't understand please ask.
Thank you for help!
Read about Classes. You can instantiate a class multiple times. e.g.:
Create a Button. The position of the button is an argument to the constructor:
class Button:
def __init__(self, c, msg, x, y): # <--- x, y
self.screen = c.calculator
self.screen_rect = self.screen.get_rect()
self.settings = c.settings
self.width,self.height = 60,60
self.button_color = (230,230,230)
self.text_color = (100,100,100)
self.rect = pygame.Rect(x, y, self.width, self.height) # <--- x, y
self.font = pygame.font.SysFont('Comic Sans MS',20,bold=pygame.font.Font.bold)
self._prep_msg(msg)
def _prep_msg(self,msg):
self.msg_image = self.font.render(msg,True,self.text_color,self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
self.screen.fill(self.button_color,self.rect)
self.screen.blit(self.msg)
Create multiple Instance Objects of the class Button:
self.button_one = Button(self, "1", 30, 600)
self.button_two = Button(self, "2", 100, 600)

Pygame Scroll Bar To Play Volume Low OR High.?

so I have a scroll bar in my game what Im trying to do is make it so if my mouse is over the bar1 button and we are on the moving_spot of the bar1 button then we can move it up and down on its y axis
how can I move the bar up and down and if its colliding with with any of the volume buttons I can change the volume of my background music either 0.1 or 0.2 or 0.3 so it controls my game volumepygame.mixer.music.set_volume(0.3)
my problem is im not sure how I could get this started I have everything in place but not sure where to start ***how can I move the bar with my mouse on the moving_spot on its y values only and if the bar1 is over and of the volume1 2 or 3 4 buttons then it should play the volume at defferent level
im not sure how to approach this problem any help is appreciated I just need a way to adjust my music of my game if the player moves the bar up or down
while run:
# Making game run with fps
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
# this is our bar1 the gray part that we will be able to move
if bar1.isOver(pos):
bar1.y = pos
print("WORKING{")
here are my buttons and positions places the move_spot is where the bar1 can only move up and down
the bar1 is the bar that the player can control to control the volume
and also the volume 1 2 3 4 are where the defferent volume of our background music will be set
move_spot = button((colors),720,125,25,260, '')
bar1 = button((colors),715,125,30,60, '')
volume1 = button((colors2),715,145,30,60, '')
volume2 = button((colors2),715,210,30,60, '')
volume3 = button((colors2),715,280,30,60, '')
volume4 = button((colors2),715,350,30,60, '')
buttons = [bar1,move_spot,volume1,volume2,volume3,volume4]
this is my buttons class
# our buttons
class button():
def __init__(self, color, x,y,width,height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.over = False
def draw(self,window,outline=None):
#Call this method to draw the button on the screen
if outline:
pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
if self.text != '':
font = pygame.font.SysFont('image/abya.ttf', 60)
text = font.render(self.text, 1, (255,255,255))
window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
def isOver(self, pos):
#Pos is the mouse position or a tuple of (x,y) coordinates
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
def playSoundIfMouseIsOver(self, pos, sound):
if self.isOver(pos):
if not self.over:
click.play()
self.over = True
else:
self.over = False
here a minimal code you can run and test with this bar image
heres the background music music
import pygame
pygame.init()
window = pygame.display.set_mode((800,800))
# our buttons
class button():
def __init__(self, color, x,y,width,height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.over = False
def draw(self,window,outline=None):
#Call this method to draw the button on the screen
if outline:
pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
if self.text != '':
font = pygame.font.SysFont('freesansbold.ttf', 60)
text = font.render(self.text, 1, (255,255,255))
window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
def isOver(self, pos):
#Pos is the mouse position or a tuple of (x,y) coordinates
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
def playSoundIfMouseIsOver(self, pos, sound):
if self.isOver(pos):
if not self.over:
click.play()
self.over = True
else:
self.over = False
colors = 0,23,56
colors2 = 0,123,56
bar11 = pygame.image.load("bar.png").convert_alpha()
move_spot = button((colors),720,125,25,260, '')
bar1 = button((colors),715,125,30,60, '')
volume1 = button((colors2),715,145,30,60, '')
volume2 = button((colors2),715,210,30,60, '')
volume3 = button((colors2),715,280,30,60, '')
volume4 = button((colors2),715,350,30,60, '')
buttons = [bar1,move_spot,volume1,volume2,volume3,volume4]
# fos
fps = 60
clock = pygame.time.Clock()
# redraw
def redraw():
window.fill((40,100,200))
for button in buttons:
button.draw(window)
window.blit(bar11,(bar1.x,bar1.y))
# main loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
redraw()
pygame.display.update()
pygame.quit()
As a general rule of thumb, you want to delegate all the heavy lifting to classes, and leave the "good" stuff to the mainloop of your program. I have created a simple class here, which takes some inputs (width, height, number of slider options), and will take care of all the drawing, positioning, etc. for you. It also has an option of self.chosen, which tells you which option is picked. I then used this to set the volume outputted by the mixer, based on which option is chosen, in the update() function:
class VolumeBar(pygame.sprite.Sprite):
def __init__(self, options, width, height):
# Store these useful variables to the class
self.options = options
self.width = width
self.height = height
# The slider
self.slider = pygame.image.load('slider.png')
self.slider_rect = self.slider.get_rect()
# The background "green" rectangles, mostly for decoration
self.back = []
objectHeight = (height-options*6)/(options-1)
self.backHeight = objectHeight
for index in range(options-1):
self.back.append(pygame.Rect((0, rint((6*index+6)+index*objectHeight)), (width, rint(objectHeight))))
# The foreground "blue" rectangles, mostly for decoration
self.fore = []
for index in range(options):
self.fore.append(pygame.Rect((0, rint(index*(objectHeight+6))), (width, 6)))
# Get list of possible "snaps", which the slider can be dragged to
self.snaps = []
for index in range(options):
self.snaps.append((width/2, 3+(index*(objectHeight+6))))
# A simple variable to tell you which option has been chosen
self.chosen = 0
# Generate the image surface, then render the bar to it
self.image = pygame.Surface((width, height))
self.rect = self.image.get_rect()
self.render()
self.focus = False
def render(self):
self.image.fill([255, 255, 255])
for back in self.back:
pygame.draw.rect(self.image, [0, 192, 0], back)
for fore in self.fore:
pygame.draw.rect(self.image, [0, 0, 0], fore)
self.image.blit(self.slider, (rint((self.width-self.slider_rect.width)/2),
rint(self.snaps[self.chosen][1]-(self.slider_rect.height/2))))
def draw(self, surface):
surface.blit(self.image, self.rect.topleft)
def mouseDown(self, pos):
if self.rect.collidepoint(pos):
self.focus = True
return True
return False
def mouseUp(self, pos):
if not self.focus:
return
self.focus = False
if not self.rect.collidepoint(pos):
return
pos = list(pos)
# We've made sure the mouse started in our widget (self.focus), and ended in our widget (collidepoint)
# So there is no reason to care about the exact position of the mouse, only where it is relative
# to this widget
pos[0] -= self.rect.x
pos[1] -= self.rect.y
# Calculating max distance from a given selection, then comparing that to mouse pos
dis = self.backHeight/2 + 3
for index, snap in enumerate(self.snaps):
if abs(snap[1]-pos[1]) <= dis:
self.chosen = index
break
self.render()
def update(self):
pygame.mixer.music.set_volume((self.options-self.chosen)*0.1)
Most of the __init__ function is spent calculating positions for each of the green and black rectangles, which are drawn in render() and displayed in draw(). The other functions are there for the mouse input, the first checks if the mouseDown happened on said button, and if it did, it sets self.focus to True, so that the mouseUp handler knows that it should change the slider position.
All of this works together to make a working VolumeBar. Below is an example of how it would be used in a mainloop:
import pygame
pygame.init()
rint = lambda x: int(round(x, 0)) # Rounds to the nearest integer. Very useful.
class VolumeBar(pygame.sprite.Sprite):
def __init__(self, options, width, height):
# Store these useful variables to the class
self.options = options
self.width = width
self.height = height
# The slider
self.slider = pygame.image.load('slider.png')
self.slider_rect = self.slider.get_rect()
# The background "green" rectangles, mostly for decoration
self.back = []
objectHeight = (height-options*6)/(options-1)
self.backHeight = objectHeight
for index in range(options-1):
self.back.append(pygame.Rect((0, rint((6*index+6)+index*objectHeight)), (width, rint(objectHeight))))
# The foreground "blue" rectangles, mostly for decoration
self.fore = []
for index in range(options):
self.fore.append(pygame.Rect((0, rint(index*(objectHeight+6))), (width, 6)))
# Get list of possible "snaps", which the slider can be dragged to
self.snaps = []
for index in range(options):
self.snaps.append((width/2, 3+(index*(objectHeight+6))))
# A simple variable to tell you which option has been chosen
self.chosen = 0
# Generate the image surface, then render the bar to it
self.image = pygame.Surface((width, height))
self.rect = self.image.get_rect()
self.render()
self.focus = False
def render(self):
self.image.fill([255, 255, 255])
for back in self.back:
pygame.draw.rect(self.image, [0, 192, 0], back)
for fore in self.fore:
pygame.draw.rect(self.image, [0, 0, 0], fore)
self.image.blit(self.slider, (rint((self.width-self.slider_rect.width)/2),
rint(self.snaps[self.chosen][1]-(self.slider_rect.height/2))))
def draw(self, surface):
surface.blit(self.image, self.rect.topleft)
def mouseDown(self, pos):
if self.rect.collidepoint(pos):
self.focus = True
return True
return False
def mouseUp(self, pos):
if not self.focus:
return
self.focus = False
if not self.rect.collidepoint(pos):
return
pos = list(pos)
# We've made sure the mouse started in our widget (self.focus), and ended in our widget (collidepoint)
# So there is no reason to care about the exact position of the mouse, only where it is relative
# to this widget
pos[0] -= self.rect.x
pos[1] -= self.rect.y
# Calculating max distance from a given selection, then comparing that to mouse pos
dis = self.backHeight/2 + 3
for index, snap in enumerate(self.snaps):
if abs(snap[1]-pos[1]) <= dis:
self.chosen = index
break
self.render()
def update(self):
pygame.mixer.music.set_volume((self.options-self.chosen)*0.1)
screen = pygame.display.set_mode([500, 500])
test = VolumeBar(10, 30, 300)
test.rect.x = 50
test.rect.y = 50
clock = pygame.time.Clock()
game = True
while game:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
game = False
if event.type == pygame.MOUSEBUTTONDOWN:
test.mouseDown(pygame.mouse.get_pos())
if event.type == pygame.MOUSEBUTTONUP:
test.mouseUp(pygame.mouse.get_pos())
if not game:
break
screen.fill([255, 255, 255])
test.update()
test.draw(screen)
pygame.display.update()
clock.tick(60)
The final product:
https://i.gyazo.com/f6c2b5ede828f7715e5fd53a65c32c13.mp4
As long as your mouseDown happened on this widget, mouseUp will determine where the slider ends up. Thusly, you can click, drag, or tap the slider anywhere on it, and the slider will go to the correct position.
Accessing the position of the slider is quite simple, just look at self.chosen. It defaults to 0 (Because it was set to that in the __init__) function, which is at the very top.

Button with different text size pygame

I am creating a simulator in pygame for school. I have created 3 buttons, which change colur when you hover. However, I want one of the buttons to have smaller text in. Currently, to set the text I have it within a class, but not as a method. I have made the button smaller when calling it as an instance, but I am not sure how to make the text smaller for this button, without changing the text in the others.
this is my code for my button class
class button():
def __init__(self, color, x , y , width , height , text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self,win,outline= None):
if outline:
pygame.draw.rect(win, outline, (self.x-2 , self.y-2, self.width+4, self.height+4),0)
pygame.draw.rect(win, self.color, (self.x , self.y , self.width, self.height),0)
if self.text != '':
font = pygame.font.SysFont( "Times New Roman,Arial",40, bold = True)
text = font.render(self.text, 1 , (0,0,0))
win.blit(text, (self.x +(self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
def isOver(self,pos):
if pos[0] > self.x and pos [0] < self.x + self.width:
if pos [1] > self.y and pos[1] < self.y + self.height:
return True
return False
def redrawWindow():
userButton.draw(win,(0,0,0))
passButton.draw(win,(0,0,0))
loginButton.draw(win,(0,0,0))
this is the code for creating the instances:
userButton = button((TURQUOISE), 300,225,400,100, 'Username' )
passButton = button((TURQUOISE), 300,350,400,100, 'Password' )
loginButton = button((TURQUOISE), 465,470,100,25, 'Login' )
Just add an additional argument textSize to the contrcutor of the button:
class button():
def __init__(self, color, x , y , width , height , text='', textSize = 40):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.textSize = textSize
def draw(self,win,outline= None):
# [...]
if self.text != '':
font = pygame.font.SysFont( "Times New Roman,Arial", self.textSize, bold = True)
Pass the size of the text to the constructor. For isntance
userButton = button((TURQUOISE), 300,225,400,100, 'Username', 40)
passButton = button((TURQUOISE), 300,350,400,100, 'Password', 40)
loginButton = button((TURQUOISE), 465,470,100,25, 'Login', 30)
However creating a font object is a very expensive operation. You should avoid creating the font once per frame or even once per button drawing. See How to render/blit text in pygame for good performance.
I recommend to create the different fonts once at the begin of the application:
font30 = pygame.font.SysFont("Times New Roman,Arial", 30, bold = True)
font40 = pygame.font.SysFont("Times New Roman,Arial", 40, bold = True)
Add font attribute to the class instead of the text size attribute:
class button():
def __init__(self, color, x, y, width, height, font, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.font = font
self.text = text
def draw(self,win,outline= None):
# [...]
if self.text != '':
text = self.font.render(self.text, 1 , (0,0,0))
Pass the font object to the constructor of button. For instance:
userButton = button((TURQUOISE), 300,225,400,100, font40, 'Username')
passButton = button((TURQUOISE), 300,350,400,100, font40, 'Password')
loginButton = button((TURQUOISE), 465,470,100,25, font30, 'Login')

how do you blit text to screen using classes in pygame?

I am trying to blit text onto a screen, but only using classes. When I create an instance from a class, is there any way to reference that same instance within the class? For example, in my code:
class _text_():
def __init__(self, font, size, text, antialias, colour, background):
self.font = font
self.size = size
self.text = text
self.antialias = antialias
self.colour = colour
self.background = background
def _textsettings_(self):
texts = pygame.font.SysFont(self.font, self.size)
return texts.render(self.text, self.antialias, self.colour, self.background)
def _textblit_(self):
return win.blit[text1, (400, 240)]
text1 = _text_('calibri', 20, "press any button to start", True, black, None)
in the textblit function, I am trying to use variable 'text1' so that I can blit it to screen, but obviously this isn't working and I am stumped as to how to do it.
Any suggestions appreciated!
The text surface becomes an instance attribute of the class (self.text):
class _text_():
def __init__(self, font, size, text, antialias, colour, background):
self.font = font
self.size = size
self.text = text
self.antialias = antialias
self.colour = colour
self.background = background
texts = pygame.font.SysFont(self.font, self.size)
self.text = texts.render(self.text, self.antialias, self.colour, self.background)
def _textblit_(self):
win.blit(self.text, (400, 240))
Call _textblit_ in the main application loop:
text1 = _text_('calibri', 20, "press any button to start", True, black, None)
while True:
# [...]
text1._textblit_()
# [...]
This is the class :
name file = text.py
import pygame
class _text_():
def __init__(self,text):
pygame.init()
self.text = text
self.antialias = True
self.colour = (255,255,255)
self.background = None
self.font = pygame.font.SysFont("arialblack",40)
self.img = self.font.render(self.text, self.antialias, self.colour,self.background)
def _textblit_(self,surface):
surface.blit(self.img,(400,240))
This is the test
import pygame
from text import _text_
SCREEN_HEIGHT = 800
SCREEN_WIDTH = 1200
screen = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
text1 = _text_(": )")
run = True
while run:
screen.fill((52,78,91))
text1._textblit_(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()

How do I access a variable in a class that was defined before in another class?

I am trying to make a python library:
There is a class (game) in it is a function that defines a variable (display)
Then there is another class (char) in main, I want to access display in char
How do I do it?
In the past I have tried: self.display, global display, and game.display
class game():
def __init__(self, disp, width, height):
self.display = disp # VARIABLE I WANT TO ACCESS
self.height = height
self.width = width
class sprite():
def __init__(self, size, position, image):
self.image = image
self.size = size
self.rect = self.image.get_rect()
self.rect.x = position[0]
self.rect.y = position[1]
self.x = position[0]
self.y = position[1]
self.collisionDirection = 5
self.hasCollided = False
self.mask = pygame.mask.from_surface(self.image)
self.velocity = 0
def render(self):
self.rect.x = self.x
self.rect.y = self.y
self.mask = pygame.mask.from_surface(self.image)
display.blit(self.image, (self.x, self.y)) # WHERE I WANT TO ACCESS IT
I keep getting AttributeError what can I do?
You can pass the Game instance to another class. For example
# instantiate the game
g = game()
# create an instance of char
c = char(game)
assuming the __init__ of char() looks something like this:
class char():
def __init__(self, game):
# save a reference to the game object as an attribute
self.game = game
# now you can access the game's attributes
print(self.game.display)
Allow that the below example is contrived, and not likely the way you would design pen/paper, but it shows that there are a couple different ways that you can accomplish what you ask.
class Pen:
def __init__(self, thickness, color):
self.thickness = thickness
self.color = color
class Paper:
def __init__(self, pen: Pen):
self.pen = pen
def draw(self):
pen_color = self.pen.color
return pen_color
def draw2(self, pen: Pen):
pen_color = pen.color
return pen_color
red_pin = Pen(2, 'Red')
blue_pin = Pen(1, 'Blue')
paper = Paper(red_pin)
print(paper.draw()) # prints Red
print(paper.draw2(blue_pin)) # prints Blue

Categories

Resources