In this tutorial, we’ll create VID Splitter, a lightweight desktop app that splits videos into short clips. It supports MP4, AVI, and MOV files, shows a live preview of total duration and clips, and includes a progress bar and live log.
We’ll use Python, Tkinter, ttkbootstrap, and OpenCV. This guide is beginner-friendly—no prior GUI experience needed.
Step 1: Set Up the Project
First, install the required Python packages:
pip install opencv-python ttkbootstrap
opencv-python: For reading/writing video files.
ttkbootstrap: For a modern, styled Tkinter interface.
Create a Python file, e.g., vid_splitter.py.
Step 2: Import Libraries
At the top of your file, import the necessary modules:
import sys
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox
import ttkbootstrap as tb
from ttkbootstrap.widgets.scrolled import ScrolledText
import threading
import cv2
import os
import math
Explanation:
tkinter and ttkbootstrap are used to create the GUI.
threading allows the video splitting to run in the background.
cv2 (OpenCV) handles video processing.
math and os help with file and clip calculations.
Step 3: Create the Main App Window
Initialize the Tkinter window and set its title, size, and style:
app = tk.Tk()
style_obj = tb.Style(theme="superhero") # Modern UI theme
app.title("VID Splitter v1.0.0")
app.geometry("950x600")
Explanation:
tb.Style from ttkbootstrap lets you use sleek themes.
geometry sets the window size.
Step 4: Input & Output Selection
We need users to select a video file and an output folder.
# Input video
video_path = tk.StringVar()
tb.Label(app, text="Video Input").pack()
tb.Entry(app, textvariable=video_path).pack(fill="x")
tb.Button(app, text="Browse Video", bootstyle="info",
command=lambda: video_path.set(filedialog.askopenfilename(
filetypes=[("Video Files", "*.mp4 *.avi *.mov")]
))).pack()
# Output folder
output_dir = tk.StringVar()
tb.Label(app, text="Output Folder").pack()
tb.Entry(app, textvariable=output_dir).pack(fill="x")
tb.Button(app, text="Browse Folder", bootstyle="info",
command=lambda: output_dir.set(filedialog.askdirectory())).pack()
Explanation:
StringVar stores dynamic text values for GUI entries.
filedialog opens file/folder selection dialogs.
Step 5: Add Clip Settings
Let users set clip length and filename prefix:
clip_length = tk.IntVar(value=5)
file_prefix = tk.StringVar(value="clip")
tb.Label(app, text="Clip Length (seconds)").pack()
tb.Entry(app, textvariable=clip_length, width=5).pack()
tb.Label(app, text="Filename Prefix").pack()
tb.Entry(app, textvariable=file_prefix, width=15).pack()
Explanation:
IntVar stores numeric values for clip length.
Users can define how many seconds each clip should be.
Step 6: Live Preview of Clips
Show total video duration and how many clips will be generated:
preview_label = tb.Label(app, text="Total: --s | Clips: --")
preview_label.pack()
def update_preview():
path = video_path.get()
if not path or clip_length.get() <= 0:
preview_label.config(text="Total: --s | Clips: --")
return
cap = cv2.VideoCapture(path)
if not cap.isOpened():
preview_label.config(text="Total: --s | Clips: --")
return
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
duration = total_frames / fps
clips = math.ceil(duration / clip_length.get())
preview_label.config(text=f"Total: {duration:.2f}s | Clips: {clips}")
cap.release()
video_path.trace_add("write", lambda *args: update_preview())
clip_length.trace_add("write", lambda *args: update_preview())
Explanation:
OpenCV reads the video and calculates total duration.
trace_add automatically updates the preview when the user changes inputs.
Step 7: Add a Live Log & Progress Bar
# Live log
log_card = tb.Labelframe(app, text="Live Output")
log_card.pack(fill="both", expand=True)
log = ScrolledText(log_card)
log.pack(fill="both", expand=True)
log.text.config(state="disabled", height=5)
# Progress bar
progress = tb.Progressbar(app)
progress.pack(fill="x", pady=5)
Explanation:
Users can see each clip as it’s saved.
Progress bar gives real-time feedback.
Step 8: Video Splitting Logic
Here’s the core function:
stop_event = threading.Event()
def split_video():
if not video_path.get() or not output_dir.get():
messagebox.showerror("Missing Input", "Please select video and output folder.")
return
cap = cv2.VideoCapture(video_path.get())
fps = cap.get(cv2.CAP_PROP_FPS)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
frames_per_clip = int(fps * clip_length.get())
clip_count = 0
frame_idx = 0
while frame_idx < total_frames:
remaining_frames = total_frames - frame_idx
current_clip_frames = min(frames_per_clip, remaining_frames)
frames = []
for _ in range(current_clip_frames):
ret, frame = cap.read()
if not ret:
break
frames.append(frame)
frame_idx += 1
progress.configure(value=(frame_idx / total_frames) * 100)
if not frames:
break
clip_name = f"{file_prefix.get()}_{clip_count:03d}.mp4"
clip_path = os.path.join(output_dir.get(), clip_name)
h, w, _ = frames[0].shape
out = cv2.VideoWriter(clip_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
for f in frames:
out.write(f)
out.release()
clip_count += 1
cap.release()
messagebox.showinfo("Done", f"Total clips: {clip_count}")
Explanation:
Splits the video into chunks based on clip_length.
Saves each clip using OpenCV’s VideoWriter.
Updates the progress bar while processing.
Step 9: Add Buttons to Start/Stop
tb.Button(app, text="Start Splitting", bootstyle="success",
command=lambda: threading.Thread(target=split_video, daemon=True).start()).pack()
tb.Button(app, text="Stop", bootstyle="danger",
command=lambda: stop_event.set()).pack()
Explanation:
Video splitting runs in a background thread so the UI stays responsive.
Stop button lets the user interrupt processing safely.
Step 10: Run the App
Finally, start the Tkinter main loop:
app.mainloop()
Open vid_splitter.py and try splitting a video! 🎬
✅ What You Learned
Creating a modern GUI with Tkinter + ttkbootstrap
Using OpenCV for video processing
Managing threads to keep the UI responsive
Adding live preview, logs, and progress bars

Top comments (0)