diff --git a/requirements.txt b/requirements.txt index 26ae3bf..b1bdfb1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ -tkinter opencv-python==4.8.1.78 pillow==10.0.1 ffmpeg-python==0.2.0 psutil==5.9.5 -threading \ No newline at end of file +sv-ttk \ No newline at end of file diff --git a/vodkeeper.py b/vodkeeper.py index d380fec..5ceda3f 100644 --- a/vodkeeper.py +++ b/vodkeeper.py @@ -9,6 +9,7 @@ from datetime import datetime import cv2 from PIL import Image, ImageTk import ffmpeg +import sv_ttk class VodKeeper: def __init__(self, root): @@ -16,6 +17,21 @@ class VodKeeper: self.root.title("VodKeeper - Video Processing Workflow") self.root.geometry("1200x800") + # Dark mode colors + self.colors = { + 'bg': '#2b2b2b', + 'fg': '#e0e0e0', + 'select_bg': '#0078d4', + 'select_fg': '#ffffff', + 'entry_bg': '#3c3c3c', + 'entry_fg': '#e0e0e0', + 'button_bg': '#404040', + 'button_fg': '#e0e0e0', + 'frame_bg': '#383838', + 'text_bg': '#1e1e1e', + 'text_fg': '#e0e0e0' + } + # Configuration self.config = { 'input_path': 'R:\\mux\\input', @@ -36,6 +52,67 @@ class VodKeeper: self.setup_ui() self.load_config() + def setup_dark_theme(self): + # Configure root window + self.root.configure(bg=self.colors['bg']) + + # Configure ttk styles for dark theme + style = ttk.Style() + style.theme_use('clam') + + # Configure Notebook (tabs) + style.configure('TNotebook', background=self.colors['bg'], borderwidth=0) + style.configure('TNotebook.Tab', background=self.colors['frame_bg'], foreground=self.colors['fg'], + padding=[10, 4]) + style.map('TNotebook.Tab', background=[('selected', self.colors['select_bg']), + ('active', self.colors['select_bg'])]) + + # Configure Frames + style.configure('TFrame', background=self.colors['bg'], borderwidth=0) + style.configure('TLabelFrame', background=self.colors['bg'], foreground=self.colors['fg'], + borderwidth=1, relief='solid') + style.configure('TLabelFrame.Label', background=self.colors['bg'], foreground=self.colors['fg']) + + # Configure Labels + style.configure('TLabel', background=self.colors['bg'], foreground=self.colors['fg']) + + # Configure Buttons + style.configure('TButton', background=self.colors['button_bg'], foreground=self.colors['button_fg'], + borderwidth=1, relief='solid') + style.map('TButton', background=[('active', self.colors['select_bg']), + ('pressed', self.colors['entry_bg'])]) + + # Configure Entry widgets + style.configure('TEntry', fieldbackground=self.colors['entry_bg'], foreground=self.colors['entry_fg'], + borderwidth=1, insertcolor=self.colors['entry_fg']) + style.map('TEntry', focuscolor=[('!focus', self.colors['entry_bg'])]) + + # Configure Combobox + style.configure('TCombobox', fieldbackground=self.colors['entry_bg'], foreground=self.colors['entry_fg'], + background=self.colors['entry_bg'], borderwidth=1, arrowcolor=self.colors['fg']) + style.map('TCombobox', fieldbackground=[('readonly', self.colors['entry_bg'])], + background=[('readonly', self.colors['entry_bg'])]) + + # Configure Spinbox + style.configure('TSpinbox', fieldbackground=self.colors['entry_bg'], foreground=self.colors['entry_fg'], + background=self.colors['entry_bg'], borderwidth=1, arrowcolor=self.colors['fg']) + + # Configure Checkbutton + style.configure('TCheckbutton', background=self.colors['bg'], foreground=self.colors['fg'], + focuscolor=self.colors['bg']) + + # Configure Progressbar + style.configure('TProgressbar', background=self.colors['select_bg'], troughcolor=self.colors['entry_bg'], + borderwidth=1, lightcolor=self.colors['select_bg'], darkcolor=self.colors['select_bg']) + + # Configure Treeview + style.configure('Treeview', background=self.colors['entry_bg'], foreground=self.colors['entry_fg'], + fieldbackground=self.colors['entry_bg'], borderwidth=1) + style.configure('Treeview.Heading', background=self.colors['frame_bg'], foreground=self.colors['fg'], + borderwidth=1, relief='solid') + style.map('Treeview', background=[('selected', self.colors['select_bg'])], + foreground=[('selected', self.colors['select_fg'])]) + def setup_ui(self): # Create notebook for tabs self.notebook = ttk.Notebook(self.root) @@ -65,7 +142,10 @@ class VodKeeper: # File list ttk.Label(file_frame, text="Available Files:").grid(row=1, column=0, sticky='w', pady=(10,2)) - self.file_listbox = tk.Listbox(file_frame, height=8, width=70) + self.file_listbox = tk.Listbox(file_frame, height=8, width=70, + bg=self.colors['entry_bg'], fg=self.colors['entry_fg'], + selectbackground=self.colors['select_bg'], + selectforeground=self.colors['select_fg']) self.file_listbox.grid(row=2, column=0, columnspan=3, sticky='ew', pady=2) self.file_listbox.bind('<>', self.on_file_select) @@ -106,13 +186,16 @@ class VodKeeper: ttk.Label(bitrate_frame, text="Manual bitrate cap (Mbps):").grid(row=1, column=0, sticky='w', pady=2) self.manual_bitrate_var = tk.DoubleVar(value=0) - ttk.Entry(bitrate_frame, textvariable=self.manual_bitrate_var, width=10).grid(row=1, column=1, padx=5, pady=2) + manual_bitrate_entry = ttk.Entry(bitrate_frame, textvariable=self.manual_bitrate_var, width=10) + manual_bitrate_entry.grid(row=1, column=1, padx=5, pady=2) # File info info_frame = ttk.LabelFrame(input_frame, text="File Information", padding=10) info_frame.pack(fill='x', padx=10, pady=5) - self.file_info_text = scrolledtext.ScrolledText(info_frame, height=6, width=80) + self.file_info_text = scrolledtext.ScrolledText(info_frame, height=6, width=80, + bg=self.colors['text_bg'], fg=self.colors['text_fg'], + insertbackground=self.colors['text_fg']) self.file_info_text.pack(fill='both', expand=True) # Action buttons @@ -165,7 +248,9 @@ class VodKeeper: log_frame = ttk.LabelFrame(render_frame, text="FFmpeg Output", padding=10) log_frame.pack(fill='both', expand=True, padx=10, pady=5) - self.log_text = scrolledtext.ScrolledText(log_frame, height=10) + self.log_text = scrolledtext.ScrolledText(log_frame, height=10, + bg=self.colors['text_bg'], fg=self.colors['text_fg'], + insertbackground=self.colors['text_fg']) self.log_text.pack(fill='both', expand=True) def setup_qc_tab(self): @@ -211,17 +296,19 @@ class VodKeeper: display_frame = ttk.Frame(comparison_frame) display_frame.pack(fill='both', expand=True) - self.source_canvas = tk.Canvas(display_frame, bg='black') + self.source_canvas = tk.Canvas(display_frame, bg=self.colors['text_bg']) self.source_canvas.pack(side='left', fill='both', expand=True, padx=5) - self.output_canvas = tk.Canvas(display_frame, bg='black') + self.output_canvas = tk.Canvas(display_frame, bg=self.colors['text_bg']) self.output_canvas.pack(side='right', fill='both', expand=True, padx=5) # QC results results_frame = ttk.LabelFrame(qc_frame, text="QC Results", padding=10) results_frame.pack(fill='x', padx=10, pady=5) - self.qc_results_text = scrolledtext.ScrolledText(results_frame, height=6) + self.qc_results_text = scrolledtext.ScrolledText(results_frame, height=6, + bg=self.colors['text_bg'], fg=self.colors['text_fg'], + insertbackground=self.colors['text_fg']) self.qc_results_text.pack(fill='both', expand=True) def browse_input(self): @@ -436,6 +523,7 @@ class VodKeeper: def main(): root = tk.Tk() + sv_ttk.use_dark_theme() app = VodKeeper(root) root.mainloop() diff --git a/vodkeeper.spec b/vodkeeper.spec new file mode 100644 index 0000000..5488b6e --- /dev/null +++ b/vodkeeper.spec @@ -0,0 +1,51 @@ +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None + +a = Analysis( + ['vodkeeper.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[ + 'tkinter', + 'tkinter.ttk', + 'tkinter.filedialog', + 'tkinter.messagebox', + 'tkinter.scrolledtext', + 'PIL._tkinter_finder' + ], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) + +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='VodKeeper', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=None, +) \ No newline at end of file