This question already has answers here:
Why is my PyGame application not running at all?
(2 answers)
Why is nothing drawn in PyGame at all?
(2 answers)
Closed 2 years ago.
import pygame as pg
import sys
pg.init()
buttonFont = pg.font.SysFont("garamond", 25)
screenGray = pg.Color('gray80')
buttonGray2 = pg.Color('gray50')
textColour = pg.Color('navy')
screen = pg.display.set_mode((800, 600))
clock = pg.time.Clock()
class Button(pg.sprite.Sprite):
def __init__(self, text, x, y, width, height, colour):
super().__init__()
self.image = pg.Surface((width, height))
self.image.fill(colour)
self.rect = self.image.get_rect()
txt = buttonFont.render(text, True, textColour)
txtRect = txt.get_rect(center = self.rect.center)
self.image.blit(txt, txtRect)
self.rect.topleft = x, y
def isPressed(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
return True
return False
def FrontPage():
screen.fill(screenGray)
Continue = Button('Continue', 105, 455, 120, 50, buttonGray2)
buttonsGroup1 = pg.sprite.Group(Continue)
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
elif Continue.isPressed(event):
Menu()
buttonsGroup1.draw(screen)
pg.display.flip()
clock.tick(60)
def Menu():
screen.fill(screenGray)
Scytale = Button('Scytale', 105,105,140,65, buttonGray2)
Caesar = Button('Caesar', 330,105,140,65, buttonGray2)
Vigenere = Button('Vigenere', 555,105,140,65, buttonGray2)
Enigma = Button('Enigma', 105,430,140,65,buttonGray2)
PublicKey = Button('Public Key', 330,430,140,65, buttonGray2)
Rijndael = Button('Rijndael', 555,430,140,65, buttonGray2)
buttonsGroup2 = pg.sprite.Group(Scytale,Caesar,Vigenere,Enigma,PublicKey,Rijndael)
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
buttonsGroup2.draw(screen)
clock.tick(60)
FrontPage()
Above is the stripped back code of my FrontPage, that has a button on it that, when clicked, should take the user to the menu screen where 6 more buttons are displayed to move onto the users chosen encryption method.
However, when I press the Continue button, nothing happens.
Is it because there is something wrong with the Button Class?
Or is there something else that makes the button stop working?
Thanks in advance
You have to call pg.display.flip() in the Menu function.
I also have a little recommendation about the code structure. I'd use another function or class (main in this case) to manage the different scenes. So I first assign the current scene function to a variable and call it in the main while loop. When the scene is done, I return the next scene and assign it to the scene variable to swap the scenes. That will avoid potential recursion errors which you get if you just call the next function directly from within another scene (although it's unlikely that you'll exceed the recursion limit of 1000 in a simple game or app).
import pygame as pg
pg.init()
screen = pg.display.set_mode((600, 400))
clock = pg.time.Clock()
BLUE = pg.Color('dodgerblue3')
ORANGE = pg.Color('sienna3')
def front_page():
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return None
# Press a key to return the next scene.
elif event.type == pg.KEYDOWN:
return menu
screen.fill(BLUE)
pg.display.flip()
clock.tick(60)
def menu():
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return None
# Press a key to return the next scene.
elif event.type == pg.KEYDOWN:
return front_page
screen.fill(ORANGE)
pg.display.flip()
clock.tick(60)
def main():
scene = front_page # Set the current scene.
while scene is not None:
# Execute the current scene function. When it's done
# it returns either the next scene or None which we
# assign to the scene variable.
scene = scene()
main()
pg.quit()
Related
I'm displaying two random images in my pygame project that need to change when pressing one "z" or "x" in my keyboard. So I've created a function to do so and the images are correctly showing and changing. My problem is with the if statement in the run function: when the condition are met the sound start playing, but the images don't change their transparency. This is my code:
class Main():
def __init__(self):
pygame.init()
self.window = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.clock = pygame.time.Clock()
self.sound_one = pygame.mixer.Sound(os.path.join("soundone.mp3"))
self.sound_two = pygame.mixer.Sound(os.path.join("soundtwo.mp3"))
self.image_list = [] # a list with all the images
def show_image(self, image_one, image_two):
rect_one = image_one.get_rect(midleft=(50, 240))
rect_two = image_two.get_rect(midright=(640- 50, 240))
image_one.set_alpha(0)
image_two.set_alpha(0)
self.window.blit(image_one, rect_one)
self.window.blit(image_two, rect_two)
pygame.display.update()
return image_one, image_two
def run(self):
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == pygame.K_z:
self.one, self.two = self.show_image(random.choice(self.image_list), random.choice(self.image_list))
if event.key == pygame.K_x:
self.one, self.two = self.show_image(random.choice(self.image_list), random.choice(self.image_list))
self.window.fill((0,0,0))
if self.value <= 42:
self.one.set_alpha(50)
self.two.set_alpha(0)
self.sound_two.play(loops=0)
elif self.value > 53:
self.sound_one.play(loops=0)
self.one.set_alpha(0)
self.two.set_alpha(50)
elif 42 > self.value > 53:
self.one.set_alpha(0)
self.two.set_alpha(0)
self.clock.tick(30)
if __name__ == "__main__":
main = Main()
main.run()
How can I access the image created with a function to edit it? My code is not giving me back any error. It just doesn't work
You have to redraw the scene in every frame You must draw the images in the application loop and update the display in every frame:
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = True
if event.key == pygame.K_z:
self.one, self.two = random.choice(self.image_list), random.choice(self.image_list)
if event.key == pygame.K_x:
self.one, self.two = random.choice(self.image_list), random.choice(self.image_list)
self.window.fill((0,0,0))
rect_one = image_one.get_rect(midleft=(50, 240))
rect_two = image_two.get_rect(midright=(640- 50, 240))
self.window.blit(self.one, rect_one)
self.window.blit(self.two, rect_two)
pygame.display.update()
# [...]
pygame.quit()
sys.exit()
The typical PyGame application loop has to:
limit the frames per second to limit CPU usage with pygame.time.Clock.tick
handle the events by calling either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()
I'm working through a python book. So far, all of the programs have worked, but now I'm stuck. I typed in the program, and I'm getting this error when I run it. I checked all of the lines several times, and I think everything's right. It is supposed to open a text window and 10 seconds later show a game character. At first, I had an indent error, but I fixed that. Now I'm getting this new error. it starts up and runs for about 0.5 seconds, but it immediately closes and gives this error. I'm using Python 3.8.10 On Windows 10 Pro 64-bit and this is the error: pygame.error: video system not initialized and here is the code:
import pygame
import time
import subprocess
pygame.init()
screen = pygame.display.set_mode((800, 250))
clock = pygame.time.Clock()
font = pygame.font.Font(None, 25)
pygame.time.set_timer(pygame.USEREVENT, 200)
def text_generator(text):
tmp = ""
for letter in text:
tmp += letter
if letter != " ":
yield tmp
class DynamicText(object):
def __init__(self, font, text, pos, autoreset=False):
self.done = False
self.font = font
self.text = text
self._gen = text_generator(self.text)
self.pos = pos
self.autoreset = autoreset
self.update()
def reset(self):
self._gen = text_generator(self.text)
self.done = False
self.update()
def update(self):
if not self.done:
try: self.rendered = self.font.render(next(self._gen), True, (0, 128, 0))
except StopIteration:
self.done = True
time.sleep(10)
subprocess.Popen("python C:\\Users\\david\\Documents\\pythonbook\\pygame1.py 1", shell=True)
def draw(self, screen):
screen.blit(self.rendered, self.pos)
text=("Steve has gone on a quest to defeat the Ender Dragon. Will he make it?")
message = DynamicText(font, text, (65, 120), autoreset=True)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT: break
if event.type == pygame.USEREVENT: message.update()
else:
screen.fill(pygame.color.Color('black'))
message.draw(screen)
pygame.display.flip()
clock.tick(60)
continue
break
pygame.quit()
How can I figure out what the error message means? Or does anyone know?
You quit the game in every frame with pygame.quit(). quit the game after the application loop instead of in the application loop. Also why do you update the game just in the else case? The break statement in case of event.type == pygame.QUIT only breaks the event loop, but not the application loop. Change the control flow of your code:
# application loop
run = True
while run:
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT: run = False
if event.type == pygame.USEREVENT: message.update()
# redraw in every frame
screen.fill(pygame.color.Color('black'))
message.draw(screen)
pygame.display.flip()
clock.tick(60)
# quit after the application loop
pygame.quit()
This question already has answers here:
Pygame mouse clicking detection
(4 answers)
How can I add an image or icon to a button rectangle in Pygame?
(1 answer)
Closed 1 year ago.
I've created a button that I've imported from another class Button. However, after I create the button object, I still don't see it. How can I fix this?
Below is my function that runs the game.
def run_game(width, height, fps, starting_scene):
pygame.init()
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
active_scene = starting_scene
while active_scene != None:
pressed_keys = pygame.key.get_pressed()
# Event filtering
filtered_events = []
for event in pygame.event.get():
quit_attempt = False
if event.type == pygame.QUIT:
quit_attempt = True
elif event.type == pygame.KEYDOWN:
alt_pressed = pressed_keys[pygame.K_LALT] or \
pressed_keys[pygame.K_RALT]
if event.key == pygame.K_ESCAPE:
quit_attempt = True
elif event.key == pygame.K_F4 and alt_pressed:
quit_attempt = True
if quit_attempt:
active_scene.Terminate()
else:
filtered_events.append(event)
active_scene.ProcessInput(filtered_events, pressed_keys)
active_scene.Update()
active_scene.Render(screen)
active_scene = active_scene.next
pygame.display.flip()
clock.tick(fps)
# The rest is code where you implement your game using the Scenes model
class TitleScene(SceneBase):
def __init__(self):
SceneBase.__init__(self)
def ProcessInput(self, events, pressed_keys):
for event in events:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
# Move to the next scene when the user pressed Enter
self.SwitchToScene(GameScene())
if event.type == pygame.KEYUP:
print("You are hitting up!")
print(self.next)
def Update(self):
pass
def Render(self, screen):
# For the sake of brevity, the title scene is a blank red screen
print("where's the button")
screen.fill((255, 0, 0))
#Title Creation
myfont = pygame.font.SysFont(("Moyko"), 50)
textImage = myfont.render("Anime Pong", True, (0, 255, 0))
screen.blit(textImage, (100,100))
#Button Creation
#play_button = TextButton((50,50), (50,50), ((180, 20, 20)), "Play")
button_01 = Button("Great!", (60, 30), my_great_function)
#button_02 = Button("Boo!", (60, 70), my_fantastic_function, bg=(50, 200, 20))
def my_great_function():
print("Great! " * 5)
def my_fantastic_function():
print("Fantastic! " * 4)
class GameScene(SceneBase):
def __init__(self):
SceneBase.__init__(self)
def ProcessInput(self, events, pressed_keys):
pass
def Update(self):
pass
def Render(self, screen):
# The game scene is just a blank blue screen
screen.fill((0, 0, 255))
run_game(400, 300, 60, TitleScene())
This question already has an answer here:
How to detect when a rectangular object, image or sprite is clicked
(1 answer)
Closed 2 years ago.
Basically, here is a section of my code in Pygame:
button_text=pygame.font.Font("C:\Windows\Fonts\Another Danger - Demo.otf",35)
textSurface,textRect=text_objects("Start game",button_text)
textRect.center=(105,295)
screen.blit(textSurface,textRect)
This is the text I'd like to turn into a clickable format, so that when someone presses the text, it can run a function, such as running the next thing possible.
Any help would be greatly appreciated.
Thanks.
Get the rect from the surface that font.render returns and use it for collision detection and the blit position.
import sys
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
text_surface = font.render('text button', True, pg.Color('steelblue3'))
# Use this rect for collision detection with the mouse pos.
button_rect = text_surface.get_rect(topleft=(200, 200))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
# Use event.pos or pg.mouse.get_pos().
if button_rect.collidepoint(event.pos):
print('Button pressed.')
screen.fill((40, 60, 70))
screen.blit(text_surface, button_rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
PyGame does not have buttons, so what you can do here is get the mouse cursor position when clicking with pygame.mouse.get_pos(). If the mouse cursor position is inside the text then you know the text was selected.
Here is an example:
import pygame, sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((1000, 700))
clock = pygame.time.Clock()
tx, ty = 250, 250
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
mouse = pygame.mouse.get_pos()
if mouse[0]in range (tx, tx + 130) and mouse[1] in range (ty, ty + 20):
print("You clicked on the text.")
myfont = pygame.font.SysFont("Marlett", 35)
textsurface = myfont.render(("Start game"), True, (230, 230, 230))
screen.blit(textsurface,(tx, ty))
pygame.display.update()
clock.tick(60)
In this example, I used tx and ty for sizes but you can use rect, it's the same thing.
First off, this is a school assignment so I want to be upfront about that. Second I'm just asking for advice on the approach, possible help with the code. I'm working on a MSPAINT style clone using some pre-existing code from our book. The code already has the use of the draw.line when pressing mouse button 1. Teacher wants us to add ability to make circles or rectangles. I'm working on the circle part and I have figured out (thanks to the forums on here) how to implement what I wanted to do with the MOUSEBUTTONDOWN and MOUSEBUTTONUP events.. This has brought me to a new Question.. How would I blit then erase then blit a preview of the circle until it is the size the user wants and they release the MOUSEBUTTON and view the final blit...
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
elif event.type == pygame.MOUSEMOTION:
lineEnd = pygame.mouse.get_pos()
if pygame.mouse.get_pressed() == (1,0,0):
pygame.draw.line(background, drawColor, lineStart, lineEnd, lineWidth)
lineStart = lineEnd
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
circleStart = pygame.mouse.get_pos()
elif event.type == pygame.MOUSEBUTTONUP and event.button == 3:
circleEnd = pygame.mouse.get_pos()
size = (circleEnd[0] - circleStart[0])
pygame.draw.circle(background, drawColor, circleStart, size, lineWidth)
elif event.type == pygame.KEYDOWN:
myData = (event, background, drawColor, lineWidth, keepGoing)
myData = checkKeys(myData)
event, background, drawColor, lineWidth, keepGoing) = myData
Thanks so much
-Ben
So after some thinking this is the best solution I came up with using pygame. Tell me what you think and if it has helped you.
import pygame,sys,math #---- Import modules we will need
pygame.init() #---- Initialize the module
def get_rad(origin_x,origin_y,x,y): #----- Returns the appropriate radius
return math.sqrt((origin_x - x)**2 + (origin_y - y)**2) #----- Distance between 2
#----- points
screen = pygame.display.set_mode((400,400)) #----- Sets up the screen
clock = pygame.time.Clock() #------- Sets up the clock
mouse_button = 0 #--------- This variable is used to determine whether a mouse button
#--------- has been pressed
draw_final_circle = False #---------- This variable lets us know that we should draw the
#---------- final circle
while True: #------ main loop
clock.tick(60) #------ Limit the Fps
mouse_button0 = mouse_button #-------- This variable holds the previous value of
#-------- mouse_button(it will be useful later)
mouse_x,mouse_y = pygame.mouse.get_pos() #----- Get the mosue coordinates
for e in pygame.event.get(): #---- Cycle through events
if e.type == pygame.QUIT: pygame.quit();sys.exit() #--Quit when window is closed
if e.type == pygame.MOUSEBUTTONDOWN: #---- If the mouse button is pressed
if mouse_button == 0: #---- if the mouse button is released
mouse_button = 1 #----- set it to pressed basically
originx,originy = mouse_x,mouse_y #---- keep the mouse_x,mouse_y pos
if e.type == pygame.MOUSEBUTTONUP: #---- if the mouse button is released
if mouse_button == 1: #-------- if it is pressed
mouse_button = 0 #--------- set it to released
screen.fill((255,255,255)) #---- clear the screen
#-------- If a mouse button is pressed and a circle can be drawn (rad>width) ------#
if mouse_button == 1 and get_rad(originx,originy,mouse_x,mouse_y) > 1:
rad = int(get_rad(originx,originy,mouse_x,mouse_y)) #---- get the radius(as int)
pos = mouse_x,mouse_y
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #--- draw the circle
#----------------------------------------------------------------------------------#
#---------- if the button is released but in the previous loop it was pressed -----#
if mouse_button == 0 and mouse_button0 == 1:
draw_final_circle = True #----- set the final circle boolean to True
if draw_final_circle: #----- if the final circle is decided
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #---- keep drawing it
pygame.display.flip() #----- flip the buffer
I suggest you implement the different modes of your drawing program into different classes that represent the current mode and its state. This way, implementing different modes become very easy.
As for a circle drawing mode, you want to take a copy the screen surface when the user presses the mouse button, and blit that copy to the screen every frame.
Then draw your circle on that copy. This way, you basically "erase" the temporary circles.
Here's a simple example. Press SPACE to cycle between the different modes (draw, circle, rect) and TAB for different colors:
import pygame
from math import hypot
from itertools import cycle
from operator import itemgetter
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
colors = cycle(sorted(pygame.color.THECOLORS.iteritems(), key=itemgetter(0)))
color = next(colors)[1]
class DrawMode(object):
def __init__(self):
self.last = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.last = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.last = None
def draw(self, screen):
pos = pygame.mouse.get_pos()
if self.last:
pygame.draw.line(screen, color, self.last, pos)
self.last = pos
class ClickReleaseMode(object):
def __init__(self):
self.tmp = None
self.start = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.start = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.start = self.tmp = None
def draw(self, screen):
if not self.tmp:
self.tmp = screen.copy()
pos = pygame.mouse.get_pos()
screen.blit(self.tmp, (0,0))
if self.start:
self.do_draw(screen, pos)
class CircleMode(ClickReleaseMode):
def __init__(self):
super(CircleMode, self).__init__()
def do_draw(self, screen, pos):
r = hypot(pos[0] - self.start[0], pos[1] - self.start[1])
if r >= 2:
pygame.draw.circle(screen, color, self.start, int(r), 2)
class RectMode(ClickReleaseMode):#
def __init__(self):
super(RectMode, self).__init__()
def do_draw(self, screen, pos):
p = pos[0] - self.start[0], pos[1] - self.start[1]
pygame.draw.rect(screen, color, pygame.Rect(self.start, p), 2)
quit = False
modes = cycle((DrawMode, CircleMode, RectMode))
mode = next(modes)()
while not quit:
quit = pygame.event.get(pygame.QUIT)
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
mode = next(modes)()
print 'enter', mode.__class__.__name__
if e.key == pygame.K_TAB:
name, color = next(colors)
print 'changing color to', name, color
mode.handle(e)
mode.draw(screen)
pygame.display.flip()
clock.tick(60)