Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Here is all the code, but in main(), a while loop checks if on_title_screen is true, and if it is, displays the title screen, but if not, displays the game. However, after starting the program, running the game, and returning to the title screen, attempting to press the start button makes both the code for if on_title_screen==True and elif on_title_screen==False run when only the first bit should run.
import random
import pygame
from pygame import *
import math
import sys
#Presets for window
size=width,height=500,500
Ag=-9.80665
clock = pygame.time.Clock()
white=(255,255,255)
blue=(0,0,255)
red=(255,0,0)
gray_bgColor=(190,193,212)
#Initialise pygame Surface as screen
pygame.init()
pygame.font.init()
screen=pygame.display.set_mode(size)
pygame.display.set_caption("Flappy Bird Replica")
def falling_loop():
for event in pygame.event.get():
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_UP:
preset.vY=-10
if preset.yPos>height-50:
preset.on_title_screen=True
preset.vY+=1
preset.yPos+=preset.vY
class presets():
#Holds all the "global" values for the game
vY=0
xPos,yPos=200,100
on_title_screen=True
class graphics():
#Holds the methods for loading/displaying graphics
def load_images(self):
#Loads the background and sprite images
self.background_image=pygame.image.load("flappy_background.png").convert()
self.bird_image=pygame.image.load("flappy_sprite.jpg").convert()
self.bird_image.set_colorkey(white)
self.birdHitBox=self.bird_image.get_rect()
def show_background(self):
#blits the background
screen.blit(self.background_image,[0,0])
def show_bird(self):
#blits the bird onto screen at xPos, yPos
screen.blit(self.bird_image,[preset.xPos,preset.yPos])
def refresh_display(self):
#updates the display
screen.blit(self.background_image,[0,0])
falling_loop()
self.show_bird()
class titleScreen():
#Holds the methods for the title screen/menu
def title(self):
#Sets up the title
titleText="Flappy Game"
titlePos=(0,0)
currentFont=pygame.font.SysFont("arialms",30,bold=True,italic=True)
renderTitle=currentFont.render(titleText,1,blue,gray_bgColor)
self.titlex,self.titley=currentFont.size(titleText)
screen.blit(renderTitle,titlePos)
def start(self):
#Sets up the start Button
startText="Start Game"
self.startPos=(0,self.titley)
currentFont=pygame.font.SysFont("arialms",25,bold=False,italic=False)
renderStart=currentFont.render(startText,1,blue,gray_bgColor)
self.startx,self.starty=currentFont.size(startText)
self.start_rect = pygame.Rect(self.startPos[0],self.titley,self.startx,self.starty)
screen.blit(renderStart,self.startPos)
def quit(self):
#Sets up the quit button
quitText="Quit"
self.quitPos=(0,self.starty+self.titley)
currentFont=pygame.font.SysFont("arialms",25,bold=False,italic=False)
renderQuit=currentFont.render(quitText,1,red,gray_bgColor)
self.quitx,self.quity=currentFont.size(quitText)
self.quit_rect = pygame.Rect(self.quitPos[0],self.titley+self.starty,self.quitx,self.quity)
screen.blit(renderQuit,self.quitPos)
def get_click(self):
#Gets mouse click and processes outcomes
for event in pygame.event.get():
if event.type==pygame.MOUSEBUTTONDOWN:
x,y=pygame.mouse.get_pos()
#Tests for start:
if self.start_rect.collidepoint(x,y):
print("start")
preset.on_title_screen=False
graphicsC.show_background()
elif self.quit_rect.collidepoint(x,y):
print("quit")
sys.exit()
#Assign objects to respective classes
preset=presets()
titleC=titleScreen()
graphicsC=graphics()
def setupTitle():
#bundles all title_screen functions
titleC.title()
titleC.start()
titleC.quit()
def main():
graphicsC.load_images()
graphicsC.show_background()
setupTitle()
while True:
clock.tick(30)
if preset.on_title_screen==False:
graphicsC.refresh_display()
print("working...")
elif preset.on_title_screen==True:
setupTitle()
titleC.get_click()
pygame.display.flip()
main()
You have to use self.xxx to make class attributes. You also need to make sure those values are defined when initializing the class using the __init__ method. So use:
class presets():
#Holds all the "global" values for the game
def __init__(self):
self.vY=0
self.xPos,self.yPos=200,100
self.on_title_screen=True
However, making 'global' variables this way probably isn't the best way, as Cyber mentions in the comments.
Related
I want to create multiple small games in pygame zero, with a main window on which you have buttons to click - when clicking a button it would start a new window and a new subgame (coded on its own in another file). The secondary games should be able to return if the game was won or lost. Is this possible using pygame-zero? I am thinking the subgame should be encapsulated in some function to be able to give a return value, but I am not sure if this is doable in pygame-zero since it calls some functions itself... any idea?
Or am I better off adding some kind of game state in the main program and do everything there like this?
def update():
if state == 'game1':
#all of the game1 update code here
elif state == 'game2':
#all of the game2 update code here
#etc
def draw():
if state == 'game1':
#all of the game1 draw code here
elif state == 'game2':
#all of the game2 draw code here
#etc
I think you are definitely better off by implementing a game state. I'd use an object-oriented approach like:
class GameState:
def update():
pass
def draw():
pass
class Game1(GameState):
def update():
# ...
def draw():
# ...
class GameChooser(GameState):
def update():
# choose game here
global state
if ...:
state = Game1()
def draw():
# ...
state = GameChooser()
def update():
state.update()
def draw():
state.draw()
The pen size stays the same size when I press the - and + keys, as I draw with turtle.
I reworked the problem using some plausible answers, but to no avail. I looked on the internet for a similar solution and came up empty-handed.
import turtle
turtle.setup(400,500) # Determine the window size
wn = turtle.Screen() # Get a reference to the window
wn.title("Handling keypresses!") # Change the window title
wn.bgcolor("lightgreen") # Set the background color
tess = turtle.Turtle() # Create our favorite turtle
# The next four functions are our "event handlers".
def h1():
tess.forward(30)
def h2():
tess.left(45)
def h3():
tess.right(45)
def h4():
tess.color("red")
def h5():
tess.color("green")
def h6():
tess.color("blue")
def h7():
tess.pensize(0)
def h8():
tess.pensize(20)
def h9():
wn.bye() # Close down the turtle window
def h10():
tess.backward(30)
# These lines "wire up" keypresses to the handlers we've defined.
wn.onkey(h1, "Up")
wn.onkey(h2, "Left")
wn.onkey(h3, "Right")
wn.onkey(h4, "r")
wn.onkey(h5, "g")
wn.onkey(h6, "b")
wn.onkey(h7, "-")
wn.onkey(h8, "+")
wn.onkey(h9, "q")
wn.onkey(h10, "Down")
# Now we need to tell the window to start listening for events,
# If any of the keys that we're monitoring is pressed, its
# handler will be called.
wn.listen()
wn.mainloop()
I'm trying to use the .pensize() method in turtle to increase/decrease its thickness between a restricted range of 0 and 20 using the - and + keys by function with the .onkey() method. Any help is appreciated.
You don't need a global variable to track the pen size. Turtles are already (effectively) global entities and know their own pen size (tested code):
def h7():
pensize = tess.pensize() - 1
if pensize >= 0:
tess.pensize(pensize)
def h8():
pensize = tess.pensize() + 1
if pensize <= 20:
tess.pensize(pensize)
However, there's another issue that will keep this, or any solution, from working correctly. This code:
wn.onkey(h7, "-")
wn.onkey(h8, "+")
Needs to instead be:
wn.onkey(h7, "minus")
wn.onkey(h8, "plus")
Otherwise, the "-" symbol will cause all unassigned keys (including shift key needed to type "+") to invoke the h7() handler! This change should also allow the equivalent keys on the keypad to work. Complete code:
from turtle import Screen, Turtle
wn = Screen() # Get a reference to the window
wn.setup(400, 500) # Determine the window size
wn.title("Handling keypresses!") # Change the window title
wn.bgcolor("lightgreen") # Set the background color
tess = Turtle() # Create our favorite turtle
# The next nine functions are our "event handlers".
def h1():
tess.forward(30)
def h2():
tess.left(45)
def h3():
tess.right(45)
def h4():
tess.color("red")
def h5():
tess.color("green")
def h6():
tess.color("blue")
def h7():
pensize = tess.pensize() - 1
if pensize >= 0:
tess.pensize(pensize)
def h8():
pensize = tess.pensize() + 1
if pensize <= 20:
tess.pensize(pensize)
def h9():
tess.backward(30)
# These lines "wire up" keypresses to the handlers we've defined.
wn.onkey(h1, "Up")
wn.onkey(h2, "Left")
wn.onkey(h3, "Right")
wn.onkey(h4, "r")
wn.onkey(h5, "g")
wn.onkey(h6, "b")
wn.onkey(h7, "minus")
wn.onkey(h8, "plus")
wn.onkey(h9, "Down")
wn.onkey(wn.bye, "q") # Close down the turtle window
# Now we need to tell the window to start listening for events,
# If any of the keys that we're monitoring is pressed, its
# handler will be called.
wn.listen()
wn.mainloop()
I have been working with creating some code that I can use in future in order to embed a pygame window within a tkinter window in order to make use of tkinter menus and buttons. I am currently having some issues with dealing with key presses. i want all key presses to be dealt with by pygame rather than tkinter so that if the pygame element is made fullscreen (thus meaning tkinter is not used) then tkinter key bindings are left ignored.
My problem is that when the window is initially opened (or after it has been clicked off and back on again), only tkinter is registering key bindings. Once the user clicks on the pygame window, only pygame registers key bindings. My question is how can I detect whether tkinter or pygame is detecting the key presses and also how can I make it so that pygame detects the presses rather than tkinter when I have detected it?
My code is below (sorry it's quite long)
import pygame, os, _tkinter, sys
try:
import Tkinter as tk
BOTH,LEFT,RIGHT,TOP,BOTTOM,X,Y = tk.BOTH,tk.LEFT,tk.RIGHT,tk.TOP,tk.BOTTOM,tk.X,tk.Y
two = True
except ImportError:
import tkinter as tk
from tkinter.constants import *
two = False
from pygame.locals import *
class PygameWindow(tk.Frame):
""" Object for creating a pygame window embedded within a tkinter window.
Please note: Because pygame only supports a single window, if more than one
instance of this class is created then updating the screen on one will update
all of the windows.
"""
def __init__(self, pygame_size, pygame_side, master=None, **kwargs):
"""
Parameters:
pygame_size - tuple - The initial size of the pygame screen
pygame_side - string - A direction to pack the pygame window to
master - The window's master (often a tk.Tk() instance
pygame_minsize - tuple - The minimum size of the pygame window.
If none is specified no restrictions are placed
pygame_maxsize - tuple - The maximum size of the pygame window.
If none is specified no restrictions are placed.
Note: This includes the pygame screen even when fullscreen.
tkwin - string - A direction to pack a tkinter frame to designed to be
used to contain widgets to interact with the pygame window.
If none is specified the frame is not included.
fullscreen - boolean - Whether fullscreen should be allowed
menu - boolean - Whether a menu bar should be included.
the menu bar contains a File menu with quit option and an Options
menu with fullscreen option (If enabled)
"""
# I have decided to use a global variable here because pygame only supports a single screen,
# this should limit confusion if multiple instances of the class are created because each
# instance will always contain the same screen. A global variable should hopefully make this
# clearer than screen being a seperate attribute for each instance
global screen
self.master = master
self.fullscreen = tk.BooleanVar(value=False)
if two:
tk.Frame.__init__(self,master)
else:
super().__init__(self,master)
self.pack(fill=BOTH,expand=1)
if 'pygame_minsize' in kwargs:
w,h = kwargs['pygame_minsize']
master.minsize(w,h)
del kwargs['pygame_minsize']
w,h = pygame_size
self.embed = tk.Frame(self, width = w, height = h)
self.embed.pack(side=pygame_side,fill=BOTH,expand=1)
if 'tkwin' in kwargs:
if kwargs['tkwin'] != None:
self.tk_frame = tk.Frame(self,bg='purple')
if kwargs['tkwin'] in [TOP,BOTTOM]:
self.tk_frame.pack(side=kwargs['tkwin'],fill=X)
elif kwargs['tkwin'] in [LEFT,RIGHT]:
self.tk_frame.pack(side=kwargs['tkwin'],fill=Y)
else:
raise ValueError('Invalid value for tkwin: "%r"' %kwargs['tkwin'])
del kwargs['tkwin']
if 'fullscreen' in kwargs:
if kwargs['fullscreen']:
self.fs_okay = True
else:
self.fs_okay = False
else:
self.fs_okay = False
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
if sys.platform == "win32":
os.environ['SDL_VIDEODRIVER'] = 'windib'
pygame.display.init()
if 'pygame_maxsize' in kwargs:
w,h = kwargs['pygame_maxsize']
self.pygame_maxsize = (w,h)
screen = pygame.display.set_mode((w,h),RESIZABLE)
del kwargs['pygame_maxsize']
else:
screen = pygame.display.set_mode((0,0),RESIZABLE)
self.pygame_maxsize = (0,0)
screen.fill((255,255,255))
if 'menu' in kwargs:
if kwargs['menu']:
self.menubar = tk.Menu(self.master)
self.master.config(menu=self.menubar)
self.filemenu = tk.Menu(self.menubar,tearoff=0)
self.filemenu.add_command(label='Quit',command=self.close,accelerator='Ctrl+Q')
self.menubar.add_cascade(label='File',menu=self.filemenu)
self.optionmenu = tk.Menu(self.menubar,tearoff=0)
if self.fs_okay:
self.optionmenu.add_checkbutton(label='Fullscreen',command=self.updatefs,variable=self.fullscreen,accelerator='F11')
self.menubar.add_cascade(label='Options',menu=self.optionmenu)
def update(self):
""" Update the both the contents of the pygame screen and
the tkinter window. This should be called every frame.
"""
pressed = pygame.key.get_pressed()
if self.fullscreen.get():
if pressed[K_ESCAPE] or pressed[K_F11] or not pygame.display.get_active():
self.togglefs()
else:
if pressed[K_q] and (pressed[K_LCTRL] or pressed[K_RCTRL]):
self.close()
for event in pygame.event.get(KEYDOWN):
if event.key == K_F11:
self.togglefs()
pygame.event.post(event)
pygame.event.pump()
pygame.display.flip()
self.master.update()
def close(self,*args):
""" Closes the open window."""
self.master.destroy()
def togglefs(self,*args):
"""Toggles the self.fullscreen variable and then calls
the updatefs function.
"""
self.fullscreen.set(not self.fullscreen.get())
self.updatefs()
def updatefs(self):
"""Updates whether the window is fullscreen mode or not
dependent on the value of the fullscreen attribute.
"""
if not self.fs_okay:
self.fullscreen.set(False)
global screen
tmp = screen.convert()
cursor = pygame.mouse.get_cursor()
flags = screen.get_flags()
bits = screen.get_bitsize()
if self.fullscreen.get():
pygame.display.quit()
del os.environ['SDL_WINDOWID']
if sys.platform == "win32":
del os.environ['SDL_VIDEODRIVER']
pygame.display.init()
screen = pygame.display.set_mode(self.pygame_maxsize,FULLSCREEN|(flags&~RESIZABLE),bits)
else:
pygame.display.quit()
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
if sys.platform == "win32":
os.environ['SDL_VIDEODRIVER'] = 'windib'
pygame.display.init()
screen = pygame.display.set_mode(self.pygame_maxsize,RESIZABLE|(flags&~FULLSCREEN),bits)
screen.blit(tmp,(0,0))
pygame.mouse.set_cursor(*cursor)
class TestWindow(PygameWindow):
def __init__(self, pygame_size, pygame_side, master=None, **kwargs):
if two:
PygameWindow.__init__(self,pygame_size, pygame_side, master=master, **kwargs)
else:
super().__init__(self,pygame_size, pygame_side, master=master, **kwargs)
self.drawn = False
self.button1 = tk.Button(self.tk_frame,text = 'Draw', command=self.draw)
self.button1.pack(side=LEFT)
screen.fill((255,255,255))
pygame.display.flip()
def draw(self):
if not self.drawn:
pygame.draw.circle(screen, (0,255,175), (250,250), 125)
else:
screen.fill((255,255,255))
self.drawn = not self.drawn
if __name__ == '__main__':
root = tk.Tk()
window = TestWindow((500,500),LEFT,root,pygame_minsize=(500,500),tkwin=LEFT,menu=True,fullscreen=True)
while True:
try:
window.update()
except _tkinter.TclError:
break
quit()
If there is no direct solution (of which I do not know), you could make a handler that passes the keypresses detected in tkinter to pygame.
The idea is to bind the keys to dispatch to a dispatch_event_to_pygame function, which will create a corresponding pygame.event.Event object, and to inject the latter into pygame's event loop, through the pygame.event.post function.
First, I define a dictionary that establishes the correspondence between the keys I want to dispatch from tkinter to pygame, and the corresponding symbols in pygame:
tkinter_to_pygame = {
'Down': pygame.K_DOWN,
'Up': pygame.K_UP,
'Left': pygame.K_LEFT,
'Right': pygame.K_RIGHT}
Then, I define a dispatch_event_to_pygame function, that takes a tkinter event, creates a corresponding pygame event, and posts it:
def dispatch_event_to_pygame(tkEvent):
if tkEvent.keysym in tkinter_to_pygame:
pgEvent = pygame.event.Event(pygame.KEYDOWN,
{'key': tkinter_to_pygame[tkEvent.keysym]})
pygame.event.post(pgEvent)
Finally, I bind on the root widget all the keys that I want to dispatch to pygame:
for key in tkinter_to_pygame:
root.bind("<{}>".format(key), dispatch_event_to_pygame)
References for the key names:
tkinter
pygame
I was looking for a solution to play mp3 files in python and many stackoverflow answers (to other questions) seemed to recommend pyglet. I am writing a program that takes a piece of text, breaks it into individual words and then downloads mp3s of those words (if they aren't already downloaded) using gTTs and plays them.
from pyglet import media, app, clock
from gtts import gTTS
import os
import time
from num2words import num2words
cwd = os.getcwd()
beep = media.load('beep.mp3', streaming = False)
def get_mp3(text):
player = media.Player()
lowertext = text.lower()
words = lowertext.split()
player.queue(beep)
for word in words:
save_path = cwd + '\\tts_downloads\\{}.mp3'.format(word)
if os.path.isfile(save_path) == False:
tts = gTTS(word, 'en-us')
tts.save(save_path)
mp3 = media.load(save_path)
player.queue(mp3)
player.queue(beep)
player.play()
app.run()
However I find that after playing the files pyglet won't let my program progress. How can I exit the pyglet app after playback has finished, so that my code can progress?
Alternatively is there some other way that I can play mp3 files in python?
This is souly because app.run() is a never-ending loop in order to keep the GL context alive. There's only one way around this and that is to create your own class that inherits the Pyglet properties.
I'll give you a sample code and if you have any questions feel free to ask away.
import pyglet
from pyglet.gl import *
# Optional audio outputs (Linux examples):
# pyglet.options['audio'] = ('alsa', 'openal', 'silent')
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False)
self.x, self.y = 0, 0
self.bg = pyglet.sprite.Sprite(pyglet.image.load('background.jpg'))
self.sprites = {}
self.player = pyglet.media.Player()
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
# Do something when a key is pressed?
# Pause the audio for instance?
# use `if symbol == key.SPACE: ...`
# This is just an example of how you could load the audio.
# You could also do a standard input() call and enter a string
# on the command line.
if symbol == key.ENTER:
self.player.queue(media.load('beep.mp3', streaming = False))
if nog self.player.playing:
self.player.play()
if symbol == key.ESC: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.bg.draw()
# self.sprites is a dictionary where you store sprites
# to be rendered, if you have any.
for sprite_name, sprite in self.sprites.items():
sprite.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
self.player.delete() # Free resources. (Not really needed but as an example)
x = main()
x.run()
Now this is just the most basic example of how to load audio sources and play them. You press Enter and that triggers beep.mp3.
Normally, you'd also want to hook a function to self.player.eos() that does something when you run out of sources.
Also note that it only calls play() once if it's not already playing. These are attributes you want to honor.
Ah and Escape exits the application.
I have a program where the user controls a block and navigates through pipes, similar to the game flappy bird. I want to know if tkinter has a way of telling me if one rectangle touches or hits another. I know there is a find_overlapping method for the canvas, but when i use find_overlapping on the pipe's rectangle it gives me the id of the pipe! I want to know if canvas items have a way of knowing if another canvas item is touching it!
Thanks
Here is my code:
from Tkinter import *
from random import *
root=Tk()
c=Canvas(root,width=600,height=600)
speed=20
num=1
first=True
b=0
pipes=[]
done=False
class Pipe():
def __init__(self,canvas,pipes,length,width=75,color="Green",position=600,speed=5):
self.speed=speed
self.canvas=canvas
self.length=length
self.width=width
self.color=color
self.position=position
self.current1=self.canvas.create_rectangle((self.position,0,self.position+self.width,self.length),outline=self.color,fill=self.color)
self.current2=self.canvas.create_rectangle((self.position,600,self.position+self.width,self.length+150),outline=self.color,fill=self.color)
self.pipes=pipes
self.pipes.append(self.current1)
self.pipes.append(self.current2)
def move(self):
global done
if (len(self.canvas.find_overlapping(self.position,0,self.position+self.width,self.length))==1 and len(self.canvas.find_overlapping(self.position,600,self.position+self.width,self.length+150))==1) and not done:
self.position-=3
self.canvas.coords(self.current1,(self.position,0,self.position+self.width,self.length))
self.canvas.coords(self.current2,(self.position,600,self.position+self.width,self.length+150))
if self.position>-75:
self.canvas.after(self.speed,self.move)
else:
self.pipes.remove(self.current1)
self.pipes.remove(self.current2)
self.canvas.delete(self.current1)
self.canvas.delete(self.current2)
else:
print self.canvas.find_overlapping(self.position,0,self.position+self.width,self.length)
print
print self.canvas.find_overlapping(self.position,600,self.position+self.width,self.length+150)
done=True
class Player():
def __init__(self,canvas,x=150,y=300,size=40):
self.size=size
self.faller=True
self.x=x
self.y=y
self.fell=5
self.canvas=canvas
#For now
self.current=self.canvas.create_rectangle((self.x-20,self.y-20,self.x+20,self.y+20),tags="user",outline="Blue",fill="Blue")
self.canvas.after(100,self.fall)
self.canvas.bind("<1>",self.jump)
def fall(self):
global done
if self.faller and not done:
self.y+=self.fell
self.fell*=1.001
self.canvas.coords(self.current,(self.x-20,self.y-20,self.x+20,self.y+20))
self.canvas.after(30,self.fall)
elif done:
a=600-self.y+20
a/=50
while self.y<580:
self.y+=a
self.canvas.coords(self.current,(self.x-20,self.y-20,self.x+20,self.y+20))
def jump(self,e):
if not done:
self.faller=False
for x in range(10):
self.canvas.after(100,self.move)
self.faller=True
self.fell=5
def move(self):
self.y-=7.5
self.canvas.coords(self.current,(self.x-20,self.y-20,self.x+20,self.y+20))
def changey(self,a):
self.y=a
def run():
global b,first,done
if not done:
if first:
b=randint(5,450)
first=False
else:
if b<225:
b=randint(5,b+225)
if b>225:
b=randint(b-225,450)
else:
b=randint(0,600)
a=Pipe(c,pipes,b)
a.move()
c.after(2000,run)
else:
return
root.focus_set()
user=Player(c)
c.after(2000,run)
c.pack()
root.mainloop()
Canvas.find_overlapping return a tuple with all the ids of shapes in the bbox. If only your id is returned it might be because it is the sole one.
You can use it with the bbox of another shape like this : canvas.find_overlapping(*canvas.bbox(aShape)).
Note that overlapping works on rectangle and might be erroneous for instance on circles.