Гид по технологиям

Создание видео‑плеера на Python с Tkinter, VLC и datetime

6 min read Python Обновлено 29 Dec 2025
Видео‑плеер на Python: Tkinter + VLC
Видео‑плеер на Python: Tkinter + VLC

Мужчина и женщина смотрят видео на мобильном телефоне

Что делает этот проект

Этот проект показывает, как собрать базовый видео‑плеер, который:

  • открывает локальные видеофайлы (.mp4/.avi);
  • воспроизводит, ставит на паузу, останавливает, перематывает и ускоряет воспроизведение;
  • отображает прогресс и время в формате HH:MM:SS;
  • использует Tkinter для интерфейса и python-vlc для воспроизведения.

Зачем это полезно: вы получаете практику в создании настольных приложений, обработке мультимедиа и управлении событиями в GUI.

Коротко о компонентах (одной строкой)

  • Tkinter — стандартная библиотека для создания GUI в Python.
  • python-vlc — обёртка вокруг библиотеки VLC (VideoLAN) для управления воспроизведением.
  • datetime (timedelta) — для форматирования времени в HH:MM:SS.

Важно: tkinter часто уже установлен с дистрибутивом Python, но на некоторых системах его нужно установить отдельно (см. раздел Установка).

Установка и системные требования

Рекомендуемая среда: Python 3.7+.

Установка зависимостей:

  • python-vlc: pip install python-vlc

Tkinter:

  • Windows / macOS: обычно входит в комплект Python.
  • Linux (Debian/Ubuntu): sudo apt install python3-tk

Дополнительно: VLC (сам плеер/библиотеки) должен присутствовать в системе. На Linux это чаще всего пакет libvlc; на Windows установщик VLC добавит нужные библиотеки.

Советы по установке:

  • Если python-vlc не находит libvlc, убедитесь, что версия VLC (32/64 бита) совпадает с вашей версией Python.
  • На macOS иногда требуется установить VLC через Homebrew или официальный инсталлятор.

Архитектура приложения — кратко

Приложение организовано вокруг класса MediaPlayerApp (наследник tk.Tk). Логика разбита на методы:

  • initialize_player — создаёт экземпляр vlc.Instance и media_player;
  • create_widgets — создаёт холст для видео, кнопки управления, метку времени и полосу прогресса;
  • select_file — диалог выбора файла и запуск воспроизведения;
  • play_video / pause_video / stop / fast_forward / rewind — управляющие действия;
  • get_duration_str / update_video_progress — получение и обновление времени;
  • VideoProgressBar — пользовательский виджет полосы прогресса на основе tk.Scale.

Ниже — основной пример кода, адаптированный для удобного чтения и с типичным кроссплатформенным установлентием окна вывода видео.

import sys
import tkinter as tk
import vlc
from tkinter import filedialog
from datetime import timedelta

class MediaPlayerApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Media Player")
        self.geometry("800x600")
        self.configure(bg="#f0f0f0")
        self.initialize_player()

    def initialize_player(self):
        self.instance = vlc.Instance()
        self.media_player = self.instance.media_player_new()
        self.current_file = None
        self.playing_video = False
        self.video_paused = False
        self.create_widgets()

    def create_widgets(self):
        self.media_canvas = tk.Canvas(self, bg="black", width=800, height=400)
        self.media_canvas.pack(pady=10, fill=tk.BOTH, expand=True)

        self.select_file_button = tk.Button(
            self,
            text="Select File",
            font=("Arial", 12, "bold"),
            command=self.select_file,
        )
        self.select_file_button.pack(pady=5)

        self.time_label = tk.Label(
            self,
            text="00:00:00 / 00:00:00",
            font=("Arial", 12, "bold"),
            fg="#555555",
            bg="#f0f0f0",
        )
        self.time_label.pack(pady=5)

        self.control_buttons_frame = tk.Frame(self, bg="#f0f0f0")
        self.control_buttons_frame.pack(pady=5)

        self.play_button = tk.Button(
            self.control_buttons_frame,
            text="Play",
            font=("Arial", 12, "bold"),
            bg="#4CAF50",
            fg="white",
            command=self.play_video,
        )
        self.play_button.pack(side=tk.LEFT, padx=5, pady=5)

        self.pause_button = tk.Button(
            self.control_buttons_frame,
            text="Pause",
            font=("Arial", 12, "bold"),
            bg="#FF9800",
            fg="white",
            command=self.pause_video,
        )
        self.pause_button.pack(side=tk.LEFT, padx=10, pady=5)

        self.stop_button = tk.Button(
            self.control_buttons_frame,
            text="Stop",
            font=("Arial", 12, "bold"),
            bg="#F44336",
            fg="white",
            command=self.stop,
        )
        self.stop_button.pack(side=tk.LEFT, pady=5)

        self.fast_forward_button = tk.Button(
            self.control_buttons_frame,
            text="Fast Forward",
            font=("Arial", 12, "bold"),
            bg="#2196F3",
            fg="white",
            command=self.fast_forward,
        )
        self.fast_forward_button.pack(side=tk.LEFT, padx=10, pady=5)

        self.rewind_button = tk.Button(
            self.control_buttons_frame,
            text="Rewind",
            font=("Arial", 12, "bold"),
            bg="#2196F3",
            fg="white",
            command=self.rewind,
        )
        self.rewind_button.pack(side=tk.LEFT, pady=5)

        self.progress_bar = VideoProgressBar(
            self, self.set_video_position, bg="#e0e0e0", highlightthickness=0
        )
        self.progress_bar.pack(fill=tk.X, padx=10, pady=5)

    def select_file(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("Media Files", "*.mp4 *.avi")]
        )
        if file_path:
            self.current_file = file_path
            # Время будет вычислено после старта плеера
            self.time_label.config(text="00:00:00 / " + self.get_duration_str())
            self.play_video()

    def get_duration_str(self):
        if self.playing_video:
            total_duration = self.media_player.get_length()
            total_duration_str = str(timedelta(milliseconds=total_duration))[:-3]
            return total_duration_str
        return "00:00:00"

    def play_video(self):
        if not self.playing_video and self.current_file:
            media = self.instance.media_new(self.current_file)
            self.media_player.set_media(media)
            # Кроссплатформенное привязание окна вывода
            if sys.platform.startswith('win'):
                self.media_player.set_hwnd(self.media_canvas.winfo_id())
            elif sys.platform.startswith('linux'):
                self.media_player.set_xwindow(self.media_canvas.winfo_id())
            elif sys.platform == 'darwin':
                self.media_player.set_nsobject(self.media_canvas.winfo_id())
            self.media_player.play()
            self.playing_video = True

    def fast_forward(self):
        if self.playing_video:
            current_time = self.media_player.get_time() + 10000
            self.media_player.set_time(current_time)

    def rewind(self):
        if self.playing_video:
            current_time = self.media_player.get_time() - 10000
            self.media_player.set_time(max(0, current_time))

    def pause_video(self):
        if self.playing_video:
            if self.video_paused:
                self.media_player.play()
                self.video_paused = False
                self.pause_button.config(text="Pause")
            else:
                self.media_player.pause()
                self.video_paused = True
                self.pause_button.config(text="Resume")

    def stop(self):
        if self.playing_video:
            self.media_player.stop()
            self.playing_video = False
        self.time_label.config(text="00:00:00 / " + self.get_duration_str())

    def set_video_position(self, value):
        if self.playing_video:
            total_duration = self.media_player.get_length()
            position = int((float(value) / 100) * total_duration)
            self.media_player.set_time(position)

    def update_video_progress(self):
        if self.playing_video:
            total_duration = self.media_player.get_length()
            current_time = self.media_player.get_time()
            if total_duration > 0:
                progress_percentage = (current_time / total_duration) * 100
                self.progress_bar.set(progress_percentage)
                current_time_str = str(timedelta(milliseconds=current_time))[:-3]
                total_duration_str = str(timedelta(milliseconds=total_duration))[:-3]
                self.time_label.config(text=f"{current_time_str} / {total_duration_str}")
        self.after(1000, self.update_video_progress)


class VideoProgressBar(tk.Scale):
    def __init__(self, master, command, kwargs):
        kwargs["showvalue"] = False
        super().__init__(
            master,
            from_=0,
            to=100,
            orient=tk.HORIZONTAL,
            length=800,
            command=command,
            kwargs,
        )
        self.bind("", self.on_click)

    def on_click(self, event):
        if self.cget("state") == tk.NORMAL:
            value = (event.x / self.winfo_width()) * 100
            self.set(value)


if __name__ == "__main__":
    app = MediaPlayerApp()
    app.update_video_progress()
    app.mainloop()

Примечания по коду и подробности реализации

  • Привязка окна вывода VLC зависит от платформы: set_hwnd (Windows), set_xwindow (Linux), set_nsobject (macOS). Неправильный метод приводит к отсутствию видео в холсте, хотя звук может идти.
  • media_player.get_length() и get_time() возвращают значения в миллисекундах. Перед форматированием следует проверять, что get_length() > 0, иначе деление даст ошибку.
  • Некоторые кодеки/контейнеры могут не предоставлять длину сразу после play(); в таких случаях можно повторно запрашивать длину через небольшую задержку.

Important: если плеер не запускается, проверьте, доступна ли библиотека libvlc, и совпадает ли архитектура (32/64 бит) с Python.

Отладка и распространённые проблемы

  • «Видео не отображается, только звук»: скорее всего, неверный method привязки окна. Проверьте platform и используйте соответствующий метод.
  • «media_player.get_length() возвращает -1»: это значит, что длительность ещё не определена. Подождите несколько сотен миллисекунд после play() и попробуйте снова.
  • «Ошибка при импорте tkinter»: установите пакет python3-tk (Linux) или убедитесь, что используете стандартный Python с включённым Tk.
  • Проблемы с путями к файлам с кириллицей: убедитесь, что кодировка файловой системы поддерживается, и используйте пути в формате Unicode (строки Python 3 уже поддерживают это).

Тестирование и критерии приёмки

Критерии приёмки:

  • При выборе файла воспроизведение начинается автоматически.
  • Кнопки Play/Pause/Stop/Fast Forward/Rewind работают корректно.
  • Полоса прогресса отражает текущее положение и позволяет переходить по клику/перетаскиванию.
  • Время отображается в формате HH:MM:SS и корректно обновляется.

Минимальные тесты (Test cases):

  1. Открыть .mp4, проверить, что видео и звук воспроизводятся.
  2. Нажать Pause — воспроизведение приостанавливается; кнопка меняет текст на Resume.
  3. Нажать Fast Forward — позиция увеличилась примерно на 10 секунд.
  4. Перемещение по прогресс‑бару — видео переходит к соответствующему моменту.
  5. Выбрать файл с кириллическим именем — открывается и воспроизводится.

Роль‑ориентированные чеклисты

Разработчик:

  • Проверить корректность привязки окна для каждой ОС.
  • Обработать ошибки get_length() == -1.
  • Добавить логи для ошибок libvlc.

Тестировщик:

  • Прогнать тесты из раздела выше на Windows/macOS/Linux.
  • Проверить работу с разными кодеками и контейнерами.

Конечный пользователь (README):

  • Установить VLC и python-vlc.
  • Запустить скрипт и выбрать файл через кнопку Select File.

Варианты расширения (идеи)

  • Поддержка субтитров (загрузка .srt через vlc.Media.add_slave или использование функционала VLC).
  • Регулировка громкости и mute.
  • Изменение соотношения сторон и масштабирования видео.
  • Плейлист и очередь воспроизведения.
  • Горячие клавиши для основных действий.

Альтернативные подходы:

  • Pygame / SDL — можно использовать для рендеринга и управления мультимедиа в более игровом контексте.
  • PyQt / PySide — для более современного интерфейса и расширенной виджетной базы.

Кого выбрать: если нужна простота и отсутствие внешних зависимостей от GUI‑фреймворка — Tkinter + python-vlc подходит. Для более «desktop»-ориентированного интерфейса — PyQt/PySide рекомендуется.

Мозговые модели и рекомендации по реализации (heuristics)

  • Разделяйте GUI и логику управления плеером: медиа‑операции — в отдельном слое, GUI только отправляет команды и отображает состояние.
  • Всегда проверяйте возвращаемые значения vlc (длительность, текущее время) и защищайте от деления на ноль.
  • Делегируйте обновление UI в основной поток Tkinter через after(), чтобы не блокировать окно.

Совместимость и локальные нюансы

  • Windows: используйте set_hwnd. Убедитесь, что VLC установлен и видим в PATH при использовании python-vlc.
  • Linux: возможно, потребуется установить пакеты libvlc-dev и сборки для поддержки конкретных кодеков.
  • macOS: для set_nsobject иногда нужно дополнительно разбирать типы объектов Cocoa; проверяйте документацию python-vlc.

Локальные рекомендации для русскоязычных пользователей:

  • Диалог открытия файла корректно работает с кириллицей в имени. Если возникают проблемы, тестируйте в окружении с UTF‑8 по умолчанию.

Безопасность и приватность

  • Программа работает локально и не отправляет данные по сети по умолчанию.
  • Если добавляете функциональность сетевой загрузки, учтите обработку опасных форматов и проверку целостности.

Небольшой плейбук для добавления новой функциональности (mini‑methodology)

  1. Запланировать требование и критерии приёмки.
  2. Добавить интерфейс (кнопка/меню) и заглушку‑обработчик.
  3. Реализовать логику в отдельном методе MediaPlayerApp.
  4. Написать тесты и прогнать их на всех целевых ОС.
  5. Провести ручное тестирование с разнообразными файлами.

Примеры улучшений: короткие сниппеты

  • Установка громкости:
# volume: 0..100
self.media_player.audio_set_volume(80)
  • Проверка готовности длительности с таймаутом:
def wait_for_length(player, timeout_ms=2000):
    import time
    waited = 0
    while player.get_length() <= 0 and waited < timeout_ms:
        time.sleep(0.1)
        waited += 100

Скриншоты интерфейса (оригинальные файлы)

Экран запуска плеера

Когда вы выбираете видео, оно автоматически стартует, а метки времени обновляются.

Видео воспроизводится в плеере

При нажатии Pause кнопка меняет текст на Resume; Fast Forward пропускает ~10 секунд.

Кнопка Pause меняет текст на Resume при паузе

Когда этот подход может не подойти (counterexamples)

  • Требуется аппаратное ускорение специфичного видеодекодирования: VLC его поддерживает, но могут потребоваться дополнительные настройки или другие библиотеки.
  • Нужен очень «тяжёлый» UI с большим количеством кастомизации — лучше использовать Qt.

Итоговое резюме

Создание базового видео‑плеера на Python с Tkinter и python-vlc — хороший учебный проект, который даёт практику в GUI, обработке мультимедиа и управлении состоянием. Начните с минимального примера, проверьте кроссплатформенную привязку окна вывода и затем добавляйте функции: субтитры, регуляторы громкости, плейлисты.

Важно: тестируйте на целевых системах и учитывайте различия в поведении библиотеки VLC на разных платформах.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как найти и выбрать зарядные станции для электромобиля
Электромобили

Как найти и выбрать зарядные станции для электромобиля

Трекер чтения и виртуальная полка в Notion
Продуктивность

Трекер чтения и виртуальная полка в Notion

Сберегательный счёт Apple для держателей Apple Card
Финансы

Сберегательный счёт Apple для держателей Apple Card

Карточки в Google Таблицах: Flippity шаг за шагом
Образование

Карточки в Google Таблицах: Flippity шаг за шагом

Избегать платных дорог и шоссе в Google Maps
Навигация

Избегать платных дорог и шоссе в Google Maps

Массовые выплаты PayPal: экономьте на комиссиях
Платежи

Массовые выплаты PayPal: экономьте на комиссиях