Skip to content

Instantly share code, notes, and snippets.

@Cimbali
Last active June 23, 2016 16:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Cimbali/86a081e78b0ffc691b50 to your computer and use it in GitHub Desktop.
Save Cimbali/86a081e78b0ffc691b50 to your computer and use it in GitHub Desktop.
MCVE can't hide VLC player after it stopped, when running Python with Gtk+3 and VLC bindings in windows
#!/usr/bin/env python
from __future__ import print_function
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject, GLib
import ctypes
import sys, os
import vlc
# let python find the DLLs
os.environ['PATH'] = vlc.plugin_path + ';' + os.environ['PATH']
# Create a single vlc.Instance() to be shared by (possible) multiple players.
instance = vlc.Instance()
def get_window_handle(window):
''' Uses ctypes to call gdk_win32_window_get_handle which is not available
in python gobject introspection porting (yet ?)
Solution from http://stackoverflow.com/a/27236258/1387346
'''
# get the c gpointer of the gdk window
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object]
drawingarea_gpointer = ctypes.pythonapi.PyCapsule_GetPointer(window.__gpointer__, None)
# get the win32 handle
gdkdll = ctypes.CDLL('libgdk-3-0.dll')
return gdkdll.gdk_win32_window_get_handle(drawingarea_gpointer)
class VLCVideoDA(Gtk.DrawingArea):
''' Simple VLC widget.
Its player can be controlled through the 'player' attribute, which
is a vlc.MediaPlayer() instance.
'''
player = None
controls = None
relative_margins = None
def __init__(self, show_controls, relative_margins):
Gtk.DrawingArea.__init__(self)
self.relative_margins = relative_margins
self.player = instance.media_player_new()
event_manager = self.player.event_manager()
event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.hide)
def handle_embed(*args):
# we need to be on the main thread (espcially for the mess from the win32 window handle)
#assert isinstance(threading.current_thread(), threading._MainThread)
if sys.platform.startswith('win'):
self.player.set_hwnd(get_window_handle(self.get_window())) # get_property('window')
else:
self.player.set_xwindow(self.get_window().get_xid())
return True
self.connect('map', handle_embed)
self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
self.connect('button-press-event', self.on_click)
def get_player_control_toolbar(self):
''' Return a player control toolbar.
'''
tb = Gtk.Toolbar()
tb.set_style(Gtk.ToolbarStyle.ICONS)
tb.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(0, 0, 0))
for text, tooltip, stock, callback in (
('Play', 'Play', Gtk.STOCK_MEDIA_PLAY, lambda b: self.play()),
('Pause', 'Pause', Gtk.STOCK_MEDIA_PAUSE, lambda b: self.pause()),
('Stop', 'Stop', Gtk.STOCK_MEDIA_STOP, lambda b: self.hide()),
):
b=Gtk.ToolButton(stock)
b.set_tooltip_text(tooltip)
b.connect('clicked', callback)
tb.insert(b, -1)
return tb
def resize(self):
parent = self.get_parent()
if not parent:
return
pw, ph = parent.get_allocated_width(), parent.get_allocated_height()
self.props.margin_left = pw * self.relative_margins[0]
self.props.margin_right = pw * self.relative_margins[1]
self.props.margin_bottom = ph * self.relative_margins[2]
self.props.margin_top = ph * self.relative_margins[3]
def set_file(self, filepath):
''' Sets the media file to be played bu the widget.
'''
GLib.idle_add(lambda: self.player.set_media(instance.media_new(filepath)))
def play(self):
''' Start playing the media file.
'''
GLib.idle_add(lambda: self.player.play())
def pause(self):
''' Start playing the media file.
'''
GLib.idle_add(lambda: self.player.pause())
def on_click(self, widget, event):
''' React to click events by playing or pausing the media.
'''
if event.type == Gdk.EventType.BUTTON_PRESS:
GLib.idle_add(lambda: self.player.pause() if self.player.is_playing() else self.player.play())
elif event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
GLib.idle_add(lambda: self.player.set_time(0)) # in ms
def hide(self, *args):
''' Hide widget.
'''
GLib.idle_add(lambda: self.player.stop())
def main():
try:
movie = os.path.expanduser(sys.argv[1])
except IndexError:
movie = 'video.mpeg' # very short movie I use for testing: https://cimba.li/video.mpeg
if not os.access(movie, os.R_OK):
print("Can't open movie \"{}\", try passing another file on the command line:".format(movie))
print("{} /path/to/movie".format(sys.argv[0]))
exit(-1)
win = Gtk.Window(Gtk.WindowType.TOPLEVEL)
black = Gdk.Color(0, 0, 0)
win.modify_bg(Gtk.StateType.NORMAL, black)
win.set_default_size(800, 600)
win.connect("destroy", Gtk.main_quit)
win.connect("delete-event", Gtk.main_quit)
relative_margins = (0.2, 0.2, 0.2, 0.2)
vidWidget = VLCVideoDA(True, relative_margins)
vidWidget.set_file(movie)
vbox = Gtk.VBox()
vbox.pack_start(vidWidget, True, True, 0)
vbox.pack_end(vidWidget.get_player_control_toolbar(), False, False, 0)
win.add(vbox)
win.show_all()
Gtk.main()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment