Pygame game loop trouble - python

I am a novice programming a game in the Python language using Pygame. I'm having a problem with my game loop. I can only run my game once then I can't make my game loop again. I will post my code below here:
This is my mouse based menu (Menu.py)
import pygame, os, sys
from pygame.locals import *
pygame.init()
def play_music(name, volume=0.7, loop=-1):
fullname = os.path.join('data', name)
try:
pygame.mixer.music.load(fullname)
pygame.mixer.music.set_volume(volume)
pygame.mixer.music.play(loop)
except:
raise SystemExit, "Can't load: " + fullname
def load_sound(name, volume=0.05):
fullname = os.path.join('data', name)
try:
sound = pygame.mixer.Sound(fullname)
sound.set_volume(volume)
except:
raise SystemExit, "Can't load: " + fullname
return sound
def run_game():
import Grab
def reset_highscore():
open("data/highscore.sav", "w").write(str(0))
print "Grab High score reset to 0"
class GrabMenu():
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.mouse.set_visible(0) #make the mouse cursor invisible
pygame.display.set_caption("Grab") #name the game
pygame.display.set_icon(pygame.image.load("data/Icon.png")) #set an icon for the game
screen = pygame.display.set_mode((800, 800)) #screen resolution width and height
background = pygame.Surface(screen.get_size()) #make the background
background = pygame.image.load("data/BG.png").convert() #convert background
background = pygame.transform.scale(background,(800, 800)) #scale the background down to the screen resulution
cursor = pygame.image.load("data/Hand1.png") #make a new cursor inside the game
WHITE = (255, 255, 255) # colors
YELLOW = (255, 216, 0)
running = True # value for main loop
clock = pygame.time.Clock()
font = pygame.font.SysFont("times new roman", 28, bold=True) # menu font and text.
fontcolor = WHITE
fontcolor2 = WHITE
fontcolor3 = WHITE
fontcolor4 = WHITE
mousetrigger = False
mouseclick = load_sound("Click.wav", 3.5) # mouse click
Menu1 = True
Menu2 = False #options menu
Menu3 = False #reset score menu
#JOYSTICK
try:
j = pygame.joystick.Joystick(0) # create a joystick instance
j.init() # init instance
print 'Enabled joystick: ' + j.get_name()
except:
pass
#main_loop
while running == True:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False, sys.exit()
if event.type == MOUSEBUTTONDOWN and event.button == 1: # if mouse button is pressed, (left mouse key only)
if Menu1:
if ren_r.collidepoint(pygame.mouse.get_pos()): # play the game
if Menu1 == False:
pass
else:
mouseclick.play()
run_game()
elif ren2_r.collidepoint(pygame.mouse.get_pos()): #options
if Menu1 == False:
pass
else:
mouseclick.play()
Menu1 = False
Menu2 = True
elif ren3_r.collidepoint(pygame.mouse.get_pos()): #how to play
if Menu1 == False:
pass
else:
mouseclick.play()
elif ren4_r.collidepoint(pygame.mouse.get_pos()): #quit the game
if Menu1 == False:
pass
else:
mouseclick.play()
running = False, pygame.quit(), sys.exit()
elif Menu2:
if ren5_r.collidepoint(pygame.mouse.get_pos()): #reset high score
mouseclick.play()
reset_highscore()
elif ren6_r.collidepoint(pygame.mouse.get_pos()): #go back
mouseclick.play()
Menu1 = True
Menu2 = False
screen.blit(background, (0, 0)) #draw the background
screen.blit(cursor, pygame.mouse.get_pos()) #set the cursor image as the new mouse cursor
if Menu1:
ren = font.render("Play", True, (fontcolor))
ren_r = ren.get_rect()
ren_r.x, ren_r.y = 340, 530
ren2 = font.render("Options", True, (fontcolor2))
ren2_r = ren2.get_rect()
ren2_r.x, ren2_r.y = 340, 590
ren3 = font.render("How to Play", True, (fontcolor3))
ren3_r = ren3.get_rect()
ren3_r.x, ren3_r.y = 340, 650
ren4 = font.render("Quit Game", True, (fontcolor4))
ren4_r = ren4.get_rect()
ren4_r.x, ren4_r.y = 340, 710
screen.blit(ren, (340, 530))
screen.blit(ren2, (340, 590))
screen.blit(ren3, (340, 650))
screen.blit(ren4, (340, 710))
if ren_r.collidepoint(pygame.mouse.get_pos()):
mousetrigger = True
fontcolor = YELLOW
else:
fontcolor = WHITE
if ren2_r.collidepoint(pygame.mouse.get_pos()):
mousetrigger = True
fontcolor2 = YELLOW
else:
fontcolor2 = WHITE
if ren3_r.collidepoint(pygame.mouse.get_pos()):
mousetrigger = True
fontcolor3 = YELLOW
else:
fontcolor3 = WHITE
if ren4_r.collidepoint(pygame.mouse.get_pos()):
mousetrigger = True
fontcolor4 = YELLOW
else:
fontcolor4 = WHITE
elif Menu2:
options = font.render("Options", 1, (WHITE))
ren5 = font.render("Reset High Score", True, (fontcolor))
ren5_r = ren5.get_rect()
ren5_r.x, ren5_r.y = 350, 650
ren6 = font.render("Go back", True, (fontcolor2))
ren6_r = ren6.get_rect()
ren6_r.x, ren6_r.y = 350, 700
screen.blit(options, (350, 600))
screen.blit(ren5, (350, 650))
screen.blit(ren6, (350, 700))
if ren5_r.collidepoint(pygame.mouse.get_pos()):
mousetrigger = True
fontcolor = YELLOW
else:
fontcolor = WHITE
if ren6_r.collidepoint(pygame.mouse.get_pos()):
mousetrigger = True
fontcolor2 = YELLOW
else:
fontcolor2 = WHITE
pygame.display.update()
furthermore I use this when I press on "Play" text on my menu:
def run_game():
import Grab
to start my main program "Grab.py":
import pygame, time, math, sys, random, os
from pygame.locals import *
pygame.init() #make pygame ready
pygame.joystick.init
#my game code
done = False
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.key == K_ESCAPE:
if highscore == 0:
pass
if highscore < get_highscore():
pass
else:
save_highscore(highscore)
if score == 0:
pass
else:
save_score(score)
save_level(level)
save_world(world)
done = True
pygame.mixer.music.stop()
Main game loop ends here and sends me back to my menu just like I want. However, when I try to import grab again from my menu then nothing happens. I went and searched for "Python import cycle" from Google and I discovered a few things but i'm still stuck. There must be an easier way to make Grab.py loop from this:
def run_game():
import Grab
Any help is very much appreciated.
Regards HJ.

Generally speaking, use import to load the definition of constants, functions or classes. Do not use it to run a bunch of statements. Call a function to run a bunch of statements.
For efficiency, Python only imports modules once. After it is loaded the first time, the module is cached in sys.modules. Every time Python encounters an import statement it check if the module is in sys.modules and uses the cached module if it exists. Only if the module is not in sys.modules does Python execute the code in the module. Thus the code in the module is never executed more than once. That is why import Grab does not execute the code in Grab.py the second time the import statement is reached.
So, instead, refactor Grab.py to put all the code you wish to run multiple times inside a function:
import pygame, time, math, sys, random, os
from pygame.locals import *
pygame.init() #make pygame ready
pygame.joystick.init() # <-- Note you need parentheses to *call* the init function
def main():
done = False
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.key == K_ESCAPE:
if highscore == 0:
pass
if highscore < get_highscore():
pass
else:
save_highscore(highscore)
if score == 0:
pass
else:
save_score(score)
save_level(level)
save_world(world)
done = True
pygame.mixer.music.stop()
and then change
import Grab
to
import Grab
Grab.main()
You could move import Grab to the top of Menu.py; generally speaking it is recommended to put all the import statements at the top of any Python script or module.

Related

Run 2 pygame windows with 2 different processes [duplicate]

I need to to build an application that has multiple windows. In one of these windows, I need to be able to play a simple game and another window has to display questions and get response from a user that influences the game.
(1) I was wanting to use pygame in order to make the game. Is there a simple way to have pygame operate with multiple windows?
(2) If there is no easy way to solve (1), is there a simple way to use some other python GUI structure that would allow for me to run pygame and another window simultaneously?
The short answer is no, creating two pygame windows in the same process is not possible (as of April 2015). If you want to run two windows with one process, you should look into pyglet or cocos2d.
An alternative, if you must use pygame, is to use inter-process communication. You can have two processes, each with a window. They will relay messages to each other using sockets. If you want to go this route, check out the socket tutorial here.
Internally set_mode() probably sets a pointer that represents the memory of a unique display. So if we write:
screenA = pygame.display.set_mode((500,480), 0, 32)
screenB = pygame.display.set_mode((500,480), 0, 32)
For instance we can do something like that later:
screenA.blit(background, (0,0))
screenB.blit(player, (100,100))
both blit() calls will blit on the same surface. screenA and screenB are pointing to the same memory address. Working with 2 windows is quite hard to achieve in pygame.
Yes, that is possible. SDL2 is able to open multiple windows. In the example folder you can take a look at "video.py".
https://github.com/pygame/pygame/blob/main/examples/video.py
"This example requires pygame 2 and SDL2. _sdl2 is experimental and will change."
I've been trying to do this for a few days now, and I'm finally making progress. Does this count as simple? Or even as "being in" pygame. In this exampe I never even call pygame.init() That just seems to get in the way. The event pump is running (for mouse and keyboard) but not all the normal events seem to be coming thru (FOCUSGAINED and LOST in particular). In this example each window renders it status (size, position, etc) to it's self. I also have versions where I mix SDL windows with the pygame display. But those involve encapsulating a Window rather than extending it.
In order to draw on these windows you can draw on a vanilla surface as usually and then use the Renderer associated with the window to create a texture that will update the the window. (texture.draw(), renderer.present). You dont't use display.update() or flip() because you you aren't using the pygame display surface.
The X11 package is just my experimental windowing stuff and has nothing to do with X11. I think all my imports are explicit so it should be easy to figure out what the missing pieces are.
from typing import List
from pygame import Rect, Surface, Color
import pygame.event
from pygame.event import Event
from pygame.freetype import Font
from pygame._sdl2.video import Window, Renderer, Texture
from X11.windows import DEFAULT_PAD, default_font, window_info
from X11.text import prt
class MyWindow(Window):
def __init__(self, font: Font=None):
super().__init__()
self._font = font if font else default_font()
self.resizable = True
self._renderer = None
def destroy(self) -> None:
super().destroy()
def update(self):
r = self.renderer
r.draw_color = Color('grey')
r.clear()
#self.render_line(f"TICKS: {pg.time.get_ticks()}", 5, size=16.0)
txt: List[str] = window_info(self)
self.render_text(txt, lineno=0)
r.present()
#property
def renderer(self):
if self._renderer is None:
try:
self._renderer = Renderer.from_window(self)
except:
self._renderer = Renderer(self)
return self._renderer
def render_text(self, txt: List[str], lineno: int=0):
for line in txt:
self.render_line(line, lineno, size=16.0)
lineno += 1
def render_line(self, txt: str, lineno: int = 0, size: float = 0.0):
font = self._font
line_spacing = font.get_sized_height(size) + DEFAULT_PAD
x = DEFAULT_PAD
y = DEFAULT_PAD + lineno * line_spacing
# compute the size of the message
src_rect = font.get_rect(txt, size=size)
# create a new surface (image) of text
l_surf = Surface((src_rect.width, src_rect.height))
src_rect = font.render_to(l_surf, (0, 0), txt, size=size)
# get ready to draw
texture = Texture.from_surface(self.renderer, l_surf)
dst = Rect(x, y, src_rect.width, src_rect.height)
texture.draw(None, dst)
_running: bool = False
def test():
global _running
win1 = MyWindow()
win2 = MyWindow()
my_windows = {win1.id: win1, win2.id: win2}
win = win1
rnd = win1.renderer
print("pygame.get_init():", pygame.get_init())
print("pygame.display.get_init():", pygame.display.get_init())
print("pygame.mouse.get_pos():", pygame.mouse.get_pos())
clock = pygame.time.Clock()
_running = True
while _running:
events = pygame.event.get()
for event in events:
if event.type != pygame.MOUSEMOTION:
print(event)
if event.type == pygame.QUIT:
_running = False
elif event.type == pygame.WINDOWENTER:
win = my_windows[event.window.id]
print(f"Enter Window ({event.window.id}")
elif event.type == pygame.WINDOWLEAVE:
pass
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
_running = False
if event.key == pygame.K_1:
win = my_windows[1]
rnd = win.renderer
if event.key == pygame.K_2:
win = my_windows[2]
rnd = win.renderer
elif event.key == pygame.K_b:
rnd.draw_color = Color('blue')
rnd.clear()
elif event.key == pygame.K_g:
rnd.draw_color = Color('grey')
rnd.clear()
elif event.key == pygame.K_t:
win.render_line("Hello, world")
elif event.key == pygame.K_s:
surface = pygame.display.get_surface()
print("surface: ", surface)
elif event.key == pygame.K_f:
pygame.display.flip()
# pygame.error: Display mode not set
elif event.key == pygame.K_u:
pygame.display.update()
# pygame.error: Display mode not set
for win in my_windows.values():
win.update()
clock.tick(40)
if __name__ == '__main__':
test()

Is there a way to get pygame to detect what color my cursor is on

I'm trying to make a game where it spawns another circle every time you click on a circle. And the error i'm getting is "TypeError: 'bool' object is not callable". I'm looking for a solution that doesn't completly change the code since i'm new and want to understand the code myself. But at this point i'll take any help.
import pygame
import random
import time
from pygame.math import Vector2
# Define some colors
BLACK = (0, 0, 0)
WHITE = (247, 247, 247)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (225,225,0)
tuple1 = (247, 247, 247, 255)
# Setup
pygame.init()
# Set the width and height of the screen [width,height]
surface = pygame.display.set_mode( (2560, 1440) )
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Hide the mouse cursor
pygame.mouse.set_visible(0)
# Speed in pixels per frame
x_speed = 0
y_speed = 0
# Current position
cursor = pygame.image.load('cursor.png').convert_alpha()
pygame.image.load("pattern.jpg")
background_image = pygame.image.load("pattern.jpg").convert()
circposy = 770
circposx = 1280
# -------- Main Program Loop -----------
while done ==False:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
done = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Drawing Code
surface.fill(WHITE)
# First, clear the screen to WHITE. Don't put other drawing commands
# above this, or they will be erased with this command.\
player_position = pygame.mouse.get_pos()
a = 0
b = 1
p=player_position[a]
o=player_position[b]
player_position = (p,o)
pygame.draw.circle(surface,RED,[circposx,circposy], 40)
tuple2 = surface.get_at(pygame.mouse.get_pos())
print (tuple2)
q = p - 2545
w = o - 2545
surface.blit( cursor, (q, w) )
a=0
result = tuple(map(int, tuple2)) > tuple1
print (result)
while event.type == pygame.MOUSEBUTTONDOWN:
done = True
if result == True():
a+1
surface.fill(WHITE)
pygame.draw.circle(surface,RED,[circposx + randint, circposy+randint],40)
print (a)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit frames per second
clock.tick(144)
# Close the window and quit.
pygame.quit()
cursor.png
The short answer is that the code is trying to call True(), which isn't a function.
while event.type == pygame.MOUSEBUTTONDOWN:
done = True
if result == True(): # <<-- HERE
a+1
surface.fill(WHITE)
pygame.draw.circle(surface,RED,[circposx + randint, circposy+randint],40)
Simply change this to True.
But you will also need to define what randint is a few lines lower. Did you mean random.randint( 0, 500 ) or suchlike? And fixing this leads to another error, because the surrounding loop to this code, is an infinite loop:
while event.type == pygame.MOUSEBUTTONDOWN: # <<-- HERE
done = True
if result == True:
a+1
surface.fill(WHITE)
rand_x = random.randint( 0, 500 )
rand_y = random.randint( 0, 500 )
pygame.draw.circle(surface,RED,[circposx + rand_x, circposy+rand_y],40)
Because there is no way event.type can ever change inside that loop. This should probably read:
if event.type == pygame.MOUSEBUTTONDOWN:
If I may make some suggestions:
Put all your event handling to a single place.
There's some doubling-up of event handling, and it would have prevented that infinite loop.
Move your screen dimensions into variables
SCREEN_WIDTH = 2560
SCREEN_HEIGHT = 1440
Instead of having constant numbers through the code (e.g.: 2545) make these functions of the screen size.
SCREEN_MARGIN = SCREEN_WIDTH - round( SCREEN_WIDTH * 0.10 )
q = p - SCREEN_MARGIN
If you want to detect what color your cursor is on, you can use pyautogui. Make sure you have pyautogui installed. type pip install pyautogui. If it doesn't install successfully, you already have it installed.
# Import everything
import pygame
import pyautogui
from pyautogui import *
# Initialize
pygame.init()
# Get mouse position
mouse_pos = pygame.mouse.get_pos()
x = mouse_pos[0]
y = mouse_pos[1]
# Get Color
r = pyautogui.pixel(x,y)[0]
g = pyautogui.pixel(x,y)[1]
b = pyautogui.pixel(x,y)[2]
color = [r,g,b]
Hopefully, you found this helpful!

Copying Function

I am making a game similar to Adventure Capitialist. The desired outcome is to have multiple "buy buttons". I have a function that creates one, but if I copy and paste, and change coordinates, it still doesn't work. I will put code from before I attempted to copy the function, because I think I was pretty far off from my desired outcome. The Function I want to copy is capitialistOne
import pygame, sys, shelve, pickle
import time as Time
from decimal import Decimal
pygame.init()
WHITE = (255,255,255)
BLACK = (0,0,0)
GREEN = (10, 200, 40)
RED = pygame.Color('red')
DISPLAYSURF = pygame.display.set_mode((460, 720))
clock = pygame.time.Clock()
logo = pygame.image.load('Logo.png')
menu = pygame.image.load('menu.png')
storeBoard = pygame.image.load('storeBoard.png')
loadingBar = pygame.image.load('loadingBar.png')
mainMenu = pygame.image.load('mainMenu.png')
Font1 = pygame.font.SysFont('monaco', 24)
Font2 = pygame.font.SysFont('monaco', 30)
cash = 5
barlength = 102 # the lenght of the growing bar
def buyDraw(amount, minxbuy, minybuy):
buySurface = Font1.render('{0}'.format(amount), True, BLACK)
buyRect = buySurface.get_rect()
buyRect.midtop = (85, minybuy)
DISPLAYSURF.blit(buySurface, buyRect)
def cashDraw(cash):
cashSurface = Font2.render(' ${0}'.format(cash), True, GREEN)
cashRect = cashSurface.get_rect()
cashRect.midtop = (387, 10)
DISPLAYSURF.blit(cashSurface, cashRect)
def capitalistOne(amount, cost, timez, gain, minxbuy, maxxbuy, minybuy, maxybuy, minxgain, maxxgain, minygain, maxygain, cash):
pygame.display.set_caption('capitalist')
buy_button = pygame.Rect(minxbuy, minybuy, maxxbuy, maxybuy)
gain_button = pygame.Rect(minxgain, minygain, maxxgain, maxygain)
menuRect = pygame.Rect(400, 680, 30, 30)
coefficient = maxxgain / timez #
time = 0
dt = 0
upgrade = amount * gain
loop = False
while True:
mouse_pressed = pygame.mouse.get_pressed()
mouse_pos = pygame.mouse.get_pos()
inc = time * coefficient
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#Return to menu
if menuRect.collidepoint(mouse_pos) and mouse_pressed[0]:
f = open('store.pckl', 'wb')
pickle.dump(amount, f)
f.close()
opening()
# Max LVL
if buy_button.collidepoint(mouse_pos) and mouse_pressed[0] and amount >= 1000:
maxLvlSurface = Font1.render('Max Lvl Reached', True, RED)
maxLvlRect = maxLvlSurface.get_rect()
maxLvlRect.midtop = (215, 5)
DISPLAYSURF.blit(maxLvlSurface, maxLvlRect)
pygame.display.flip()
Time.sleep(0.5)
# buy button
if buy_button.collidepoint(mouse_pos) and mouse_pressed[0] and cash >= cost and amount < 1000:
amount += 1
cash -= cost
upgrade = amount * gain
#Gain Button
if gain_button.collidepoint(mouse_pos) and mouse_pressed[0] and amount > 0:
loop = True # alows the user to click, then have the bar grow, rather than while they are clicking run it
if loop == True:
if time < timez: # if the bar isnt full, add to it
time += dt
if time >= timez: # if the bar is full, reset time, give cash.
cash += upgrade
time = 0
loop = False
# Draw everything
else:
DISPLAYSURF.blit(storeBoard, (0,0)) # Draw the background, icons, ect.
pygame.draw.rect(DISPLAYSURF, GREEN, (minxgain + 1, minygain, inc, maxygain)) # Draws a portion of the green bar
pygame.draw.rect(DISPLAYSURF, BLACK, gain_button, 2) # draws a border around the gain bar
DISPLAYSURF.blit(mainMenu, (400, 680))# draws a main menu button to return to main menu
buyDraw(amount, minxbuy, minybuy)# draws the buy button
# Should the cash be displayed in Sci Notation or in standar
if int(cash) < 1000000:
cashDraw(cash)
if int(cash) > 1000000:
SciNot = '%.2E' % Decimal(str(cash))
cashDraw(SciNot)
pygame.display.flip()
dt = clock.tick(60) / 1000
def opening():
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(logo, (155, 50))
DISPLAYSURF.blit(menu, (0 , 125))
saveRect = pygame.Rect(400, 680, 30, 30)
pygame.display.set_icon(logo)
pygame.display.flip()
f = open('store.pckl', 'rb')
gShovelAmount = pickle.load(f)
f.close()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
(x, y) = pygame.mouse.get_pos()
if x < 375 and x > 80 and y < 545 and y > 395:
capitalistOne(
amount=gShovelAmount, cost=5, timez=10, gain=5, minxbuy=21,
maxxbuy=41, minybuy=21, maxybuy=41, minxgain=120,
maxxgain=204, minygain=21, maxygain=41, cash=cash)
opening()
Here is a button that should suit your needs:
class Button(object):
global screen_width,screen_height,screen
def __init__(self,x,y,width,height,text_color,background_color,text):
self.rect=pygame.Rect(x,y,width,height)
self.x=x
self.y=y
self.width=width
self.height=height
self.text=text
self.text_color=text_color
self.background_color=background_color
def check(self):
return self.rect.collidepoint(pygame.mouse.get_pos())
def draw(self):
pygame.draw.rect(screen, self.background_color,(self.rect),0)
drawTextcenter(self.text,font,screen,self.x+self.width/2,self.y+self.height/2,self.text_color)
pygame.draw.rect(screen,self.text_color,self.rect,3)
Implemented into the main loop:
button=Button(x,y,width,height,text_color,background_color,text)
while not done:
for event in pygame.event.get():
if event.type==QUIT:
terminate()
elif event.type==pygame.MOUSEBUTTONDOWN:
if dodger_button.check():
#what to do when button is pressed
#fill screen with background
screen.fill(background)
button.draw()
pygame.display.flip()
clock.tick(fps)

Raspberry Pi sending same data?

So I am using Pygame to create a display for an quadrature encoder counter. I am using EzText to prompt a user input on the display where I can change the maximum count and send updated value via UART to the microcontroller that is in charge of doing the actual counting. Here is my code (sorry for the length, I marked the part that deals with the user input towards the bottom):
import sys, serial, string, pygame, eztext
import pygame.locals as GAME_GLOBALS
import pygame.event as GAME_EVENTS
windowWidth = 1680
windowHeight = 1050
maxCount New = 32767
posDataStr = " "
rect1 = (1000,350,290,75)
rect2 = (1000,555,290,75)
black = (0,0,0)
white = (248,248,255_
pygame.init()
infoObject = pygame.display.Info()
surface = pygame.display.set_mode((windowWidth,windowHeight), pygame.FULLSCREEN)
background = pygame.image.load("/home/pi/MyProjects/background.png")
myFont = pygame.font.SysFont("monospace",96,1)
surface.blit(background, (0,0))
pygame.mouse.set_visible(0) #hide mouse pointer
def quitGame():
pygame.quit()
sys.exit()
DEVICE = "/dev/ttyAMA0"
ser = serial.Serial(DEVICE, 19200)
#draw Max Count rectangles
pygame.draw.rect(surface, (black), (995,550,300,85))
pygame.draw.rect(surface, (white), (1000,555,290,75))
# draw current count background (black) rectangle (since it doesn't need to update)
pygame.draw.rect(surface, (black), (995,345,300,85))
#draw maxCountNew as text
maxCountText = myFont.render(str(maxCountNew), 1, (black))
surface.blit(maxCountText, (1000,545))
pygame.display.update()
while True:
posData = [0,0,0,0,0,0,0]
i = ser.read()
if i == '*':
for x in range (0,6):
posData[x] = ser.read()
if posData[x] == ',' or posData[x] == '\00'
del posData[x:]
posDataStr = ''.join(posData)
break
#draw posDataStr rectangles
pygame.draw.rect(surface, (white), (1000,350,290,75))
#draw currentCountStr as text
currentCountText = myFont.render(posDataStr,1,(black))
surface.blit(currentCountText, (1000,340))
for event in GAME_EVENTS.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
quitGame()
###################################################################################################
#THIS IS THE PROBLEMATIC SECTION (I think)
if event.key == pygame.K_r:
txtbx = eztext.Input(maxlength=5,color=black,prompt='Enter New Resolution: ')
while True:
events = pygame.event.get()
#blit txtbx on the screen
txtbx.draw(surface)
#update txtbx
txtbx.update(events)
#refresh the display
pygame.display.flip()
if event.key == pygame.K_SPACE:
break
if len(txtbx.value) > 4: #once 5 digits are entered
break
newMax = txtbx.value
#write new position to MCU
ser.write(newMax.encode())
print newMax.encode() //for diagnostic purposes
#max count is resolution - 1
maxCountNewInt = int(newMax) - 1
#convert back to a string
maxCountNew = str(maxCountNewInt)
#refresh the display
surface.blit(background, (0,0))
pygame.draw.rect(surface,(white),(1000,555,290,75))
maxCountText = myFont.render(maxCountNew, 1, (black)) #display new max count txt
surface.blit(maxCountText, (1000,545))
pygame.display.update()
pygame.display.update(rect1)
pygame.display.update(rect2)
When I enter a value using the EzText prompt, the value entered is saved correctly into the newMax variable. The first time I send the data to my microcontroller, I get the correct value. On subsequent inputs on the RasPi, the newMax variable is adjusted correctly but the same value as the first time is sent to the microcontroller. Am I using ser.write() incorrectly?
Can you not just do txtbx.value = ""?

text not showing up dynamically, pygame

The code below is supposed to create a green button that makes a score text appear. unfortunately the button does nothing, and the only way I've managed to get it to work is by putting the function call for makeText in the while loop instead of in the clickButton function, but if I do that it's no longer dynamic. Can someone explain why the text isn't showing up when I press the button and fix my code so it does show up?
import pygame
import sys
#game stuff
pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()
#functions
def makeText(title,text,posx,posy):
font=pygame.font.Font(None,30)
scoretext=font.render(str(title)+ ": " +str(text), 1,(0,0,0))
screen.blit(scoretext, (posx, posy))
def clickButton(name,x,y,width,height):
if x + width > cur[0] > x and y + height > cur[1] > y:
if click == (1,0,0):
makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))
while True:
screen.fill((255,255,255))
screen.fill((55,155,0), button1)
#update display
pygame.display.update()
clock.tick(60)
#event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
cur = event.pos
click = pygame.mouse.get_pressed()
clickButton("button1",button1.left,button1.top,button1.width,button1.height)
The problem is that once you created the text, your main loop keeps going and calls screen.fill, overdrawing the text even before pygame.display.update() is called.
You could change it to:
...
def clickButton(name,x,y,width,height):
print x + width > cur[0] > x and y + height > cur[1] > y
if x + width > cur[0] > x and y + height > cur[1] > y:
if click == (1,0,0):
makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))
while True:
screen.fill((255,255,255))
screen.fill((55,155,0), button1)
#event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
cur = event.pos
click = pygame.mouse.get_pressed()
clickButton("button1",button1.left,button1.top,button1.width,button1.height)
...
so the text is created after filling the screen with the background color and before pygame.display.update() is called, but that does not solve the problem of the screen being filled again the next iteration of the while loop.
So the solution is to keep track of the fact that the button was pressed, a.k.a. keeping track of a state.
Here's an example of a different approach, using classes for the buttons and a dict for the global state (so you don't need global variables, which should you avoid most of the time, because it can get very confusing fast if your game starts becoming more complex).
Click the first button to show or hide the score, and click the second button to change the background color and earn 100 points.
See how easy it becomes to create new buttons; it's just adding a simple function.
import pygame
import sys
import random
pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()
# create font only once
font = pygame.font.Font(None,30)
# it's always a good idea to cache all text surfaces, since calling 'Font.render' is
# an expensive function. You'll start to notice once your game becomes more complex
# and uses more text. Also, use python naming conventions
text_cache = {}
def make_text(title, text):
key = "{title}: {text}".format(title=title, text=text)
if not key in text_cache:
text = font.render(key, 1,(0,0,0))
text_cache[key] = text
return text
else:
return text_cache[key]
# we use the 'Sprite' class because that makes drawing easy
class Button(pygame.sprite.Sprite):
def __init__(self, rect, color, on_click):
pygame.sprite.Sprite.__init__(self)
self.rect = rect
self.image = pygame.Surface((rect.w, rect.h))
self.image.fill(color)
self.on_click = on_click
# this happens when the first button is pressed
def toggle_score_handler(state):
state['show_score'] = not state['show_score']
# this happens when the second button is pressed
def toggle_backcolor_handler(state):
state['backcolor'] = random.choice(pygame.color.THECOLORS.values())
state['score'] += 100
# here we create the buttons and keep them in a 'Group'
buttons = pygame.sprite.Group(Button(pygame.Rect(30, 30, 32, 32), (55, 155 ,0), toggle_score_handler),
Button(pygame.Rect(250, 250, 32, 32), (155, 0, 55), toggle_backcolor_handler))
# here's our game state. In a real
# game you probably have a custom class
state = {'show_score': False,
'score': 0,
'backcolor': pygame.color.Color('White')}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
# you can check for the first mouse button with 'event.button == 1'
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
# to check if the mouse is inside the button, you
# can simple use the 'Rect.collidepoint' function
for button in (b for b in buttons if b.rect.collidepoint(event.pos)):
button.on_click(state)
screen.fill(state['backcolor'])
# draw all buttons by simple calling 'Group.draw'
buttons.draw(screen)
if state['show_score']:
screen.blit(make_text("score", state['score']), (100, 30))
pygame.display.update()
clock.tick(60)
You are checking the value of "click" in the clickButton function, but I don't see click defined anywhere that clickButton would have access to it.
Perhaps you should pass click as an argument in the clickButton function, which would then possibly make the if condition true?

Categories