265 lines
11 KiB
Python
Executable File
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()
|