Created
July 21, 2025 20:55
-
-
Save marduk191/352dcd033c82a5d2c066256985747664 to your computer and use it in GitHub Desktop.
A small tkinter gui for loading python scripts from a directory.
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
| import tkinter as tk | |
| from tkinter import ttk, filedialog, messagebox, scrolledtext | |
| import os | |
| import subprocess | |
| import threading | |
| from pathlib import Path | |
| import sys | |
| class ScriptRunnerGUI: | |
| def __init__(self, root): | |
| self.root = root | |
| self.root.title("Python Script Runner") | |
| self.root.geometry("800x600") | |
| # Current folder path | |
| self.scripts = {} | |
| self.create_widgets() | |
| self.setup_layout() | |
| def create_widgets(self): | |
| # Folder info frame | |
| self.folder_frame = ttk.Frame(self.root) | |
| ttk.Label(self.folder_frame, text="Scripts Folder: scripts").pack(side=tk.LEFT, padx=5) | |
| ttk.Button(self.folder_frame, text="Refresh", command=self.refresh_scripts).pack(side=tk.LEFT, padx=5) | |
| # Scripts frame with scrollable area | |
| self.scripts_frame = ttk.Frame(self.root) | |
| # Create canvas and scrollbar for scrollable content | |
| self.canvas = tk.Canvas(self.scripts_frame, highlightthickness=0) | |
| self.scrollbar = ttk.Scrollbar(self.scripts_frame, orient="vertical", command=self.canvas.yview) | |
| self.scrollable_frame = ttk.Frame(self.canvas) | |
| self.scrollable_frame.bind( | |
| "<Configure>", | |
| lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")) | |
| ) | |
| self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") | |
| self.canvas.configure(yscrollcommand=self.scrollbar.set) | |
| # Bind mousewheel to canvas | |
| self.canvas.bind("<MouseWheel>", self._on_mousewheel) | |
| # Output frame | |
| self.output_frame = ttk.LabelFrame(self.root, text="Output") | |
| self.output_text = scrolledtext.ScrolledText(self.output_frame, height=10, wrap=tk.WORD) | |
| self.output_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) | |
| # Control buttons | |
| self.control_frame = ttk.Frame(self.root) | |
| ttk.Button(self.control_frame, text="Clear Output", command=self.clear_output).pack(side=tk.LEFT, padx=5) | |
| def setup_layout(self): | |
| self.folder_frame.pack(fill=tk.X, padx=10, pady=5) | |
| self.scripts_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | |
| self.canvas.pack(side="left", fill="both", expand=True) | |
| self.scrollbar.pack(side="right", fill="y") | |
| self.output_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | |
| self.control_frame.pack(fill=tk.X, padx=10, pady=5) | |
| def _on_mousewheel(self, event): | |
| self.canvas.yview_scroll(int(-1*(event.delta/120)), "units") | |
| def get_scripts_folder(self): | |
| return os.path.join(os.getcwd(), "scripts") | |
| def refresh_scripts(self): | |
| folder = self.get_scripts_folder() | |
| if not os.path.exists(folder): | |
| # Create the scripts folder if it doesn't exist | |
| try: | |
| os.makedirs(folder) | |
| ttk.Label(self.scrollable_frame, text="Created scripts folder. Add Python scripts here.").pack(pady=20) | |
| except Exception as e: | |
| messagebox.showerror("Error", f"Could not create scripts folder: {str(e)}") | |
| return | |
| return | |
| # Clear existing script widgets | |
| for widget in self.scrollable_frame.winfo_children(): | |
| widget.destroy() | |
| self.scripts.clear() | |
| # Find all Python scripts in the folder | |
| try: | |
| python_files = [f for f in os.listdir(folder) | |
| if f.endswith('.py') and os.path.isfile(os.path.join(folder, f))] | |
| if not python_files: | |
| ttk.Label(self.scrollable_frame, text="No Python scripts found in the selected folder").pack(pady=20) | |
| return | |
| # Create widgets for each script | |
| for i, script_name in enumerate(sorted(python_files)): | |
| self.create_script_widget(script_name, i) | |
| except Exception as e: | |
| messagebox.showerror("Error", f"Error reading folder: {str(e)}") | |
| # Schedule next refresh is removed - only manual refresh now | |
| def create_script_widget(self, script_name, index): | |
| # Main frame for this script | |
| script_frame = ttk.LabelFrame(self.scrollable_frame, text=script_name, padding=10) | |
| script_frame.pack(fill=tk.X, padx=5, pady=5) | |
| # Arguments frame | |
| args_frame = ttk.Frame(script_frame) | |
| args_frame.pack(fill=tk.X, pady=(0, 5)) | |
| ttk.Label(args_frame, text="Arguments:").pack(side=tk.LEFT) | |
| args_entry = ttk.Entry(args_frame, width=40) | |
| args_entry.pack(side=tk.LEFT, padx=(5, 0), fill=tk.X, expand=True) | |
| # Buttons frame | |
| buttons_frame = ttk.Frame(script_frame) | |
| buttons_frame.pack(fill=tk.X) | |
| # Run button | |
| run_btn = ttk.Button(buttons_frame, text="Run", | |
| command=lambda: self.run_script(script_name, args_entry.get())) | |
| run_btn.pack(side=tk.LEFT, padx=(0, 5)) | |
| # Run in terminal button (for interactive scripts) | |
| terminal_btn = ttk.Button(buttons_frame, text="Run in Terminal", | |
| command=lambda: self.run_in_terminal(script_name, args_entry.get())) | |
| terminal_btn.pack(side=tk.LEFT, padx=(0, 5)) | |
| # View script button | |
| view_btn = ttk.Button(buttons_frame, text="View Script", | |
| command=lambda: self.view_script(script_name)) | |
| view_btn.pack(side=tk.LEFT) | |
| # Store references | |
| self.scripts[script_name] = { | |
| 'frame': script_frame, | |
| 'args_entry': args_entry, | |
| 'run_btn': run_btn, | |
| 'terminal_btn': terminal_btn, | |
| 'view_btn': view_btn | |
| } | |
| def run_script(self, script_name, args): | |
| folder = self.get_scripts_folder() | |
| script_path = os.path.join(folder, script_name) | |
| # Prepare command | |
| cmd = [sys.executable, script_path] | |
| if args.strip(): | |
| # Split arguments respecting quoted strings | |
| import shlex | |
| try: | |
| cmd.extend(shlex.split(args)) | |
| except ValueError as e: | |
| self.output_text.insert(tk.END, f"Error parsing arguments: {str(e)}\n") | |
| self.output_text.see(tk.END) | |
| return | |
| # Run in separate thread to avoid blocking GUI | |
| def run_thread(): | |
| self.output_text.insert(tk.END, f"\n{'='*50}\n") | |
| self.output_text.insert(tk.END, f"Running: {script_name}\n") | |
| self.output_text.insert(tk.END, f"Command: {' '.join(cmd)}\n") | |
| self.output_text.insert(tk.END, f"{'='*50}\n") | |
| self.output_text.see(tk.END) | |
| try: | |
| # Run the script and capture output | |
| result = subprocess.run(cmd, capture_output=True, text=True, cwd=folder) | |
| # Display output | |
| if result.stdout: | |
| self.output_text.insert(tk.END, "STDOUT:\n") | |
| self.output_text.insert(tk.END, result.stdout) | |
| self.output_text.insert(tk.END, "\n") | |
| if result.stderr: | |
| self.output_text.insert(tk.END, "STDERR:\n") | |
| self.output_text.insert(tk.END, result.stderr) | |
| self.output_text.insert(tk.END, "\n") | |
| self.output_text.insert(tk.END, f"Exit code: {result.returncode}\n") | |
| except Exception as e: | |
| self.output_text.insert(tk.END, f"Error running script: {str(e)}\n") | |
| self.output_text.see(tk.END) | |
| threading.Thread(target=run_thread, daemon=True).start() | |
| def run_in_terminal(self, script_name, args): | |
| folder = self.get_scripts_folder() | |
| script_path = os.path.join(folder, script_name) | |
| # Prepare command for terminal | |
| cmd_str = f'python "{script_path}"' | |
| if args.strip(): | |
| cmd_str += f' {args}' | |
| try: | |
| # Platform-specific terminal commands | |
| if sys.platform.startswith('win'): | |
| subprocess.Popen(f'start cmd /k "{cmd_str}"', shell=True, cwd=folder) | |
| elif sys.platform.startswith('darwin'): # macOS | |
| subprocess.Popen(['open', '-a', 'Terminal', '--args', 'bash', '-c', f'{cmd_str}; exec bash'], | |
| cwd=folder) | |
| else: # Linux | |
| subprocess.Popen(['gnome-terminal', '--', 'bash', '-c', f'{cmd_str}; exec bash'], | |
| cwd=folder) | |
| self.output_text.insert(tk.END, f"Opened {script_name} in terminal\n") | |
| except Exception as e: | |
| self.output_text.insert(tk.END, f"Error opening terminal: {str(e)}\n") | |
| self.output_text.see(tk.END) | |
| def view_script(self, script_name): | |
| folder = self.get_scripts_folder() | |
| script_path = os.path.join(folder, script_name) | |
| # Create new window to display script content | |
| view_window = tk.Toplevel(self.root) | |
| view_window.title(f"View Script: {script_name}") | |
| view_window.geometry("700x500") | |
| # Create text widget with scrollbar | |
| text_frame = ttk.Frame(view_window) | |
| text_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) | |
| text_widget = scrolledtext.ScrolledText(text_frame, wrap=tk.NONE) | |
| text_widget.pack(fill=tk.BOTH, expand=True) | |
| try: | |
| with open(script_path, 'r', encoding='utf-8') as f: | |
| content = f.read() | |
| text_widget.insert(tk.END, content) | |
| text_widget.config(state=tk.DISABLED) # Make read-only | |
| except Exception as e: | |
| text_widget.insert(tk.END, f"Error reading file: {str(e)}") | |
| def clear_output(self): | |
| self.output_text.delete(1.0, tk.END) | |
| def main(): | |
| root = tk.Tk() | |
| app = ScriptRunnerGUI(root) | |
| # Start with refresh to check for scripts | |
| app.refresh_scripts() | |
| root.mainloop() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment