Created
March 4, 2026 20:14
-
-
Save pancho508/3c04cfd0dce256e71137e349c69a6e5b to your computer and use it in GitHub Desktop.
practiceGestureScript.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Simple gesture drawing timer script for local image folders. | |
| Displays images from a folder with an automatic timer between each one. | |
| Usage: | |
| python3 gesture-drawing-local.py [folder_path] [interval_seconds] [shuffle] | |
| Example: | |
| python3 gesture-drawing-local.py ~/Pictures/gesture_refs 120 true | |
| """ | |
| import sys | |
| import os | |
| import tkinter as tk | |
| from tkinter import filedialog | |
| from PIL import Image, ImageTk | |
| import random | |
| from pathlib import Path | |
| class GestureDrawer: | |
| def __init__(self, folder_path=None, interval=120, shuffle=True): | |
| self.interval = interval | |
| self.remaining = interval | |
| self.paused = False | |
| self.timer_active = False | |
| # Get images | |
| if folder_path is None: | |
| self.folder = self._select_folder() | |
| else: | |
| self.folder = Path(folder_path) | |
| if not self.folder.exists(): | |
| print(f"Folder not found: {folder_path}") | |
| sys.exit(1) | |
| self.images = self._load_images() | |
| if not self.images: | |
| print(f"No images found in {self.folder}") | |
| sys.exit(1) | |
| if shuffle: | |
| random.shuffle(self.images) | |
| self.current_index = 0 | |
| # Setup UI | |
| self.root = tk.Tk() | |
| self.root.title("Gesture Drawing - Local") | |
| self.root.geometry("1024x768") | |
| # Main image label | |
| self.image_label = tk.Label(self.root, bg="black") | |
| self.image_label.pack(fill=tk.BOTH, expand=True) | |
| # Control frame | |
| control_frame = tk.Frame(self.root, bg="#1a1a1a", height=80) | |
| control_frame.pack(fill=tk.X, side=tk.BOTTOM) | |
| # Timer display (top-right corner) | |
| self.timer_label = tk.Label( | |
| self.root, | |
| text=str(self.interval), | |
| font=("Courier", 48, "bold"), | |
| fg="#3b82f6", | |
| bg="#000000", | |
| padx=10, | |
| pady=10 | |
| ) | |
| self.timer_label.place(x=880, y=10, anchor="ne") | |
| # Image counter (top-left) | |
| self.counter_label = tk.Label( | |
| self.root, | |
| text=f"1 / {len(self.images)}", | |
| font=("Arial", 12), | |
| fg="white", | |
| bg="#000000", | |
| padx=10, | |
| pady=5 | |
| ) | |
| self.counter_label.place(x=10, y=10) | |
| # Control buttons in frame | |
| button_frame = tk.Frame(control_frame, bg="#1a1a1a") | |
| button_frame.pack(pady=15) | |
| tk.Button( | |
| button_frame, | |
| text="Next ⏭ (→)", | |
| command=self.next_image, | |
| font=("Arial", 11), | |
| padx=15, | |
| pady=8, | |
| bg="#3b82f6", | |
| fg="white" | |
| ).pack(side=tk.LEFT, padx=10) | |
| tk.Button( | |
| button_frame, | |
| text="Pause/Resume (Space)", | |
| command=self.toggle_pause, | |
| font=("Arial", 11), | |
| padx=15, | |
| pady=8, | |
| bg="#8b5cf6", | |
| fg="white" | |
| ).pack(side=tk.LEFT, padx=10) | |
| tk.Button( | |
| button_frame, | |
| text="Quit (Q)", | |
| command=self.root.quit, | |
| font=("Arial", 11), | |
| padx=15, | |
| pady=8, | |
| bg="#ef4444", | |
| fg="white" | |
| ).pack(side=tk.LEFT, padx=10) | |
| # Keyboard shortcuts | |
| self.root.bind("<Right>", lambda e: self.next_image()) | |
| self.root.bind("<space>", lambda e: self.toggle_pause()) | |
| self.root.bind("<q>", lambda e: self.root.quit()) | |
| self.root.bind("<Escape>", lambda e: self.root.quit()) | |
| # Display first image and start timer | |
| self.show_image() | |
| self.start_timer() | |
| def _select_folder(self): | |
| """Let user select folder if not provided""" | |
| root = tk.Tk() | |
| root.withdraw() | |
| folder = filedialog.askdirectory(title="Select folder with images") | |
| root.destroy() | |
| return Path(folder) if folder else None | |
| def _load_images(self): | |
| """Load image files from folder""" | |
| supported = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'} | |
| images = [ | |
| f for f in self.folder.iterdir() | |
| if f.suffix.lower() in supported and f.is_file() | |
| ] | |
| return sorted(images) | |
| def show_image(self): | |
| """Display current image""" | |
| img_path = self.images[self.current_index] | |
| try: | |
| img = Image.open(img_path) | |
| # Get window dimensions | |
| window_width = self.root.winfo_width() | |
| window_height = self.root.winfo_height() - 80 | |
| if window_width < 1: | |
| window_width = 1024 | |
| if window_height < 1: | |
| window_height = 688 | |
| # Expand to fill window while maintaining aspect ratio (no cropping) | |
| img_width, img_height = img.size | |
| img_aspect = img_width / img_height | |
| window_aspect = window_width / window_height | |
| if img_aspect > window_aspect: | |
| # Image is wider: fit to window width | |
| new_width = window_width | |
| new_height = int(window_width / img_aspect) | |
| else: | |
| # Image is taller: fit to window height | |
| new_height = window_height | |
| new_width = int(window_height * img_aspect) | |
| img = img.resize((new_width, new_height), Image.Resampling.LANCZOS) | |
| photo = ImageTk.PhotoImage(img) | |
| self.image_label.config(image=photo) | |
| self.image_label.image = photo | |
| # Update counter | |
| self.counter_label.config( | |
| text=f"{self.current_index + 1} / {len(self.images)}\n{img_path.name}" | |
| ) | |
| except Exception as e: | |
| self.image_label.config(text=f"Error loading image: {e}", fg="red") | |
| def start_timer(self): | |
| """Start the countdown timer""" | |
| self.remaining = self.interval | |
| self.timer_active = True | |
| self.paused = False | |
| self.update_timer() | |
| def update_timer(self): | |
| """Update timer every 100ms""" | |
| if self.timer_active and not self.paused: | |
| self.remaining -= 0.1 | |
| if self.remaining <= 0: | |
| self.next_image() | |
| return | |
| self.timer_label.config(text=str(int(self.remaining))) | |
| self.root.after(100, self.update_timer) | |
| def next_image(self): | |
| """Move to next image and restart timer""" | |
| self.current_index = (self.current_index + 1) % len(self.images) | |
| self.show_image() | |
| self.start_timer() | |
| def toggle_pause(self): | |
| """Pause/resume timer""" | |
| self.paused = not self.paused | |
| if self.paused: | |
| self.timer_label.config(fg="#fbbf24") # Amber when paused | |
| else: | |
| self.timer_label.config(fg="#3b82f6") # Blue when running | |
| def run(self): | |
| """Start the application""" | |
| self.root.mainloop() | |
| if __name__ == "__main__": | |
| folder = None | |
| interval = 120 | |
| shuffle = True | |
| # Parse arguments | |
| if len(sys.argv) > 1: | |
| folder = sys.argv[1] | |
| if len(sys.argv) > 2: | |
| try: | |
| interval = int(sys.argv[2]) | |
| except ValueError: | |
| print(f"Invalid interval: {sys.argv[2]}") | |
| sys.exit(1) | |
| if len(sys.argv) > 3: | |
| shuffle = sys.argv[3].lower() in ('true', '1', 'yes') | |
| app = GestureDrawer(folder, interval, shuffle) | |
| app.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment