arch-scripts/simple_tui.py
2025-09-08 04:47:13 -06:00

265 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Simple TUI for managing wallpaper scripts using curses
"""
import curses
import subprocess
import sys
import os
from pathlib import Path
class WallpaperTUI:
def __init__(self, stdscr):
self.stdscr = stdscr
self.scripts_dir = Path(__file__).parent
self.sort_script = self.scripts_dir / "sort_images_by_color.py"
self.sync_script = self.scripts_dir / "wallpapersync.sh"
self.wallpaper_menu = self.scripts_dir / "pywal" / "wallpapermenu.sh"
self.waybar_script = self.scripts_dir / "pywal" / "update-waybar-theme.sh"
self.openrgb_script = self.scripts_dir / "pywal" / "pywal-openrgb.py"
# Initialize colors
curses.start_color()
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK)
self.current_selection = 0
self.running = True
def draw_menu(self):
self.stdscr.clear()
height, width = self.stdscr.getmaxyx()
# Title
title = "Wallpaper Manager TUI"
self.stdscr.addstr(1, (width - len(title)) // 2, title, curses.color_pair(2) | curses.A_BOLD)
# Menu items
menu_items = [
"1. Sort and Rename Images",
"2. Sync Wallpapers",
"3. Select Wallpaper (Menu)",
"4. Update Waybar Theme",
"5. Set OpenRGB Color",
"6. Exit"
]
start_y = height // 4
for i, item in enumerate(menu_items):
y = start_y + i * 2
if i == self.current_selection:
self.stdscr.addstr(y, width // 2 - len(item) // 2, item, curses.color_pair(4) | curses.A_REVERSE)
else:
self.stdscr.addstr(y, width // 2 - len(item) // 2, item, curses.color_pair(1))
# Instructions
instructions = "Use arrow keys to navigate, Enter to select, Q to quit"
self.stdscr.addstr(height - 2, width // 2 - len(instructions) // 2, instructions, curses.color_pair(1))
self.stdscr.refresh()
def run_sort(self):
try:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Running sort script...", curses.color_pair(2))
self.stdscr.refresh()
result = subprocess.run([sys.executable, str(self.sort_script), "."],
capture_output=True, text=True, check=True)
self.stdscr.addstr(3, 1, "Sort completed successfully!", curses.color_pair(2))
self.stdscr.addstr(5, 1, "Output:", curses.color_pair(1))
output_lines = result.stdout.split('\n')
for i, line in enumerate(output_lines[:15]): # Show first 15 lines
if i < 14:
self.stdscr.addstr(6 + i, 1, line[:width-2])
self.stdscr.addstr(22, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except subprocess.CalledProcessError as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Sort failed!", curses.color_pair(3))
self.stdscr.addstr(3, 1, "Error:", curses.color_pair(1))
error_lines = e.stderr.split('\n')
for i, line in enumerate(error_lines[:10]):
if i < 9:
self.stdscr.addstr(5 + i, 1, line[:width-2])
self.stdscr.addstr(16, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except Exception as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Error running sort script:", curses.color_pair(3))
self.stdscr.addstr(3, 1, str(e), curses.color_pair(1))
self.stdscr.addstr(5, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
def run_sync(self):
try:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Running sync script...", curses.color_pair(2))
self.stdscr.refresh()
result = subprocess.run([str(self.sync_script)],
capture_output=True, text=True, check=True)
self.stdscr.addstr(3, 1, "Sync completed successfully!", curses.color_pair(2))
self.stdscr.addstr(5, 1, "Output:", curses.color_pair(1))
output_lines = result.stdout.split('\n')
for i, line in enumerate(output_lines[:15]): # Show first 15 lines
if i < 14:
self.stdscr.addstr(6 + i, 1, line[:width-2])
self.stdscr.addstr(22, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except subprocess.CalledProcessError as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Sync failed!", curses.color_pair(3))
self.stdscr.addstr(3, 1, "Error:", curses.color_pair(1))
error_lines = e.stderr.split('\n')
for i, line in enumerate(error_lines[:10]):
if i < 9:
self.stdscr.addstr(5 + i, 1, line[:width-2])
self.stdscr.addstr(16, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except Exception as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Error running sync script:", curses.color_pair(3))
self.stdscr.addstr(3, 1, str(e), curses.color_pair(1))
self.stdscr.addstr(5, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
def run_wallpaper_menu(self):
try:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Opening wallpaper menu...", curses.color_pair(2))
self.stdscr.addstr(3, 1, "(Interactive menu will open)", curses.color_pair(1))
self.stdscr.addstr(5, 1, "Note: This requires nsxiv to be installed.", curses.color_pair(1))
self.stdscr.addstr(7, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except Exception as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Error opening wallpaper menu:", curses.color_pair(3))
self.stdscr.addstr(3, 1, str(e), curses.color_pair(1))
self.stdscr.addstr(5, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
def run_waybar_update(self):
try:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Waybar theme update initiated", curses.color_pair(2))
self.stdscr.addstr(3, 1, "Note: This requires a wallpaper path.", curses.color_pair(1))
self.stdscr.addstr(5, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except Exception as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Error updating waybar:", curses.color_pair(3))
self.stdscr.addstr(3, 1, str(e), curses.color_pair(1))
self.stdscr.addstr(5, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
def run_openrgb(self):
try:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Setting OpenRGB color...", curses.color_pair(2))
self.stdscr.refresh()
result = subprocess.run([sys.executable, str(self.openrgb_script)],
capture_output=True, text=True, check=True)
self.stdscr.addstr(3, 1, "OpenRGB color set successfully!", curses.color_pair(2))
self.stdscr.addstr(5, 1, "Output:", curses.color_pair(1))
output_lines = result.stdout.split('\n')
for i, line in enumerate(output_lines[:10]): # Show first 10 lines
if i < 9:
self.stdscr.addstr(6 + i, 1, line[:width-2])
self.stdscr.addstr(16, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except subprocess.CalledProcessError as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "OpenRGB failed!", curses.color_pair(3))
self.stdscr.addstr(3, 1, "Error:", curses.color_pair(1))
error_lines = e.stderr.split('\n')
for i, line in enumerate(error_lines[:10]):
if i < 9:
self.stdscr.addstr(5 + i, 1, line[:width-2])
self.stdscr.addstr(16, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
except Exception as e:
self.stdscr.clear()
self.stdscr.addstr(1, 1, "Error setting OpenRGB color:", curses.color_pair(3))
self.stdscr.addstr(3, 1, str(e), curses.color_pair(1))
self.stdscr.addstr(5, 1, "Press any key to continue...", curses.color_pair(1))
self.stdscr.refresh()
self.stdscr.getch()
def handle_input(self, key):
if key == ord('q') or key == ord('Q'):
self.running = False
elif key == curses.KEY_UP:
self.current_selection = max(0, self.current_selection - 1)
elif key == curses.KEY_DOWN:
self.current_selection = min(5, self.current_selection + 1)
elif key == 10: # Enter key
if self.current_selection == 0:
self.run_sort()
elif self.current_selection == 1:
self.run_sync()
elif self.current_selection == 2:
self.run_wallpaper_menu()
elif self.current_selection == 3:
self.run_waybar_update()
elif self.current_selection == 4:
self.run_openrgb()
elif self.current_selection == 5:
self.running = False
def run(self):
curses.curs_set(0) # Hide cursor
self.stdscr.nodelay(False)
self.stdscr.timeout(100)
while self.running:
self.draw_menu()
key = self.stdscr.getch()
self.handle_input(key)
curses.endwin()
def main():
# Check if we're in a terminal
if not sys.stdout.isatty():
print("This program must be run in a terminal.")
return
try:
curses.wrapper(WallpaperTUI)
except KeyboardInterrupt:
print("Exiting...")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
main()