I have written some Python code in an attempt to show one video after the other where I want to in Pygame:
import pygame
import time
def playvid(vidfile, runtime, FPS):
print("playing" + vidfile)
playmovie = pygame.movie.Movie(vidfile)
movie_screen = pygame.Surface(playmovie.get_size()).convert()
playmovie.set_display(movie_screen)
playmovie.play()
thentime = time.time()
playing = True
while playing:
screen.blit(movie_screen,(0,0))
pygame.display.update()
clock.tick(FPS)
nowtime = time.time()
#Play the video for 5s
if nowtime - thentime > runtime + 1: playing = False
print("done this one")
#FPS = 29.97
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((0, 0),pygame.FULLSCREEN)
run = True
while run:
playvid('snowdone.mpg', 10, 29.97)
playvid('snowdone1.mpg', 10, 29.97)
run = False
pygame.quit()
Playing the first video is fine, however, playing the second gives the warning:
"Runtime Error!
Program: C:\Python27\pythonw.exe
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information."
Both videos play fine on their own so this is not an mpg problem. Any suggestions?
I do not know the specific reason as to why the change I am suggesting works, but apparently it works.
Using your code as is, my first movie would not run for more than 4-5 seconds no matter what (it always stopped at roughly the same point) and then the pygame window would crash.
Adding a simple piece of pygame.event.get code in the while playing loop, however, fixed everything
while playing:
for event in pygame.event.get():
pass
screen.blit(movie_screen,(0,0))
pygame.display.update()
clock.tick(FPS)
nowtime = time.time()
#Play the video for 5s
if nowtime - thentime > runtime + 1: playing = False
It seems strange that even doing nothing in the loop for events runs the code without an issue.
Related
I am looking to iterate through a list of songs such as Songs = ["Song1.mp3", "Song2.mp3", "Song3.mp3"] and I want to play each song one after each other.
I have tried various methods, the most suggested seemed to use pygame, however, I have not been able to debug the tremendous amount of errors that come with using it. My main source code and attempt at this is as shown below:
from tkinter import *
import pygame
from random import choice
import os
pygame.mixer.init()
Songs = os.listdir("Music\\")
def Play():
Song = choice(Songs)
pygame.mixer.music.load("Music\\" + Song)
pygame.mixer.music.play()
while True:
play()
Upon running this I receive error pygame.error: ModPlug_Load failed.
I am running this concurrently inside of a slideshow program I have, I want this code to run as background music and I plan on checking for the end of the song in a Function I already have set.
Use pygame.mixer.music.get_busy() to detect if a music stream is actively playing. Play the next song from the list when no stream is active. e.g:
import pygame
play_list = ["song1.mp3", "song2.mp3", "song3.mp3"]
current_list = []
pygame.init()
clock = pygame.time.Clock()
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if not pygame.mixer.music.get_busy():
if not current_list:
current_list = play_list[:]
current_song = current_list.pop(0)
pygame.mixer.music.load(current_song)
pygame.mixer.music.play()
pygame.quit()
exit()
You can register a custom event which will be triggered when the music is done playing using pygame.mixer.music.set_endevent().
Also you need to run the while loop in a child thread if you want to run concurrently with main application.
Below is an example:
import os
import random
import threading
import tkinter as tk
import pygame
pygame.init()
# create a custom event
MUSIC_DONE = pygame.event.custom_type()
# register the event
pygame.mixer.music.set_endevent(MUSIC_DONE)
folder = "Music\\"
Songs = os.listdir(folder)
def next_song():
try:
song = random.choice(Songs)
pygame.mixer.music.load(os.path.join(folder, song))
pygame.mixer.music.play()
# update song name
song_var.set(song)
except Exception as e:
print(e)
def pygame_loop():
next_song()
while pygame.get_init():
for event in pygame.event.get():
if event.type == MUSIC_DONE:
# current song done playing, play next song
next_song()
root = tk.Tk()
# label to show the current song being played
song_var = tk.StringVar()
tk.Label(root, textvariable=song_var).pack()
# start the pygame loop in a child thread
threading.Thread(target=pygame_loop).start()
root.mainloop()
# quit pygame
pygame.quit()
If it's a desktop app, you have multiple options to play a file, usually, it depends:
Also to avoid Tkinter being stuck, you need to use threads or multiprocess. I recommend you soundfile player, you will find examples here https://realpython.com/playing-and-recording-sound-python/
I'm writing a weather display program for my Raspberry Pi in python that fetches data using weather.com's api. As it stands, I have set it to sleep for 5 minutes after each main 'while' loop. This is because I don't want the Pi constantly using the wifi to fetch the same weather data. The problem with this is if I try to close or alter the program in any way, it waits to finish the time.sleep() function before continuing. I would like to add buttons to create a scroll menu but currently, the program will hang in that time.sleep() function before continuing. Is there an alternative I can use to delay the fetching of data while keeping program responsiveness?
You can do something like this:
import time, threading
def fetch_data():
# Add code here to fetch data from API.
threading.Timer(10, fetch_data).start()
fetch_data()
fetch_data method will be executed inside a thread so you wont have much problem. There is also a delay before calling the method. So you wont be bombarding the API.
Example source: Executing periodic actions in Python
Create a timer with python's time module
import time
timer = time.clock()
interval = 300 # Time in seconds, so 5 mins is 300s
# Loop
while True:
if timer > interval:
interval += 300 # Adds 5 mins
execute_API_fetch()
timer = time.clock()
Pygame has pygame.time.get_ticks() which you can use to check time and use it to execute function in mainloop.
import pygame
# - init -
pygame.init()
screen = pygame.display.set_mode((800, 600))
# - objects -
curr_time = pygame.time.get_ticks()
# first time check at once
check_time = curr_time
# - mainloop -
clock = pygame.time.Clock()
running = True
while running:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# - updates -
curr_time = pygame.time.get_ticks()
if curr_time >= check_time:
print('time to check weather')
# TODO: run function or thread to check weather
# check again after 2000ms (2s)
check_time = curr_time + 2000
# - draws -
# empty
# - FPS -
clock.tick(30)
# - end -
pygame.quit()
BTW: if fetching web content takes more time then run it in thread.
I'm making an game for ending my programming course and I sometimes have some longer scenes displayed and the loop ain't checking for if there is QUIT event and then stop everything and close the loop. So I thought multi-threading would help, but apparently it doesn't seem to get events, I have tried printing out - it gets events, but it simply aint getting QUIT event.
Here is the code:
from pygame import *
from threading import Thread
def closesearcher():
global running
while running:
for i in event.get():
if i.type == QUIT:
print ("QUIT event in closesearcher")
running = False
quit()
exit()
clock.tick(60)
def main():
init()
#True while game is running
global running
running = True
global window
window = display.set_mode([640, 480])
global clock
clock = time.Clock()
#Let's start closesearcher
searcherclose = Thread(target = closesearcher)
searcherclose.start()
scenegame = gamescene()
def gamescene():
#Render some scenery, wait for user to press button, etc in the following loop
while running:
for i in event.get():
if i.type == QUIT:
print ("QUIT event in game loop")
clock.tick(60)
if __name__ == "__main__":
main()
Any solutions that would be similiar to the structure of my current code?
Note: I use Python 3.2
How about actually exiting the loops with return instead of only printing that you got the quit event?
Currently, you only exit the loop in closesearcher but not in gamescene.
Also, I think event.get() will remove the event. So calling get in the other loop will not return anything. You need to set a global flag playerWantsToQuit and use that instead of the local running variables.
I'm trying to draw an image with PyGame, and I copied this code from Adafruit. It's strange, seems nothing displays on the screen until I Ctrl-C, at which time it shows the image. Then the image stays on the screen until I press Ctrl-C again. I then get the following message:
Traceback (most recent call last):
File "RaspiDisplay.py", line 60, in
time.sleep(10)
KeyboardInterrupt
What's going on? By the way, I'm running this via ssh on a raspberry pi, with Display set to 0 (my TV) If I put a print statement in init, that also doesn't print until I press Ctrl-C.
import os
import pygame
import time
import random
class pyscope :
screen = None;
def __init__(self):
"Ininitializes a new pygame screen using the framebuffer"
# Based on "Python GUI in Linux frame buffer"
# http://www.karoltomala.com/blog/?p=679
disp_no = os.getenv("DISPLAY")
if disp_no:
print "I'm running under X display = {0}".format(disp_no)
# Check which frame buffer drivers are available
# Start with fbcon since directfb hangs with composite output
drivers = ['fbcon', 'directfb', 'svgalib']
found = False
for driver in drivers:
# Make sure that SDL_VIDEODRIVER is set
if not os.getenv('SDL_VIDEODRIVER'):
os.putenv('SDL_VIDEODRIVER', driver)
try:
pygame.display.init()
except pygame.error:
print 'Driver: {0} failed.'.format(driver)
continue
found = True
break
if not found:
raise Exception('No suitable video driver found!')
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
print "Framebuffer size: %d x %d" % (size[0], size[1])
self.screen = pygame.display.set_mode(size, pygame.FULLSCREEN)
# Clear the screen to start
self.screen.fill((0, 0, 0))
# Initialise font support
pygame.font.init()
# Render the screen
pygame.display.update()
def __del__(self):
"Destructor to make sure pygame shuts down, etc."
def test(self):
# Fill the screen with red (255, 0, 0)
red = (255, 0, 0)
self.screen.fill(red)
# Update the display
pygame.display.update()
# Create an instance of the PyScope class
scope = pyscope()
scope.test()
time.sleep(10)
You're not running an event loop anywhere. Instead, you're just initializing everything, and then going to sleep for 10 seconds. During that 10 seconds, your code is doing nothing, because that's what you told it to do. That means no updating the screen, responding to mouse clicks, or anything else.
There are a few different ways to drive pygame, but the simplest is something like this:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
# any other event handling you need
# all the idle-time stuff you want to do each frame
# usually ending with pygame.display.update() or .flip()
See the tutorial for more information.
As a side note, your initialization code has a bunch of problems. You iterate through three drivers, but you only set SDL_VIDEODRIVER once, so you're just trying 'fbcon' three times in a row. Also, you've got code to detect the X display, but you don't allow pygame/SDL to use X, so… whatever you were trying to do there, you're not doing it. Finally, you don't need a found flag in Python for loops; just use an else clause.
I'm running it through the livewires wrapper which is just training wheels for pygame and other python modules in general; but everytime I run it, it'll execute and when I try to exit, it will not respond and then crash.
Any input on how I could go about fixing this would be great. There isn't any input in my textbook and all google seems to yield are results to this problem using pygame itself.
Apparently pygame and Tkinter seem to conflict?
Thanks in advance!
Addendum - This is the code I was trying to run:
from livewires import games
screen_w = 640
screen_h = 480
my_screen = games.Screen (wid, heit)
my_screen.mainloop()
Similar question: Pygame screen freezes when I close it
There isn't any input in my textbook
and all google seems to yield are
results to this problem using pygame
itself.
Those results probably address the same problem you're having. This is the relevant part of the games.py file from livewires, and nowhere does it call pygame.quit():
def handle_events (self):
"""
If you override this method in a subclass of the Screen
class, you can specify how to handle different kinds of
events. However you must handle the quit condition!
"""
events = pygame.event.get ()
for event in events:
if event.type == QUIT:
self.quit ()
elif event.type == KEYDOWN:
self.keypress (event.key)
elif event.type == MOUSEBUTTONUP:
self.mouse_up (event.pos, event.button-1)
elif event.type == MOUSEBUTTONDOWN:
self.mouse_down (event.pos, event.button-1)
def quit (self):
"""
Calling this method will stop the main loop from running and
make the graphics window disappear.
"""
self._exit = 1
def mainloop (self, fps = 50):
"""
Run the pygame main loop. This will animate the objects on the
screen and call their tick methods every tick.
fps -- target frame rate
"""
self._exit = 0
while not self._exit:
self._wait_frame (fps)
for object in self._objects:
if not object._static:
object._erase ()
object._dirty = 1
# Take a copy of the _objects list as it may get changed in place.
for object in self._objects [:]:
if object._tickable: object._tick ()
self.tick ()
if Screen.got_statics:
for object in self._objects:
if not object._static:
for o in object.overlapping_objects ():
if o._static and not o._dirty:
o._erase ()
o._dirty = 1
for object in self._objects:
if object._dirty:
object._draw ()
object._dirty = 0
self._update_display()
self.handle_events()
# Throw away any pending events.
pygame.event.get()
The QUIT event just sets a flag which drops you out of the while loop in the mainloop function. I'm guessing that if you find this file in your Python directory and stick a pygame.quit() after the last line in mainloop, it will solve your problem.
I agree. you need to put the whole program (the part to be executed, not the definitions and such) into a while loop.The problem is that pygame is not told to close when exited in IDLE, but outside of IDLE the closure of the program overrides the need to close pygame.
here is the loop:
done = False
while done==False:
# ALL EVENT PROCESSING SHOULD GO BELOW THIS COMMENT
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done=True # Flag that we are done so we exit this loop
#Main program here
pygame.quit()