trying to make a TUI
This commit is contained in:
parent
6ccc8606ef
commit
b2f5e65c06
264
simple_tui.py
Executable file
264
simple_tui.py
Executable 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
158
wallpaper_tui.py
Normal 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()
|
||||
Loading…
x
Reference in New Issue
Block a user