Last active
June 23, 2016 16:39
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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