trying to make a TUI

This commit is contained in:
nicholai 2025-09-08 04:47:13 -06:00
parent 6ccc8606ef
commit b2f5e65c06
2 changed files with 422 additions and 0 deletions

264
simple_tui.py Executable file
View File

@ -0,0 +1,264 @@
#!/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()

158
wallpaper_tui.py Normal file
View File

@ -0,0 +1,158 @@
#!/usr/bin/env python3
"""
TUI for managing wallpaper scripts with Gemini/claude-style interface
"""
import os
import subprocess
import sys
from pathlib import Path
from typing import List, Optional
import asyncio
import threading
from datetime import datetime
import rich
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
from rich.layout import Layout
from rich.live import Live
from rich.prompt import Prompt, Confirm
from rich.syntax import Syntax
from textual.app import App, ComposeResult
from textual.containers import Container, Vertical, Horizontal
from textual.widgets import Button, Header, Footer, Static, Input, Label
from textual import events
from textual.binding import Binding
class WallpaperTUI(App):
"""Main TUI application for wallpaper management"""
BINDINGS = [
Binding("q", "quit", "Quit"),
Binding("r", "refresh", "Refresh"),
Binding("1", "run_sort", "Run Sort"),
Binding("2", "run_sync", "Run Sync"),
Binding("3", "run_wallpaper_menu", "Wallpaper Menu"),
Binding("4", "run_waybar_update", "Update Waybar"),
Binding("5", "run_openrgb", "Set OpenRGB")
]
def __init__(self):
super().__init__()
self.console = Console()
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"
def compose(self) -> ComposeResult:
yield Header()
yield self.create_main_layout()
yield Footer()
def create_main_layout(self):
layout = Layout(name="main")
layout.split(
Layout(name="top", size=10),
Layout(name="middle", ratio=1),
Layout(name="bottom", size=5),
)
layout["top"].update(Panel("Wallpaper Manager", title="Status"))
layout["middle"].update(Panel(Static("Welcome to Wallpaper TUI"), title="Controls"))
layout["bottom"].update(Panel(Static("Ready"), title="Status"))
return layout
def on_mount(self) -> None:
self.refresh_status("Application started")
def refresh_status(self, message: str):
"""Update the status message"""
self.query_one("#status", Static).update(message)
def on_button_pressed(self, event: Button.Pressed) -> None:
"""Handle button presses"""
button_id = event.button.id
if button_id == "run_sort":
self.run_sort()
elif button_id == "run_sync":
self.run_sync()
elif button_id == "run_wallpaper_menu":
self.run_wallpaper_menu()
elif button_id == "run_waybar":
self.run_waybar_update()
elif button_id == "run_openrgb":
self.run_openrgb()
def run_sort(self):
"""Run the sort script"""
self.refresh_status("Running sort script...")
try:
result = subprocess.run([sys.executable, str(self.sort_script), "."],
capture_output=True, text=True, check=True)
self.refresh_status("Sort completed successfully")
self.show_output(result.stdout, "Sort Output")
except subprocess.CalledProcessError as e:
self.refresh_status("Sort failed")
self.show_output(e.stderr, "Sort Error")
def run_sync(self):
"""Run the sync script"""
self.refresh_status("Running sync script...")
try:
result = subprocess.run([str(self.sync_script)],
capture_output=True, text=True, check=True)
self.refresh_status("Sync completed successfully")
self.show_output(result.stdout, "Sync Output")
except subprocess.CalledProcessError as e:
self.refresh_status("Sync failed")
self.show_output(e.stderr, "Sync Error")
def run_wallpaper_menu(self):
"""Run the wallpaper menu script"""
self.refresh_status("Opening wallpaper menu...")
try:
# This would typically open an interactive menu
# For now, we'll just show a message
self.refresh_status("Wallpaper menu opened (interactive)")
except Exception as e:
self.refresh_status("Wallpaper menu failed")
self.show_output(str(e), "Wallpaper Menu Error")
def run_waybar_update(self):
"""Run the waybar update script"""
self.refresh_status("Updating waybar theme...")
try:
# This would need a wallpaper path
self.refresh_status("Waybar update initiated")
except Exception as e:
self.refresh_status("Waybar update failed")
self.show_output(str(e), "Waybar Error")
def run_openrgb(self):
"""Run the openrgb script"""
self.refresh_status("Setting OpenRGB color...")
try:
result = subprocess.run([sys.executable, str(self.openrgb_script)],
capture_output=True, text=True, check=True)
self.refresh_status("OpenRGB color set")
self.show_output(result.stdout, "OpenRGB Output")
except subprocess.CalledProcessError as e:
self.refresh_status("OpenRGB failed")
self.show_output(e.stderr, "OpenRGB Error")
def show_output(self, output: str, title: str):
"""Display output in a panel"""
# In a real implementation, this would show the output in a scrollable panel
self.refresh_status(f"{title} displayed")
def main():
app = WallpaperTUI()
app.run()
if __name__ == "__main__":
main()