Skip to content

Instantly share code, notes, and snippets.

@marduk191
Created July 21, 2025 20:55
Show Gist options
  • Select an option

  • Save marduk191/352dcd033c82a5d2c066256985747664 to your computer and use it in GitHub Desktop.

Select an option

Save marduk191/352dcd033c82a5d2c066256985747664 to your computer and use it in GitHub Desktop.
A small tkinter gui for loading python scripts from a directory.
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