В этом уроке мы узнаем, как создать мультимедийное приложение на Python с помощью LibVLC и GTK+ , которое воспроизводит мультимедиа при его запуске. Пользователи приложения также смогут управлять основными параметрами воспроизведения.
Этот учебник предполагает, что читатели знают, как создавать и запускать файлы Python; понимают основные ошибки интерпретатора; работают со строками, поплавками и булевыми значениями; ориентируются на объект; и представление о потоках в Python. Есть и другие аккуратные советы по питону, о которых стоит знать.
Учебник подходит для начинающих и средних программистов. Этот учебник охватывает GTK+ версии 3 и LibVLC media framework; он был создан и протестирован на Linux.
LibVLC
Это внешний программный интерфейс VLC media player. LibVLC (VLC SDK) – это основной движок и интерфейс мультимедийной платформы, на которой основан VLC media player. Он может быть встроен в приложения для получения мультимедийных возможностей. Поэтому приложение должно иметь те же функции, что и VLC media player.
Зависимости
VLC разработан на языке C, поэтому установите его перед использованием через Python.Привязки Python имеют полный охват API LibVLC, а сгенерированный модуль находится в чистом Python. Это зависит только от типов .
Скачать vlc.py модуль из репозитория Git и поместите модуль в место, доступное Python.
GTK+
Инструментарий для создания графических пользовательских интерфейсов, написанных на языке программирования Си.
Вы также должны знать об этих вещах:
GDK : набор GIMP Drawing Kit находится между библиотекой xlib и библиотекой GTK+, обрабатывая базовые рендеринги, такие как примитивы рисования, растровая графика (растровые изображения), курсоры, шрифты, а также оконные события и функции перетаскивания. Он содержит бэк-энды к нескольким оконным системам, таким как X11.
GdkX11 : Также является частью пакета GObject для взаимодействия с X Window System и специфичных для X backend функций.
PyGObject : Модуль Python ,который позволяет разработчикам получать доступ к библиотекам на основе GObject, таким как GTK+, Gdk, Gdk X11 и т. Д.
Установка
PyGObject и его зависимости упаковываются всеми основными дистрибутивами Linux. Итак, установите пакет из официального репозитория дистрибутива.
В приведенном ниже блоке кода показано, как импортировать библиотеку Gtk+ из пакета GI (gobject-introspection). Поскольку пользователи могут иметь несколько версий GTK+ в своих системах, импортируйте GTK, который относится к GTK+ 3, а не к какой-либо другой версии библиотеки, что является целью оператора gi.require_version('Gtk', '3.0')
.
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk
Компоненты приложения
I. Окно приложения
Это подкласс Gtk.Window
чтобы определить свой собственный Application Window
класс для создания окна верхнего уровня, содержащего другие виджеты.
В конструкторе класса вызовите конструктор суперкласса и задайте значение заголовка свойства Python-Vlc Media Player .
class ApplicationWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Python-Vlc Media Player")
Кнопки управления мультимедиа
self.playback_button = Gtk.Button() self.stop_button = Gtk.Button()
Изображения, отображаемые на кнопках
Мы используем встроенные изображения, предоставляемые библиотекой GTK+.
self.play_image = Gtk.Image.new_from_icon_name( "gtk-media-play", Gtk.IconSize.MENU ) self.pause_image = Gtk.Image.new_from_icon_name( "gtk-media-pause", Gtk.IconSize.MENU ) self.stop_image = Gtk.Image.new_from_icon_name( "gtk-media-stop", Gtk.IconSize.MENU )
Установите изображения на кнопках.
self.playback_button.set_image(self.play_image) self.stop_button.set_image(self.stop_image)
события и сигналы в GTK+
- GTK+-это система, управляемая событиями. Все графические приложения управляются событиями.
- Приложения запускаются в основном цикле, который непрерывно проверяет наличие вновь сгенерированных событий. Если события нет, приложение ждет и ничего не делает.
- В GTK+ событие-это сообщение от X-сервера . Когда событие достигает виджета, он может отреагировать на это событие, испустив сигнал.
- Программист GTK+ может подключить к сигналу определенный обратный вызов. Обратный вызов-это функция обработчика, которая реагирует на сигнал.
- Функция обработчика принимает два параметра: Первый параметр-это объект, который испустил сигнал; в нашем случае это кнопка воспроизведения/паузы. Второй параметр является необязательным, когда можно отправлять события/данные, генерируемые при срабатывании сигнала. Например: тип данных может быть Gdk.EventType .
Вот ссылка, чтобы узнать больше о Событийном программировании .
Соедините кнопки с щелкающими сигналами.
self.playback_button.connect("clicked", self.toggle_player_playback) self.stop_button.connect("clicked", self.stop_player)
Функция toggle_player_playback
обрабатывает переключение между опциями воспроизведения/паузы и соответственно устанавливает playback_button
изображение.
def toggle_player_playback(self, widget, data=None): """ Handler for Player's Playback Button (Play/Pause). """ if self.is_player_active == False and self.player_paused == False: self.player.play() self.playback_button.set_image(self.pause_image) self.is_player_active = True elif self.is_player_active == True and self.player_paused == True: self.player.play() self.playback_button.set_image(self.pause_image) self.player_paused = False elif self.is_player_active == True and self.player_paused == False: self.player.pause() self.playback_button.set_image(self.play_image) self.player_paused = True else: pass
Функция stop_player
останавливает воспроизведение мультимедиа и устанавливает кнопку воспроизведения для воспроизведения изображения.
def stop_player(self, widget, data=None): self.player.stop() self.is_player_active = False self.playback_button.set_image(self.play_image)
Поверхность Рендеринга носителей
Чтобы рисовать на экране, мы используем виджет DrawingArea. Виджет области рисования-это, по сути, X-окно и ничего больше. Это чистый холст, на котором мы можем рисовать все, что захотим. Область рисования создается с помощью вызова ниже.
Размер по умолчанию может быть переопределен, как и для всех виджетов, вызовом set_size_request_
, а это, в свою очередь, также может быть переопределено, если пользователь вручную изменит размер окна, содержащего область рисования.
self.draw_area = Gtk.DrawingArea() self.draw_area.set_size_request(300,300)
Примечание: Этот учебник не охватывает входные сигналы от пользователей GtkDrawingArea. Для захвата событий с устройств ввода, таких как мышь, для этих виджетов нам нужно использовать виджет EventBox .
Подключите область рендеринга к ее сигналу реализации. Сигнал “реализовать” состоит в том, чтобы предпринять любые необходимые действия, когда виджет создается на определенном дисплее. Я расскажу о обработчике _realize
позже в этом уроке.
self.draw_area.connect("realize",self._realized)
Прежде чем обсуждать LibVLC, давайте упакуем виджеты в Gtk.Бокс-виджеты, известные как контейнеры Gtk.
Что такое коробки? Коробки-это невидимые контейнеры, в которые мы можем упаковать наши виджеты. А при упаковке виджетов в горизонтальную коробку объекты вставляются горизонтально слева направо или справа налево в зависимости от того, используется ли Gtk.Box.pack___start()
или Gtk.Box.pack_end ()
.
В вертикальном ящике виджеты упаковываются сверху вниз или наоборот. Мы можем использовать любую комбинацию коробок внутри или рядом с другими коробками, чтобы создать желаемый эффект.
Создайте горизонтально ориентированный контейнер box, в котором между дочерними элементами будет размещено 6 пикселей. Здесь дети-это наши две кнопки управления медиа.
Мы упаковываем вещи с самого начала коробки. Запуск пакета занимает четыре параметра:
-
GtkWidget
будет добавлен в поле. - Boolean expand : TRUE, если новому дочернему элементу должно быть предоставлено дополнительное пространство, выделенное для коробки.
- Boolean fill : TRUE, если пространство, предоставленное дочернему элементу опцией expand, фактически выделено дочернему элементу, а не просто заполняет его.
- padding : дополнительное пространство в пикселях между этим дочерним элементом и его соседями, превышающее глобальную величину, заданную свойством “spacing”.
self.hbox = Gtk.Box(spacing=6) self.hbox.pack_start(self.playback_button, True, True, 0) self.hbox.pack_start(self.stop_button, True, True, 0)
Аналогично, мы создаем вертикально ориентированную коробку и ее дочернее окно верхнего уровня. Затем мы упаковываем сначала поверхность рендеринга, а затем горизонтальную коробку.
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(self.vbox) self.vbox.pack_start(self.draw_area, True, True, 0) self.vbox.pack_start(self.hbox, False, False, 0)
Покажите окно рекурсивно и любые дочерние виджеты.
self.show_all()
Ниже приведен скриншот нашего результирующего графического интерфейса приложения.
II. Воспроизведение мультимедиа с помощью фреймворка LibVLC vlc
import vlc
Примечание:
- Область рисования должна быть реализована до того, как получить ее Гдк.Окно.
- Мы не можем получить свойство окна напрямую.
- Нам нужно импортировать GdkX11 для метода xid, потому что окно рендеринга поверхностей является экземпляром Gdk X11.X11Window .
- GObject.Object -> Gdk.Окно -> Окно Gdk X11.X11
- Следовательно, импортируйте GdkX11.
gi.require_version('GdkX11', '3.0') from gi.repository import GdkX11
Как только поверхность рендеринга реализована, ее функция обработчика выполняет следующие операции:
Создайте и инициализируйте экземпляр LibVLC и передайте
"--no-xlib"
в качестве параметра.Создайте пустой объект медиаплеера.
self.vlcInstance = vlc.Instance("--no-xlib") self.player = self.vlcInstance.media_player_new()
Почему это “–нет-xlib” перешел к vlcInstance ?
Как правило, когда несколько потоков используют Xlib (также известный как libX11) одновременно, необходимо вызвать функцию XInitThreads ()
, так как здесь мы не имеем дела с потоками, использующими xlib. Таким образом, уведомление LibVLC о том, что Xlib не инициализируется для потоков.
- Получите идентификатор окна окна Media Rendering Surfaces X11 и подключите плеер к окну для вывода мультимедиа.
win_id = widget.get_window().get_xid() self.player.set_xwindow(win_id)
- Установите MRL (Media Resource Locator — используется LibVLC) для воспроизведения.
self.player.set_mrl(media_url)
Положив его совсем
import sys import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk gi.require_version('GdkX11', '3.0') from gi.repository import GdkX11 import vlc MRL = "" class ApplicationWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Python-Vlc Media Player") self.player_paused=False self.is_player_active = False self.connect("destroy",Gtk.main_quit) def show(self): self.show_all() def setup_objects_and_events(self): self.playback_button = Gtk.Button() self.stop_button = Gtk.Button() self.play_image = Gtk.Image.new_from_icon_name( "gtk-media-play", Gtk.IconSize.MENU ) self.pause_image = Gtk.Image.new_from_icon_name( "gtk-media-pause", Gtk.IconSize.MENU ) self.stop_image = Gtk.Image.new_from_icon_name( "gtk-media-stop", Gtk.IconSize.MENU ) self.playback_button.set_image(self.play_image) self.stop_button.set_image(self.stop_image) self.playback_button.connect("clicked", self.toggle_player_playback) self.stop_button.connect("clicked", self.stop_player) self.draw_area = Gtk.DrawingArea() self.draw_area.set_size_request(300,300) self.draw_area.connect("realize",self._realized) self.hbox = Gtk.Box(spacing=6) self.hbox.pack_start(self.playback_button, True, True, 0) self.hbox.pack_start(self.stop_button, True, True, 0) self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(self.vbox) self.vbox.pack_start(self.draw_area, True, True, 0) self.vbox.pack_start(self.hbox, False, False, 0) def stop_player(self, widget, data=None): self.player.stop() self.is_player_active = False self.playback_button.set_image(self.play_image) def toggle_player_playback(self, widget, data=None): """ Handler for Player's Playback Button (Play/Pause). """ if self.is_player_active == False and self.player_paused == False: self.player.play() self.playback_button.set_image(self.pause_image) self.is_player_active = True elif self.is_player_active == True and self.player_paused == True: self.player.play() self.playback_button.set_image(self.pause_image) self.player_paused = False elif self.is_player_active == True and self.player_paused == False: self.player.pause() self.playback_button.set_image(self.play_image) self.player_paused = True else: pass def _realized(self, widget, data=None): self.vlcInstance = vlc.Instance("--no-xlib") self.player = self.vlcInstance.media_player_new() win_id = widget.get_window().get_xid() self.player.set_xwindow(win_id) self.player.set_mrl(MRL) self.player.play() self.playback_button.set_image(self.pause_image) self.is_player_active = True if __name__ == '__main__': if not sys.argv[1:]: print "Exiting \nMust provide the MRL." sys.exit(1) if len(sys.argv[1:]) == 1: MRL = sys.argv[1] window = ApplicationWindow() window.setup_objects_and_events() window.show() Gtk.main() window.player.stop() window.vlcInstance.release()
Gtk.main()
Это цикл обработки GTK+, который работает внутри нашего основного потока приложения.
- Когда пользователь ничего не делает, GTK+ сидит в основном цикле и ждет ввода. Если пользователь выполняет какое — то действие — скажем, щелчок мыши, – то основной цикл “просыпается” и доставляет событие в GTK+.
- GTK+ является “потокобезопасным”, но не потокобезопасным — он обеспечивает глобальную блокировку, управляемую
gdk_threads_enter()
иgdk_threads_leave ()
, которая защищает все использование GTK+. То есть только один поток может использовать GTK+ в любой момент времени. - Вызов
Gtk.main_quit()
делает основной цикл внутриGtk.main()
return. - Таким образом, подключение сигнала “destroy” к
Gtk.main_quit()
уничтожит окно, а также завершит работу нашего приложения.
Ниже приведен скриншот, показывающий медиаплеер, воспроизводимый в нашем медиаплеере.
Похоже, что мы подошли к концу этого урока. Однако это приложение может быть расширено, чтобы добавить больше мультимедийных возможностей и функций пользовательского интерфейса. Поэтому вы можете изучить обе библиотеки, чтобы добавить более надежные функции в свое приложение.
Спасибо! Надеюсь, вам понравился урок. Пожалуйста, оставьте свои вопросы в разделе комментариев ниже.