#!/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()