Omxplayer-wrapper problems when run from terminal - python

I have the following program that I've been working on (I didn't create all of it, so some of it is pretty weird) on a Raspberry Pi. It's basically supposed to open up a tkinter window with buttons, and if you click the buttons in the right order, a new process will start. If the buttons are clicked in the wrong order, it will play a video. All of this works in Thonny IDE. (Really, the specifics of the program don't matter much other than the fact that it is using omxplayer-wrapper to play videos)
import os, subprocess
import RPi.GPIO as GPIO
import sys
import os
import tkinter as tk
from tkinter import *
from PIL import Image, ImageTk
import time
from omxplayer.player import OMXPlayer
from pathlib import Path
GPIO.setmode(GPIO.BCM)
# Variables
wrongOrder=False
desired_order=[1,2,3,8,9,10] #Desired order of clicking on images
click_num=0 #Number of clicks
videoPath = "MOV5.mp4"
loopPath = "loop2.mp4"
incorrectPath = "Incorrect Password.mp4"
secondVideoPath = "Access Denied.mp4"
secondLoopPath = "loop1.mp4"
GPIO.setup(20, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(19, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(18, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(2, GPIO.IN) #The input for when the power rod is removed (stop 1st looping video)
GPIO.setup(3, GPIO.IN) #The input for when to stop the 2nd looping video
# Actions taken when clicked on each image
def on_click1(event=None):
global IM
print("image1 clicked")
IM=1 #the image number
order(IM)
def on_click2(event=None):
global IM
print("image2 clicked")
IM=2
order(IM)
def on_click3(event=None):
global IM
print("image3 clicked")
IM=3
order(IM)
def on_click4(event=None):
global IM
print("image4 clicked")
IM=4
order(IM)
def on_click5(event=None):
global IM
print("image5 clicked")
IM=5
order(IM)
def on_click6(event=None):
global IM
print("image6 clicked")
IM=6
order(IM)
def on_click7(event=None):
global IM
print("image7 clicked")
IM=7
order(IM)
def on_click8(event=None):
global IM
print("image8 clicked")
IM=8
order(IM)
def on_click9(event=None):
global IM
print("image9 clicked")
IM=9
order(IM)
def on_click10(event=None):
global IM
print("image10 clicked")
IM=10
order(IM)
root = tk.Tk()
#Function when an image is clicked
def order(IM):
global click_num, wrongOrder
if (IM != desired_order[click_num]): #If this is not the right button
wrongOrder = True
click_num += 1
aCanvas.itemconfigure(asterisks, text = aCanvas.itemcget(asterisks, "text")+"*") #Adds an asterisk to the text
if (click_num == 6): #If it is the last number
if (wrongOrder):
print("Wrong Order!")
click_num = 0
wrongOrder = False
aCanvas.itemconfigure(asterisks, text = "") #Set text back to no asterisks
incorrectVideo()
else:
print("Correct Order!")
afterPassword()
def afterPassword():
playVideo()
time.sleep(11) #Wait before door opens
PIN() #play_video, pin low
time.sleep(8)
root.destroy()
playFirstLoop() #This will have to wait for a GPIO pin to change
playSecondVideo()
PIN2()#Trigger different relay
relayFlash()
piOutput() #Trigger other pi
playSecondLoop()
def PIN():
GPIO.setup(21, GPIO.OUT, initial=GPIO.LOW) # set GPIO21 as an output
#GPIO.output(21, 0) # set GPIO21 to 0/GPIO.LOW/True
def PIN2():
GPIO.output(20,0)
def playVideo():
player = OMXPlayer(videoPath, args=["--win", "0 0 1280 1024"])
def playFirstLoop():
player = OMXPlayer(loopPath, args=["--loop","--no-osd", "--win", "0 0 1280 1024"])
while(True):
if (GPIO.input(2)==False):
player.quit()
break
def playSecondVideo():
player = OMXPlayer(secondVideoPath, args=["--win", "0 0 1280 1024"])
player.quit()
def playSecondLoop():
player = OMXPlayer(secondLoopPath, args=["--loop","--no-osd", "--win", "0 0 1280 1024"])
while(True):
if (GPIO.input(3)==False):
player.quit()
break
def incorrectVideo():
player = OMXPlayer(incorrectPath, args=["--win", "0 0 1280 1024"], dbus_name='org.mpris.MediaPlayer2.omxplayer0')
time.sleep(2)
player.quit()
def relayFlash():
GPIO.output(19,0)
time.sleep(0.5)
for i in range(0,14):
GPIO.output(19,1)
time.sleep(0.5)
GPIO.output(19,0)
time.sleep(0.5)
GPIO.output(19,1)
def piOutput():
GPIO.output(18,1)
size_x=249
size_y = 350
#Screen resolution: 1245 x 1044
# load images
image1 = Image.open("/home/pi/Documents/1.jpg")
image1 = image1.resize((size_x, size_y), Image.ANTIALIAS)
photo1 = ImageTk.PhotoImage(image1)
image2 = Image.open("/home/pi/Documents/2.jpg")
image2 = image2.resize((size_x, size_y), Image.ANTIALIAS)
photo2 = ImageTk.PhotoImage(image2)
image3 = Image.open("/home/pi/Documents/3.jpg")
image3 = image3.resize((size_x, size_y), Image.ANTIALIAS)
photo3 = ImageTk.PhotoImage(image3)
image4 = Image.open("/home/pi/Documents/4.jpg")
image4 = image4.resize((size_x, size_y), Image.ANTIALIAS)
photo4 = ImageTk.PhotoImage(image4)
image5 = Image.open("/home/pi/Documents/5.jpg")
image5 = image5.resize((size_x, size_y), Image.ANTIALIAS)
photo5= ImageTk.PhotoImage(image5)
image6 = Image.open("/home/pi/Documents/6.jpg")
image6 = image6.resize((size_x, size_y), Image.ANTIALIAS)
photo6 = ImageTk.PhotoImage(image6)
image7 = Image.open("/home/pi/Documents/7.jpg")
image7 = image7.resize((size_x, size_y), Image.ANTIALIAS)
photo7 = ImageTk.PhotoImage(image7)
image8 = Image.open("/home/pi/Documents/8.jpg")
image8 = image8.resize((size_x, size_y), Image.ANTIALIAS)
photo8 = ImageTk.PhotoImage(image8)
image9 = Image.open("/home/pi/Documents/9.jpg")
image9 = image9.resize((size_x, size_y), Image.ANTIALIAS)
photo9 = ImageTk.PhotoImage(image9)
image10 = Image.open("/home/pi/Documents/0.jpg")
image10 = image10.resize((size_x, size_y), Image.ANTIALIAS)
photo10 = ImageTk.PhotoImage(image10)
buttonFrame = Frame(root)
# button with image binded to the same function
b1 = tk.Button(buttonFrame, image=photo1, command=on_click1)
b2 = tk.Button(buttonFrame, image=photo2, command=on_click2)
b3 = tk.Button(buttonFrame, image=photo3, command=on_click3)
b4 = tk.Button(buttonFrame, image=photo4, command=on_click4)
b5 = tk.Button(buttonFrame, image=photo5, command=on_click5)
b6 = tk.Button(buttonFrame, image=photo6, command=on_click6)
b7 = tk.Button(buttonFrame, image=photo7, command=on_click7)
b8 = tk.Button(buttonFrame, image=photo8, command=on_click8)
b9 = tk.Button(buttonFrame, image=photo9, command=on_click9)
b10 = tk.Button(buttonFrame, image=photo10, command=on_click10)
aFrame = Frame(root,bg="white") #Make a new frame containing the canvas
aCanvas = Canvas(aFrame, width = 747, height = 324) #Make canvas for asterisks
aCanvas.create_rectangle(0,0,747,324, fill="white", width = 15) #Make rectangle for asterisks to display on
squarePaddingX = 0
squarePaddingXStart = 25
squarePaddingY = 87
squareLength = 117
for i in range(0,6):
aCanvas.create_rectangle((squarePaddingX * (i) + squarePaddingXStart)+(squareLength * i),squarePaddingY,(squarePaddingX*(i))+(squareLength * (i+1) + squarePaddingXStart),squareLength+squarePaddingY, width = 5) #Crazy code that creates squares. Set variables above, don't touch this
asterisks = aCanvas.create_text(25,50,text="",font=("Verdana", "175"), anchor=tk.NW) #Make text for asterisks
aCanvas.pack()
b1.grid(row=0,column=0)
b2.grid(row=0,column=1)
b3.grid(row=0,column=2)
b4.grid(row=0,column=3)
b5.grid(row=0,column=4)
b6.grid(row=1,column=0)
b7.grid(row=1,column=1)
b8.grid(row=1,column=2)
b9.grid(row=1,column=3)
b10.grid(row=1,column=4)
buttonFrame.grid(row=0) #Set buttonframe above asterisk frame
aFrame.grid(row=1)
root.mainloop()
GPIO.cleanup() #resets all GPIO ports used by this program
My problem is that I want this to run when the Pi boots up, which involves starting the program from the terminal. If I use "sudo python3 [file path]", it tells me: "Traceback (most recent call last):
File "/home/pi/Documents/alienPasscodeV2.py", line 10, in
from omxplayer.player import OMXPlayer
ImportError: No module named 'omxplayer'".
If I use just "python3 [file path]", it starts off working, until it needs to play a video. Then it tells me:
"Exception in Tkinter callback Traceback (most recent call last):
File "/usr/lib/python3.5/tkinter/init.py", line 1562, in call
return self.func(*args) File "/home/pi/Documents/alienPasscodeV2.py", line 86, in on_click7
order(IM) File "/home/pi/Documents/alienPasscodeV2.py", line 121, in order
incorrectVideo() File "/home/pi/Documents/alienPasscodeV2.py", line 171, in incorrectVideo
player = OMXPlayer(incorrectPath, args=["--win", "0 0 1280 1024"], dbus_name='org.mpris.MediaPlayer2.omxplayer0') File
"/home/pi/.local/lib/python3.5/site-packages/omxplayer/player.py",
line 162, in init
self.load(source, pause=pause) File "/home/pi/.local/lib/python3.5/site-packages/omxplayer/player.py",
line 245, in load
self._load_source(source) File "/home/pi/.local/lib/python3.5/site-packages/omxplayer/player.py",
line 171, in _load_source
self._connection = self._setup_dbus_connection(self._Connection, self._bus_address_finder) File
"/home/pi/.local/lib/python3.5/site-packages/omxplayer/player.py",
line 231, in _setup_dbus_connection
raise SystemError('DBus cannot connect to the OMXPlayer process') SystemError: DBus cannot connect to the OMXPlayer process"
I have no idea why there's even a difference between using sudo and not using sudo. I've been trying to figure this out for hours and I have no idea. I'm so sorry for this program.

Turns out that when I was setting the paths, I needed to set the full path, not just the name of the file. So the paths changed to /home/pi/Documents/filename.

Related

Fullscreen slideshow with images, text and video

I'm completely new at python, learned not much yet. My goal is, to build a slideshow app, which should display images, images with transparent text frames and also videos. Playing a video is my main problem at the moment.
A little test script with static content. Later, all content should be loaded from a web service.
import tkinter as tk
import tkvideo as tv
from PIL import Image
from PIL import ImageTk
import time
# fullscreen window
window = tk.Tk()
window.attributes('-fullscreen', True)
# dimensions
window_width = window.winfo_screenwidth()
window_height = window.winfo_screenheight()
text_width = int(window_width * 0.4)
text_x = int(window_width - text_width)
# transparent rectangle for text
imgs=[]
def create_rectangle(x,y,a,b,**options):
if 'alpha' in options:
# Calculate the alpha transparency for every color(RGB)
alpha = int(options.pop('alpha') * 255)
# Use the fill variable to fill the shape with transparent color
fill = options.pop('fill')
fill = window.winfo_rgb(fill) + (alpha,)
img = Image.new('RGBA', (a-x, b-y), fill)
imgs.append(ImageTk.PhotoImage(img, master=canvas))
canvas.create_image(x, y, image=imgs[-1], anchor='nw')
canvas.create_rectangle(x, y, a, b, **options)
# fullscreen canvas
canvas = tk.Canvas(window, bg="white", bd=0)
canvas.pack(fill=tk.BOTH, expand=True)
# only image slideshow
images = ['quiz1.jpg', 'quiz2.jpg', 'quiz3.jpg', 'quiz4.jpg']
for img in images:
image = Image.open("/home/jpm/Bilder/" + img)
newimage = image.resize((window_width, window_height))
photo = ImageTk.PhotoImage(newimage, master=canvas)
canvas.create_image(0, 0, anchor="nw", image=photo)
canvas.update()
time.sleep(5)
# image with text slideshow
images = ['1658724794aff.jpg', '1658724768kar.jpg']
headlines = ['Headline 1',
'Headline 2']
paragraphs = [['paragraph 1',
'paragraph 2',
'paragraph 3',
'paragraph 4'],
['paragraph 1',
'paragraph 2',
'paragraph 3',
'paragraph 4']]
i=0
for img in images:
image = Image.open("/home/jpm/Bilder/" + img)
newimage = image.resize((window_width, window_height))
photo = ImageTk.PhotoImage(newimage, master=canvas)
canvas.create_image(0, 0, anchor="nw", image=photo)
create_rectangle(text_x, 0, window_width, window_height, fill= "white", alpha=.80, width=0)
head = canvas.create_text(text_x+20, 20, text=headlines[i], fill="#72B765", font=('Helvetica 34 bold'), anchor='nw', width=text_width-20)
canvas.update()
x0, y0, x1, y1 = canvas.bbox(head)
for paragraph in paragraphs[i]:
time.sleep(4)
line = canvas.create_text(text_x+20, y1+10, text=paragraph, fill="#000000", font=('Helvetica 18 bold'), anchor='nw', width=text_width-20)
x0, y0, x1, y1 = canvas.bbox(line)
canvas.update()
canvas.update()
time.sleep(8)
i = i + 1
canvas.destroy()
my_label = tk.Label(window)
my_label.pack(fill='both', expand=True)
player = tv.tkvideo("/home/jpm/Bilder/docsite_promotion.mp4", my_label, loop = 0, size = (window_width,window_height))
player.play()
window.mainloop()
The image and image with text parts running fine. My problem is the video. First of all, it is only displayed at the end of the script. If I put the Label with the video between the two loops or in front of the first, nothing is shown. I also found out, that the program is not waiting till the video ends.
It's all too confusing for me, to explain it better. Maybe I don't understand the flow control of python programs at all. Maybe tkvideo is not the best option. I played also with ffpyplayer, but can't find out, how use it with tkinter.
I only want to display these three types of content in any order and finally also in an endless loop.
Hope you understand me and can give me some tips.
.play() doesn't wait for end of video (it runs code in separated thread) and next image may replace it (hide it) before you see video.
I tried to use sleep() to wait for end of video but it makes problem - it raises error because function doesn't run in main thread.
You would need to use .after(millisecond, my_function) to check periodically if there is end of video and later display images.
If in tkvideo you use self.thread instead of thread then you can check periodically self.thread.is_alive() to catch end of thread. But this need also some method to run next code which will change images. First idea is to create function which changes images and send it as callback to function which check end of video - and it would run this callback() when is_alive() is False. But all this makes problem when you would like to create something more complex.
Here example which uses window.after() to check self.thread.is_alive() and later it runs callback()
I uses tkvideo from my other answer - I added self.running to have method to stop video in any moment.
import time
import tkinter as tk
import threading
from time import perf_counter, sleep
import imageio
from PIL import Image, ImageTk
import os
class tkvideo():
def __init__(self, path, label, loop=False, size=(640,360), hz=0):
self.path = path
self.label = label
self.loop = loop
self.size = size
self.hz = hz
self.running = True # <-- variable to control loop
def load(self, path, label, loop, hz):
"""
Loads the video's frames recursively onto the selected label widget's image parameter.
Loop parameter controls whether the function will run in an infinite loop
or once.
"""
frame_data = imageio.get_reader(path)
if hz > 0:
frame_duration = float(1 / hz)
else:
frame_duration = float(0)
if loop:
while True:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
if not self.running: # <-- variable to control loop
return # exit function and stop thread
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
print('[load] end of loop')
else:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
if not self.running: # <-- variable to control loop
return # exit function and stop thread
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
print('[load] end of loop')
def play(self):
"""
Creates and starts a thread as a daemon that plays the video by rapidly going through
the video's frames.
"""
# uses `self.thread` instead of `thread` to have access to `self.thread.is_alive()`
self.thread = threading.Thread(target=self.load, args=(self.path, self.label, self.loop, self.hz))
self.thread.daemon = True
self.thread.start()
def show_video(filename, size, callback=None):
global player
global my_label
fullpath = os.path.join(folder, filename)
my_label = tk.Label(window)
my_label.pack(fill='both', expand=True)
player = tkvideo(fullpath, my_label, loop=False, size=size)
player.play()
# check after 5ms
window.after(5, check_end_video, callback)
def check_end_video(callback):
global my_label
if player.thread.is_alive(): # check if it end of thread
# check again after 5ms
window.after(5, check_end_video, callback)
else:
# remove label
print('The End')
my_label.destroy()
# run next function
if callback:
callback()
def show_image(filename, size, callback=None):
global my_label
fullpath = os.path.join(folder, filename)
frame_image = ImageTk.PhotoImage(Image.open(fullpath).resize(size))
my_label = tk.Label(window)
my_label.pack(fill='both', expand=True)
my_label['image'] = frame_image # show image on label
my_label.image = frame_image # solution for bug in PhotoImage
# check after 5000ms (5s)
window.after(5000, check_end_image, callback)
def check_end_image(callback):
global my_label
# remove label
print('The End')
my_label.destroy()
# run next function
if callback:
callback()
def other_function():
button = tk.Button(window, text='Close', command=window.destroy)
button.pack(fill='both', expand=True, padx=250, pady=250)
# --- main ---
folder = "/home/jpm/Bilder/"
window = tk.Tk()
window.update()
#window.attributes('-fullscreen', True)
#window_width = window.winfo_screenwidth()
#window_height = window.winfo_screenheight()
window.geometry('800x600+800+300')
window_width = 800
window_height = 600
# ---
size = (window_width, window_height)
# run video and it will later run `show_image`
show_video("docsite_promotion.mp4", size,
lambda:show_image('quiz1.jpg', size, other_function))
# ---
window.mainloop()

Timer that prints elapsed time and resets with a button click

I have a GUI where the user can click a button named "next set" that allows them to move onto the next task. I wanted to add a timer that starts as soon as they start the application and run the timer until they press the button "next set". When clicked, I want the time elapsed to print and the timer to restart until they press "next set" button again. I would like the timer to start automatically when the code runs. Currently, the "next set" button has two actions, one is to retrieve the next set of images and the other action I am trying to include is to reset the timer and print time elapsed. I also only included part of the code that felt relevant because it is long.
import time
import tkinter as tk
import csv
from pathlib import Path
import PIL.Image
import PIL.ImageDraw
import PIL.ImageTk
MAX_HEIGHT = 500
IMAGES_PATH = Path("Images")
CSV_LABELS_KEY = "New Labels"
CSV_FILE_NAME_KEY = "FolderNum_SeriesNum"
CSV_BOUNDING_BOX_KEY = "correct_flip_bbox"
counter = 0
timer_id = None
class App(tk.Frame):
def __init__(self, master=None):
super().__init__(master) # python3 style
self.config_paths = ["config 1.yaml", "config 2.yaml", "config 3.yaml"]
self.config_index = 0
self.clickStatus = tk.StringVar()
self.loadedImages = dict()
self.loadedBoxes = dict() # this dictionary will keep track of all the boxes drawn on the images
self.master.title('Slideshow')
frame = tk.Frame(self)
tk.Button(frame, text=" Next set ", command=lambda:[self.get_next_image_set(), self.reset()]).pack(side=tk.RIGHT)
tk.Button(frame, text=" Exit ", command=self.destroy).pack(side=tk.RIGHT)
frame.pack(side=tk.TOP, fill=tk.BOTH)
self.canvas = tk.Canvas(self)
self.canvas.pack()
self._load_dataset()
self.reset()
self.start_timer = None
t = time()
t.start()
def start_timer(self, evt=None):
if self._start_timer is not None:
self._start_timer = time.perf_counter()
# global counter
# counter += 1
# label.config(text=str(counter))
# label.after(1000, count)
def reset(self):
if self._start_timer is None:
elapsed_time = time.perf_counter() - self._start_timer
self._start_timer = None
print('Time elapsed (hh:mm:ss.ms) {}'.format(elapsed_time))
def _load_dataset(self):
try:
config_path = self.config_paths[self.config_index]
self.config_index += 1
except IndexError:
return
image_data = loadData(config_path)
# drawing the image on the label
self.image_data = image_data
self.currentIndex = 0
# start from 0th image
self._load_image()
def _load_image(self):
imgName = self.image_data[self.currentIndex]['image_file']
if imgName not in self.loadedImages:
self.im = PIL.Image.open(self.image_data[self.currentIndex]['image_file'])
ratio = MAX_HEIGHT / self.im.height
# ratio divided by existing height -> to get constant amount
height, width = int(self.im.height * ratio), int(self.im.width * ratio)
# calculate the new h and w and then resize next
self.canvas.config(width=width, height=height)
self.im = self.im.resize((width, height))
if self.im.mode == "1":
self.img = PIL.ImageTk.BitmapImage(self.im, foreground="white")
else:
self.img = PIL.ImageTk.PhotoImage(self.im)
imgData = self.loadedImages.setdefault(self.image_data[self.currentIndex]['image_file'], dict())
imgData['image'] = self.img
imgData['shapes'] = self.image_data[self.currentIndex]['shapes']
# for next and previous so it loads the same image adn don't do calculations again
self.img = self.loadedImages[self.image_data[self.currentIndex]['image_file']]['image']
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.img)
self.show_drag_box()
def loadData(fname):
with open(fname, mode='r') as f:
return yaml.load(f.read(), Loader=yaml.SafeLoader)
if __name__ == "__main__":
data = loadData('config 1.yaml')
app = App(data)
app.pack() # goes here
app.mainloop()
I have used datetime instead of time, as subtracting two datetime objects will give an output with hours and minutes included, whereas subtracting two time objects only gives seconds. However, both will work, you may just need to do more reformatting using time.
Read the current time when the application starts and store it. Each time you press the button, subtract your stored time from the current time which gives you your time elapsed. Then simply store your new current time until the next button press. The code below demonstrates this.
import tkinter as tk
import datetime as dt
class TimeButton(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
# Start timer
self.current_time = dt.datetime.today()
# Button
self.next_set = tk.Button(self, text='Next Set', command = self.clicked)
self.next_set.pack()
def clicked(self):
now = dt.datetime.today()
time_elapsed = now - self.current_time
print(time_elapsed)
self.current_time = now
if __name__ == "__main__":
window = tk.Tk()
button = TimeButton(window)
button.pack()
window.mainloop()

How do I use the same mp3 file more then once?

import tkinter as tk
from tkinter import filedialog
from pygame import mixer
mixer.init()
firsttime = False
song2switch = True
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=250)
canvas.pack()
def loadsong1():
global song1
song1 = tk.filedialog.askopenfile(parent=root, initialdir="C:/",title="choose first song",filetypes=[("mp3 files", ".mp3")])
def loadsong2():
global song2
song2 = tk.filedialog.askopenfile(parent=root, initialdir="C:/",title="choose second song",filetypes=[("mp3 files", ".mp3")])
def play():
mixer.music.load(song1)
mixer.music.play()
def switch():
global firsttime
global song2switch
global time_of_song
if firsttime == False:
time_of_song = mixer.music.get_pos()
time_of_song /= 1000
mixer.music.stop()
mixer.music.load(song2)
mixer.music.play(start = time_of_song)
firsttime = True
song2switch = False
else:
if song2switch == False:
time_of_song = mixer.music.get_pos()
time_of_song /= 1000
mixer.music.stop()
mixer.music.load(song1)
mixer.music.play(start = time_of_song)
song2switch = True
playbutton = tk.Button(canvas,text="PLAY",command=play)
canvas.create_window(200,240,window=playbutton)
load1button = tk.Button(canvas, text="Load Song One",command=loadsong1)
canvas.create_window(100,240,window=load1button)
load2button = tk.Button(canvas, text="Load Song Two", command=loadsong2)
canvas.create_window(300,240,window=load2button)
switchbutton = tk.Button(canvas, text="SWITCH", command=switch)
canvas.create_window(200,200, window=switchbutton)
root.mainloop()
I'm trying to make this music player that can switch between two songs while keeping the same time between each other (for example, when I switch a song that has been playing for a minute, the other will start a minute in), but when i try to switch it to the first song, it makes this error:
pygame.error: Couldn't read first 12 bytes of audio data
How do I fix this?
It is because you have used askopenfile(...) which will open the selected file in read mode and return the file handle.
When you switch to song2, song1 is closed. When you want to switch back to song1 again, mixer.music.load(song1) will fail with exception because the file is already closed.
Use askopenfilename() instead of askopenfile().
Note 1: mixer.music.get_pos() returns the elapsed time relative to start, not the beginning.
Note 2: your switching logic will not work properly after switching two times.

AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo'. Unable to load and display a video on tkinter screen

I'm trying to write a code for a game but im having some trouble with loading a video. I'm getting this error when i run my code. I know that some similar questions have been asked before but i could not figure out the problem here. When i run the code, the first few seconds of the video are played, and then the screen just goes blank and this error pops up.
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 804, in callit
func(*args)
File "c:/Jovan/Pycharm Projects/school projects/project_files/main_game2.py", line 69, in update
photo = ImageTk.PhotoImage(image=img)
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PIL\ImageTk.py", line 112, in __init__
self.__photo = tkinter.PhotoImage(**kw)
Image.__init__(self, 'photo', name, cnf, master, **kw)
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 4006, in __init__
self.tk.call(('image', 'create', imgtype, name,) + options)
_tkinter.TclError: not enough free memory for image buffer
Exception ignored in: <function PhotoImage.__del__ at 0x03C570B8>
Traceback (most recent call last):
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PIL\ImageTk.py", line 118, in __del__
name = self.__photo.name
AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo'
I tried running the part of the code that loads and displays the video separately from the class and it works fine then. I have no idea what is going on since i am new to opencv and tkinter. Could anyone please help me? Why is this happening and how can i fix it? Here is the code
from tkinter import *
from tkinter import messagebox
from random import *
from PIL import ImageTk,Image
import cv2
root = Tk()
root.title("Captain!")
root.geometry("1280x720")
#root.geometry("660x560")
class Game :
def __init__(self):
self.m_cur = {1:["Military",50]}
self.c_cur = {1:["People's",50]}
self.r_cur = {1:["Research",50]}
self.i_cur = {1:["Industrial",50]}
self.p_cur = {1:["Research",50]}
def clear(self):
for widget in root.winfo_children():
widget.destroy()
def exit(self):
msg = messagebox.askquestion("Thank you for playing","Are you sure you want to exit?")
if msg == "yes" :
root.destroy()
else:
Game.main(self)
def start(self):
Label(root,text="Hello, what should we call you?",font=("segoe print",20)).grid(row=0,column=0)
name = Entry(root,width=20)
name.grid(row=1,column=0)
Button(root,text="Enter",font=("segoe print",20),command=lambda: Game.main(self)).grid(row=1,column=1)
self.name=name.get()
def main(self):
Game.clear(self)
Label(root,text="Welcome to the game",font=("segoe print",20)).grid(row=0,column=0)
Label(root,text='What do you want to do?',font=("segoe print",20)).grid(row=1,column=0)
qn_num = randint(1,3)
Button(root,text="Start Game",font=("segoe print",20),command=lambda: Game.intro(self,qn_num)).grid(row=2,column=0)
Button(root,text="Exit Game",font=("segoe print",20),command=lambda: Game.exit(self)).grid(row=3,column=0)
#resetting values of the variables
self.r_cur[1][1] = 50
self.c_cur[1][1] = 50
self.i_cur[1][1] = 50
self.m_cur[1][1] = 50
self.p_cur[1][1] = 50
def intro(self,qn_num):
Game.clear(self)
vid = cv2.VideoCapture("project_files\\video.mp4")
width = vid.get(cv2.CAP_PROP_FRAME_WIDTH)
height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
def get_frame():
ret,frame = vid.read()
if ret :
return(ret,cv2.cvtColor(frame,cv2.COLOR_BGR2RGB))
else :
return(ret,None)
def update():
canvas = Canvas(root, width = width, height = height)
canvas.place(relx=0.5,rely=0.5,anchor=CENTER)
ret,frame = get_frame()
if ret :
img = Image.fromarray(frame)
photo = ImageTk.PhotoImage(image=img)
photo.image=img
canvas.create_image(0, 0, image = photo, anchor = NW)
canvas.image=photo
root.after(delay,update)
delay = 15
update()
Game.qn_func(self,qn_num)
def game_over(self,x_cur):
if x_cur[1][1]<=0 or x_cur[1][1]>=100 :
Game.clear(self)
Label(root,text=f"{x_cur[1][0]} faction rebelled").place(relx=0.4,rely=0.4)
Label(root,text="GAME OVER",font=("ariel",20)).place(relx=0.5,rely=0.5,anchor=CENTER)
Button(root,text="Continue",font=("segoe print",20),command=lambda: Game.main(self)).place(relx=0.37,rely=0.6)
def qn_func(self,qn_num) :
Game.clear(self)
with open("project_files\\questions_file.txt") as q_file :
#reading the question, options, next qn numbers and the character name from the file
qn_list = q_file.readlines()
qn = qn_list[qn_num-1].strip().split("$")[1]
char_name = qn_list[qn_num-1].strip().split("$")[2]
qn1 = qn_list[qn_num-1].strip().split("$")[3]
qn2 = qn_list[qn_num-1].strip().split("$")[5]
n_qn1 = int(qn_list[qn_num-1].strip().split("$")[4])
n_qn2 = int(qn_list[qn_num-1].strip().split("$")[6])
#displaying the character name and the question as a label frame widget with character name as parent
label_frame = LabelFrame(root,text = char_name,font = ("segoe print",20))
label = Label(label_frame,text = qn,font = ("segoe print",20))
label_frame.place(relx=0.5,rely=0.5,anchor=CENTER)
label.pack()
#displaying the buttons on the screen
Button(root,text=qn1,command=lambda: Game.qn_func(self,n_qn1)).place(relx=0.2,rely=0.7,anchor=W,width=200,height=50)
Button(root,text=qn2,command=lambda: Game.qn_func(self,n_qn2)).place(relx=0.8,rely=0.7,anchor=E,width=200,height=50)
#running each variable through game_over to see if you are dead
Game.game_over(self,self.r_cur)
Game.game_over(self,self.c_cur)
Game.game_over(self,self.i_cur)
Game.game_over(self,self.m_cur)
Game.game_over(self,self.p_cur)
#defining the Doublevar variables
s_var1 = DoubleVar()
s_var2 = DoubleVar()
s_var3 = DoubleVar()
s_var4 = DoubleVar()
s_var5 = DoubleVar()
#setting the values in the scales
s_var1.set(self.r_cur[1][1])
s_var2.set(self.c_cur[1][1])
s_var3.set(self.i_cur[1][1])
s_var4.set(self.m_cur[1][1])
s_var5.set(self.p_cur[1][1])
#variables as scale widgets
scale1 = Scale(root,from_=100,to=0,orient=VERTICAL,sliderlength=10,variable=s_var1)
scale2 = Scale(root,from_=100,to=0,orient=VERTICAL,sliderlength=10,variable=s_var2)
scale3 = Scale(root,from_=100,to=0,orient=VERTICAL,sliderlength=10,variable=s_var3)
scale4 = Scale(root,from_=100,to=0,orient=VERTICAL,sliderlength=10,variable=s_var4)
scale5 = Scale(root,from_=100,to=0,orient=VERTICAL,sliderlength=10,variable=s_var5)
#opening images to be displayed under scales
img_r = ImageTk.PhotoImage(Image.open("project_files\\research.PNG"))
img_c = ImageTk.PhotoImage(Image.open("project_files\\com_ppl.PNG"))
img_i = ImageTk.PhotoImage(Image.open("project_files\\money.PNG"))
img_m = ImageTk.PhotoImage(Image.open("project_files\\military.PNG"))
img_p = ImageTk.PhotoImage(Image.open("project_files\\productivity.PNG"))
#the label widgets for images
img_l1 = Label(root,image=img_r)
img_l2 = Label(root,image=img_c)
img_l3 = Label(root,image=img_i)
img_l4 = Label(root,image=img_m)
img_l5 = Label(root,image=img_p)
#saving a reference of images
img_l1.image = img_r
img_l2.image = img_c
img_l3.image = img_i
img_l4.image = img_m
img_l5.image = img_p
#displaying images
img_l1.grid(row=1,column=0)
img_l2.grid(row=1,column=1)
img_l3.grid(row=1,column=2)
img_l4.grid(row=1,column=3)
img_l5.grid(row=1,column=4)
#displaying the scale widgets on the screen
scale1.grid(row=0,column=0,padx=40,pady=10)
scale2.grid(row=0,column=1,padx=40,pady=10)
scale3.grid(row=0,column=2,padx=40,pady=10)
scale4.grid(row=0,column=3,padx=40,pady=10)
scale5.grid(row=0,column=4,padx=40,pady=10)
#disabling the scales
scale1.config(state=DISABLED)
scale2.config(state=DISABLED)
scale3.config(state=DISABLED)
scale4.config(state=DISABLED)
scale5.config(state=DISABLED)
with open("project_files\\variables.txt") as v_file :
#reading values of variables from file
v_list = v_file.readlines()
self.r_cur[1][1] += int(v_list[qn_num-1].strip().split("$")[1])
self.c_cur[1][1] += int(v_list[qn_num-1].strip().split("$")[2])
self.i_cur[1][1] += int(v_list[qn_num-1].strip().split("$")[3])
self.m_cur[1][1] += int(v_list[qn_num-1].strip().split("$")[4])
self.p_cur[1][1] += int(v_list[qn_num-1].strip().split("$")[5])
#driver code
game = Game()
game.start()
root.mainloop()
Thanks in advance!
The problem was that i was creating a new list every time update() is called and all these lists were piling up in memory. I solved the problem by calling a function that destroys all widgets currently on screen every time update() is called. The relevant parts of the working code is given below
def clear(self):
for widget in root.winfo_children():
widget.destroy()
def intro(self):
Game.clear(self)
vid = cv2.VideoCapture("project_files\\video.mp4")
width = vid.get(cv2.CAP_PROP_FRAME_WIDTH)
height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
def get_frame():
ret,frame = vid.read()
if ret :
return(ret,cv2.cvtColor(frame,cv2.COLOR_BGR2RGB))
else :
return(ret,None)
def update():
Game.clear(self) #i added this to delete the labels.
ret,frame = get_frame()
print(ret)
if ret :
img = Image.fromarray(frame)
photo = ImageTk.PhotoImage(image=img)
photo.image=img
label = Label(root,image=photo)
label.place(relx=0.5,rely=0.5,anchor=CENTER)
label.image=photo
root.after(delay,update)
delay = 15
update()
Game.qn_call(self,"a",iteration=0)

Play mp4 video in python with GUI

I want to create a photo booth with a simple sequence. I used mp4 files as animations.
Everything has to start in GUI with a button "start" that will run a loop for users. Exit to GUI will be by pressing the esc key.
In the "user" loop there will be a start animation as mp4 video -in omx player, working over and over until the user touches the touch screen. I already did this with listener move_mouse to kill the process.
I then have another animation with a countdown after which the camera takes a picture and then displays the photo on the screen in a window with two buttons repeat or print.
The problem with the listener is it freezes the application after capturing the photo
How can I solve this problem?
try:
from Tkinter import * # Python2
except ImportError:
import tkinter as Tk # Python3import Tkinter as tk
import sys
from time import sleep
from PIL import Image, ImageTk
from datetime import datetime
from sh import gphoto2 as gp
import signal, os, subprocess, glob
from pynput import mouse
from pynput import keyboard
shot_date = datetime.now() .strftime("%Y-%m-%d")
shot_time = datetime.now() .strftime("%Y-%m-%d %H:%M:%S")
picID = "PIShots"
folder_name = shot_date + picID
save_location = "/home/pi/Desktop/gphoto/images/" + folder_name
# listener
def on_move(x,y):
print("mouse move")
playerOff()
#pynput.mouse.Listener.stop
playerOn()
capturePhoto()
# press key listener
#def on_release(key):
# print('{0} released'.format(
# key))
# if key == keyboard.Key.esc:
# Stop listener
# return False
#StartAnmation and listener
def Start():
omxc= subprocess.Popen(['omxplayer','-b','--loop','--no-osd',"/home/pi/Desktop/START.mp4"])
with mouse.Listener(
on_move=on_move) as listener:
listener.join()
# get the latest file
def get_latest_file(path, *paths):
"""Returns the name of the latest (most recent) file
of the joined path(s)"""
fullpath = os.path.join(path, *paths)
files = glob.glob(fullpath) # You may use iglob in Python3
if not files: # I prefer using the negation
return None # because it behaves like a shortcut
latest_file = max(files, key=os.path.getctime)
_, filename = os.path.split(latest_file)
return filename
#start_animation in OMX player
def playerOn():
omxc= subprocess.Popen(['omxplayer','-b',"/home/pi/Desktop/animacja.mp4"])
#Player Off
def playerOff():
os.system('killall omxplayer.bin')
#CreateSaveFolder
def createSaveFolder():
try:
os.makedirs(save_location)
except:
print("Failed to create the new directory")
os.chdir(save_location)
def quit(root):
root.destroy()
def capturePhoto():
status = 0
createSaveFolder()
sleep(6)
os.system('fswebcam -r 1920x1080 -s brightness=70% -s gain=50% -S 10 --set lights=off --no-banner %H%M%S.jpg')
print save_location
location=get_latest_file(save_location,'*.jpg')
#print location
sciezkaZdj= save_location + "/" + location
print sciezkaZdj
im = Image.open(sciezkaZdj)
width, height =im.size
LEFT_HALF = 200, 0, width-400 ,height
im = im.crop(LEFT_HALF)
im = im.transpose(Image.ROTATE_270)
ramka = Image.open("/home/pi/Desktop/ramka1.jpg")
text_img = Image.new('RGBA', (1200,2000), (0, 0, 0, 0))
text_img.paste(ramka, (0,0))
text_img.paste(im, (50,30))
text_img.save("ramka.png", format="png")
path = save_location + "/ramka.png"
top2 = Toplevel(window)
top2.geometry("1600x720")
top2.overrideredirect(1)
top2.title("fotobudka")
top2.configure(background='black')
img = ImageTk.PhotoImage(Image.open(path))
panel = Label(top2, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
playerOff()
#close gui
window.after(5000, lambda: top2.destroy())
#************MAIN
if __name__ == "__main__":
try:
#Gui main*********************************
window = Tk()
top1 = Toplevel(window)
top1.geometry("1600x720")
# top1.wm_attributes('-topmost',1) #zawsze na wierzchu
top1.configure(background='black')
button_start = Button(window, text='start',command=Start)
button_start.pack()
window.mainloop()
#Gui main end******************************
except KeyboardInterrupt:
print "koniec"

Categories