Way to play video files in Tkinter? - python

Is there a way to play video files like AVI, MP4, etc.?
I tried using PyMedia, but apparently it only works with Pygame.
What is the solution to my problem?

You could use python-gstreamer for playing videos (this works for me on Linux, but it should also work on Windows). This requires python-gstreamer and python-gobject, I would recommend you to use this all-in-one installer.
Here is the code:
import os
import sys
import Tkinter as tkinter
import gobject
import gst
def on_sync_message(bus, message, window_id):
if not message.structure is None:
if message.structure.get_name() == 'prepare-xwindow-id':
image_sink = message.src
image_sink.set_property('force-aspect-ratio', True)
image_sink.set_xwindow_id(window_id)
gobject.threads_init()
window = tkinter.Tk()
window.geometry('500x400')
video = tkinter.Frame(window, bg='#000000')
video.pack(side=tkinter.BOTTOM,anchor=tkinter.S,expand=tkinter.YES,fill=tkinter.BOTH)
window_id = video.winfo_id()
player = gst.element_factory_make('playbin2', 'player')
player.set_property('video-sink', None)
player.set_property('uri', 'file://%s' % (os.path.abspath(sys.argv[1])))
player.set_state(gst.STATE_PLAYING)
bus = player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect('sync-message::element', on_sync_message, window_id)
window.mainloop()

The following code works for me with GStreamer 1.0 and Python 3 under Ubuntu 16.04. It also enables eight video players stacked in a column in a single window. (The sound channels are simply mixed together.)
The libav/ffmpeg fork created problems under Ubuntu 14.04, which seem to be solved under 16.04. Note that you need the package gstreamer1.0-libav in addition to gstreamer1.0-plugins-*.
The code builds on the 2011 answer by #koehlma, which assumed GStreamer 0.10 and Python 2.
import sys
import os
if sys.version_info[0] < 3:
import Tkinter as tkinter
else:
import tkinter
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst, GObject
# Needed for set_window_handle():
gi.require_version('GstVideo', '1.0')
from gi.repository import GstVideo
def set_frame_handle(bus, message, frame_id):
if not message.get_structure() is None:
if message.get_structure().get_name() == 'prepare-window-handle':
display_frame = message.src
display_frame.set_property('force-aspect-ratio', True)
display_frame.set_window_handle(frame_id)
NUMBER_OF_FRAMES = 8 # with more frames than arguments, videos are repeated
relative_height = 1 / float(NUMBER_OF_FRAMES)
# Only argument number checked, not validity.
number_of_file_names_given = len(sys.argv) - 1
if number_of_file_names_given < 1:
print('Give at least one video file name.')
sys.exit()
if number_of_file_names_given < NUMBER_OF_FRAMES:
print('Up to', NUMBER_OF_FRAMES, 'video file names can be given.')
file_names = list()
for index in range(number_of_file_names_given):
file_names.append(sys.argv[index + 1])
window = tkinter.Tk()
window.title("Multiple videos in a column using Tk and GST 1.0")
window.geometry('480x960')
Gst.init(None)
GObject.threads_init()
for number in range(NUMBER_OF_FRAMES):
display_frame = tkinter.Frame(window, bg='')
relative_y = number * relative_height
display_frame.place(relx = 0, rely = relative_y,
anchor = tkinter.NW, relwidth = 1, relheight = relative_height)
frame_id = display_frame.winfo_id()
player = Gst.ElementFactory.make('playbin', None)
fullname = os.path.abspath(file_names[number % len(file_names)])
player.set_property('uri', 'file://%s' % fullname)
player.set_state(Gst.State.PLAYING)
bus = player.get_bus()
bus.enable_sync_message_emission()
bus.connect('sync-message::element', set_frame_handle, frame_id)
window.mainloop()

Make use of tkvideoplayer version>=2.0.0 library, which can help you to play, pause, seek, get metadata of the video etc.
pip install tkvideoplayer
Sample example:
import tkinter as tk
from tkVideoPlayer import TkinterVideo
root = tk.Tk()
videoplayer = TkinterVideo(master=root, scaled=True)
videoplayer.load(r"samplevideo.mp4")
videoplayer.pack(expand=True, fill="both")
videoplayer.play() # play the video
root.mainloop()
An example to create a complete video player is in the GitHub page
Refer documentation

Tha easy way is by using tkVideo
to install it
pip install tkVideo
this is script that show you how does it work easily!
from tkinter import *
from tkvideo import tkvideo
root = Tk()
my_label = Label(root)
my_label.pack()
player = tkvideo("C:\\path\\to\\video.mp4", my_label, loop = 1, size = (1280,720))
player.play()
root.mainloop()
this is PyPI link to it for more informations.

koehlma's answer is quite outdated. The "Tkinter" should be "tkinter"; "gst" should "Gst"; "playbin2" should be "playbin". "STATE_PLAYING" should be "State.PLAYING". The below setup and codes works on Debian 11 as of Apr 5, 2022.
First of all, the packages you need to install are
apt install python3-gi python3-gst-1.0 python3-tk
And the code should be updated to:
import os
import sys
import tkinter as tkinter
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst as gst
from gi.repository import GObject as gobject
def on_sync_message(bus, message, window_id):
return
# message does not have structure field any more.
if not message.structure is None:
if message.structure.get_name() == 'prepare-xwindow-id':
image_sink = message.src
image_sink.set_property('force-aspect-ratio', True)
image_sink.set_xwindow_id(window_id)
gobject.threads_init()
window = tkinter.Tk()
window.geometry('500x400')
video = tkinter.Frame(window, bg='#000000')
video.pack(side=tkinter.BOTTOM,anchor=tkinter.S,expand=tkinter.YES,fill=tkinter.BOTH)
window_id = video.winfo_id()
gst.init(None)
player = gst.ElementFactory.make('playbin', 'player')
player.set_property('video-sink', None)
player.set_property('uri', 'file://%s' % (os.path.abspath(sys.argv[1])))
player.set_state(gst.State.PLAYING)
bus = player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect('sync-message::element', on_sync_message, window_id)
window.mainloop()

I've recently made a package to do this, borderless video player. It also plays audio in sync.
pip install bvPlayer
The code:
from bvPlayer import bvPlayer
bvPlayer("file.mp4")
The repository

Related

How to use a video logo for the DearPyGUI?

I'm new to Python GUI's and am currently trying to build an app with DearPy. I was wondering if it's possible to use an mp4 formatted logo (animated logo) instead of the png logo's it would usually accept. For example:
with window("Appi", width= 520, height=677):
print("GUI is running")
set_window_pos("Appi", 0, 0)
add_drawing("logo", width=520, height=290)
draw_image("logo", "random.png", [0,240])
My question is: would it be possible to change the add_drawing to an add video and then the draw_image to allow me to insert an mp4 instead of a png? I've tried to look through the documentation but haven't found guidance for this yet.
Or is there an alternative package I should be using (i.e. tkinter)?
Thanks!
I used tkinter for this and it worked perfectly fine:
import tkinter as tk
import threading
import os
import time
try:
import imageio
except ModuleNotFoundError:
os.system('pip install imageio')
import imageio
from PIL import Image, ImageTk
# Settings:
video_name = 'your_video_path_here.extension' #This is your video file path
video_fps = 60
video_fps = video_fps * 1.2 # because loading the frames might take some time
try:
video = imageio.get_reader(video_name)
except:
os.system('pip install imageio-ffmpeg')
video = imageio.get_reader(video_name)
def stream(label):
'''Streams a video to a image label
label: the label used to show the image
'''
for frame in video.iter_data():
# render a frame
frame_image = ImageTk.PhotoImage(Image.fromarray(frame))
label.config(image=frame_image)
label.image = frame_image
time.sleep(1/video_fps) # wait
# GUI
root = tk.Tk()
my_label = tk.Label(root)
my_label.pack()
thread = threading.Thread(target=stream, args=(my_label,))
thread.daemon = 1
thread.start()
root.mainloop()
Edit: Thanks <3
Videos are in DearPyGui currently not supported

No Titlebar with PYSimpleGUI in Python

I want to make a small program using PySimpleGUI but when I include "no_titlebar = True" the taskbar icon also disappears. Is there a way to stop that? Thank you.
Maybe you can use pywin32.
Install pywin32 first,
pip install pywin32
python3.10 + Windows11
from win32gui import *
from win32con import *
from tkinter import *
root = Tk()
root.overrideredirect(True)
def taskbar(window):
hWnd = GetParent(window.winfo_id())
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_TOOLWINDOW | WS_EX_APPWINDOW)
window.wm_withdraw()
window.after(1, lambda: window.wm_deiconify())
def move(window):
ReleaseCapture()
hWnd = GetParent(window.winfo_id())
SendMessage(hWnd, WM_SYSCOMMAND,
SC_MOVE + HTCAPTION, 0)
root.after(100, lambda: taskbar(root))
root.bind("<B1-Motion>", lambda evt: move(root))
root.mainloop()

Is it possible to add an Animation with AppIndicator3?

I'm trying to create a simple AppIndicator with Python.
The code is pretty similar with:
import gi
from gi.repository import Gtk
from gi.repository import AppIndicator3 as appindicator
APPINDICATOR_ID = 'myapp'
def main():
indicator = appindicator.Indicator.new(APPINDICATOR_ID, '/usr/share/myapp/images/icon.svg', appindicator.IndicatorCategory.SYSTEM_SERVICES)
indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
Gtk.main()
It works very well, but I would like to introduce an animated icon. A blinking icon for example.
Not a static one.
I tried to convert the SVG file to GIF, but it does not work as well.
Is there a way to create a GdkPixbuf.PixbufAnimation (example) within AppIndicator3?
If yes, How can I do it?
I don't know if my answer will work for you but I see you are only importing libraries, assigning a variable, and defining some code. You may want to have the main(): code loop by typing:
This will give you the option to add return True or return False:
import gi
from gi.repository import Gtk
from gi.repository import AppIndicator3 as appindicator
APPINDICATOR_ID = 'myapp'
def main():
indicator = appindicator.Indicator.new(APPINDICATOR_ID, '/usr/share/myapp/images/icon.svg', appindicator.IndicatorCategory.SYSTEM_SERVICES)
indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
Gtk.main()
while main():
pass
Or if you want the code to keep going until you click the stop button:
import gi
from gi.repository import Gtk
from gi.repository import AppIndicator3 as appindicator
APPINDICATOR_ID = 'myapp'
def main():
indicator = appindicator.Indicator.new(APPINDICATOR_ID, '/usr/share/myapp/images/icon.svg', appindicator.IndicatorCategory.SYSTEM_SERVICES)
indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
Gtk.main()
main()
I found a workaround for this. Specially, if you are trying to configure a GIF.
For me, create a manual GIF worked. This solution requires a better approach related to concurrency but the prototype is here:
import gi
import time
import signal
import threading
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, GLib
from gi.repository import AppIndicator3 as appindicator
APPINDICATOR_ID = 'myapp'
finished = False
def change_icon(indicator, index=5):
indicator.set_icon("spinner-%s.svg" % str(index))
def render_icon(indicator):
i = 0
while True:
# I have 10 SVG images
index = (i % 10)
GLib.idle_add(change_icon, indicator, index)
time.sleep(0.5)
if finished:
break
i += 1
def on_click(widget):
global finished
finished = True
Gtk.main_quit()
def main():
indicator = appindicator.Indicator.new(APPINDICATOR_ID, 'spinner-0.svg', appindicator.IndicatorCategory.SYSTEM_SERVICES)
indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
menu = Gtk.Menu()
quit = Gtk.MenuItem('Quit')
quit.connect('activate', on_click)
menu.append(quit)
menu.show_all()
indicator.set_menu(menu)
thread = threading.Thread(target=render_icon, args=(indicator,))
thread.daemon = True
thread.start()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
main()
I created a spinner with 10 different frames and I can iterate using a Thread and set_icon + GLib.iddle_add().
Again, it requires a better solution in terms of Thread concorrency, but it works as a starting point.

python-vlc will not embed gtk widget into window, but open a new window instead

I'm working on a gtk3 front end for libvlc written in python using python-vlc. I'm following the gtk3 example from the python-vlc github page, but am experiencing strange behavior. I have a widget that looks like that:
import gi
import sys
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class PlayerWidget(Gtk.DrawingArea):
__gtype_name__ = 'VLCWidget'
def __init__(self, instance):
Gtk.DrawingArea.__init__(self)
self.player = instance.media_player_new()
def handle_embed(*args):
if sys.platform == 'win32':
self.player.set_hwnd(self.get_window().get_handle())
else:
self.player.set_xwindow(self.get_window().get_xid())
return True
self.connect("realize", handle_embed)
self.set_size_request(320, 200)
I embed it here:
import vlc
import sys
from widgets.player import PlayerWidget
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class VideoPlayer(Gtk.Window):
CONST_APP_NAME = "video player"
def __init__(self):
Gtk.Window.__init__(self)
if 'linux' in sys.platform:
self.instance = vlc.Instance("--no-xlib")
else:
self.instance = vlc.Instance()
self.set_default_size(800, 600)
header = Gtk.HeaderBar(title=self.CONST_APP_NAME)
header.set_subtitle("Filename.mp4")
header.set_show_close_button(True) # this one is the troublemaker
self.set_titlebar(header)
self.connect("destroy", Gtk.main_quit)
self.player_widget = PlayerWidget(self.instance)
self.add(self.player_widget)
def show_window(self):
self.show_all()
Gtk.main()
def set_media(self, fname):
self.player_widget.player.set_media(self.instance.media_new(fname))
def play(self):
self.player_widget.play()
if not len(sys.argv) > 0:
print('Please provide a filename')
sys.exit(1)
p = VideoPlayer()
p.set_media(sys.argv[1])
p.play()
p.show_window()
p.instance.release()
It works fine if I embed it into an empty Gtk.window. If, however, I add a HeaderBar to that window as well and then add a close button to that HeaderBar using set_show_close_button(True) it stops working as expected. The PlayerWidget will not be shown embedded anymore, but instead a new (second) window will be opened where the video is played. If I do not add the close button to the HeaderBar the widget gets embedded just fine.
A warning is thrown to the console: xcb_window window error: X server failure
I first thought it could be because I use gnome under wayland, but it occurs on X as well as on wayland.
Any help is appreciated.
Update 1: Added full code example. When I ran it today, the first time it actually worked as expected, but after that the same bug as described above occured again. Very weird.
As #mtz and #stovfl correctly pointed out, the problem was that I started the video playback (p.play()) before creating the window (p.show_window()).
As suggested I used GLib.idle_add(p.play) to let the window start the playback once it's ready. The GLib module can be imported using from gi.repository import GLib.

Playing video in Gtk in a window with a menubar

I have created a video player in Gtk3 using Gstreamer in Python3. It works except when I add a GtkMenuBar (place 2). It will then either show a black screen, or fail with an exception. The exception references the XInitThreads, which I am calling (Place 1) (I took this from the pitivi project) but this does not seem to make a diffrence.
Question: How do I make this work?
Other things I would like to know:
Why would the menubar break this?
This will clearly break on anything not X, is there some prebuilt component the abstracts this logic and is crossplatform that I am missing?
System:
python3
Gtk3
Ubuntu 16.04
The exception:
[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python3: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.
The code (in as small a form as possible to demonstrate the concept):
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gtk, xlib
from gi.repository import Gst, Gdk, GdkX11, GstVideo
Gst.init(None)
Gst.init_check(None)
# Place 1
from ctypes import cdll
x11 = cdll.LoadLibrary('libX11.so')
x11.XInitThreads()
# [xcb] Unknown request in queue while dequeuing
# [xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
# [xcb] Aborting, sorry about that.
# python3: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.
# (foo.py:31933): Gdk-WARNING **: foo.py: Fatal IO error 11 (Resource temporarily unavailable) on X server :1.
class PipelineManager(object):
def __init__(self, window, pipeline):
self.window = window
if isinstance(pipeline, str):
pipeline = Gst.parse_launch(pipeline)
self.pipeline = pipeline
bus = pipeline.get_bus()
bus.set_sync_handler(self.bus_callback)
pipeline.set_state(Gst.State.PLAYING)
def bus_callback(self, bus, message):
if message.type is Gst.MessageType.ELEMENT:
if GstVideo.is_video_overlay_prepare_window_handle_message(message):
Gdk.threads_enter()
Gdk.Display.get_default().sync()
win = self.window.get_property('window')
if isinstance(win, GdkX11.X11Window):
message.src.set_window_handle(win.get_xid())
else:
print('Nope')
Gdk.threads_leave()
return Gst.BusSyncReply.PASS
pipeline = Gst.parse_launch('videotestsrc ! xvimagesink sync=false')
window = Gtk.ApplicationWindow()
header_bar = Gtk.HeaderBar()
header_bar.set_show_close_button(True)
# window.set_titlebar(header_bar) # Place 2
drawing_area = Gtk.DrawingArea()
drawing_area.connect('realize', lambda widget: PipelineManager(widget, pipeline))
window.add(drawing_area)
window.show_all()
def on_destroy(win):
try:
Gtk.main_quit()
except KeyboardInterrupt:
pass
window.connect('destroy', on_destroy)
Gtk.main()
When searching through documentation on a separate issue, I came across a reference to the gtksink widget. This seems to be the correct way to put video in a gtk window, but unfortunately none of the tutorials on this use it.
Using the gtksink widget fixes all the problems and greatly reduces code complexity.
The revised code:
from pprint import pprint
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gtk, Gst
Gst.init(None)
Gst.init_check(None)
class GstWidget(Gtk.Box):
def __init__(self, pipeline):
super().__init__()
self.connect('realize', self._on_realize)
self._bin = Gst.parse_bin_from_description('videotestsrc', True)
def _on_realize(self, widget):
pipeline = Gst.Pipeline()
factory = pipeline.get_factory()
gtksink = factory.make('gtksink')
pipeline.add(gtksink)
pipeline.add(self._bin)
self._bin.link(gtksink)
self.pack_start(gtksink.props.widget, True, True, 0)
gtksink.props.widget.show()
pipeline.set_state(Gst.State.PLAYING)
window = Gtk.ApplicationWindow()
header_bar = Gtk.HeaderBar()
header_bar.set_show_close_button(True)
window.set_titlebar(header_bar) # Place 2
widget = GstWidget('videotestsrc')
widget.set_size_request(200, 200)
window.add(widget)
window.show_all()
def on_destroy(win):
try:
Gtk.main_quit()
except KeyboardInterrupt:
pass
window.connect('destroy', on_destroy)
Gtk.main()

Categories

Resources