Revert "0.6.0 Final"

This reverts commit 29335f8d56.
This commit is contained in:
SpaceNerd0717
2025-11-13 17:17:37 -05:00
parent b10e414607
commit 575bbf4954
19 changed files with 386 additions and 32604 deletions

BIN
RLC.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

243
background/exemaker.py Normal file
View File

@@ -0,0 +1,243 @@
#!/usr/bin/env python3
"""
Simple Tkinter GUI to run PyInstaller and make an exe from a .py file.
Features:
- choose script
- choose onefile / dir
- choose windowed (noconsole)
- add icon
- add additional data (file or folder; multiple, separated by semicolons)
- set output folder
- show live PyInstaller output
- cancel build
"""
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import subprocess
import threading
import sys
import shlex
import os
from pathlib import Path
class BuilderGUI(tk.Tk):
def __init__(self):
super().__init__()
self.title("Python → EXE (PyInstaller GUI)")
self.geometry("800x600")
self.create_widgets()
self.proc = None # subprocess handle
self.stop_requested = False
def create_widgets(self):
frame_top = tk.Frame(self)
frame_top.pack(fill="x", padx=10, pady=8)
tk.Label(frame_top, text="Script:").grid(row=0, column=0, sticky="e")
self.script_entry = tk.Entry(frame_top, width=70)
self.script_entry.grid(row=0, column=1, padx=6)
tk.Button(frame_top, text="Browse...", command=self.browse_script).grid(row=0, column=2)
tk.Label(frame_top, text="Output folder:").grid(row=1, column=0, sticky="e")
self.out_entry = tk.Entry(frame_top, width=70)
self.out_entry.grid(row=1, column=1, padx=6)
tk.Button(frame_top, text="Choose...", command=self.choose_output).grid(row=1, column=2)
opts_frame = tk.LabelFrame(self, text="Options", padx=8, pady=8)
opts_frame.pack(fill="x", padx=10)
self.onefile_var = tk.BooleanVar(value=True)
tk.Checkbutton(opts_frame, text="Onefile (single exe)", variable=self.onefile_var).grid(row=0, column=0, sticky="w", padx=6, pady=2)
self.windowed_var = tk.BooleanVar(value=False)
tk.Checkbutton(opts_frame, text="Windowed (no console) / --noconsole", variable=self.windowed_var).grid(row=0, column=1, sticky="w", padx=6, pady=2)
tk.Label(opts_frame, text="Icon (.ico):").grid(row=1, column=0, sticky="e")
self.icon_entry = tk.Entry(opts_frame, width=50)
self.icon_entry.grid(row=1, column=1, sticky="w", padx=6)
tk.Button(opts_frame, text="Browse", command=self.browse_icon).grid(row=1, column=2)
tk.Label(opts_frame, text="Additional data (src;dest pairs separated by ';', e.g. resources;resources):").grid(row=2, column=0, columnspan=3, sticky="w", pady=(6,0))
self.data_entry = tk.Entry(opts_frame, width=110)
self.data_entry.grid(row=3, column=0, columnspan=3, padx=6, pady=4)
tk.Label(opts_frame, text="Extra PyInstaller args:").grid(row=4, column=0, sticky="w")
self.extra_entry = tk.Entry(opts_frame, width=80)
self.extra_entry.grid(row=4, column=1, columnspan=2, padx=6, pady=4, sticky="w")
run_frame = tk.Frame(self)
run_frame.pack(fill="x", padx=10, pady=8)
self.build_btn = tk.Button(run_frame, text="Build EXE", command=self.start_build, bg="#2b7a78", fg="white")
self.build_btn.pack(side="left", padx=(0,6))
self.cancel_btn = tk.Button(run_frame, text="Cancel", command=self.request_cancel, state="disabled", bg="#b00020", fg="white")
self.cancel_btn.pack(side="left")
clear_btn = tk.Button(run_frame, text="Clear Log", command=self.clear_log)
clear_btn.pack(side="left", padx=6)
open_out_btn = tk.Button(run_frame, text="Open Output Folder", command=self.open_output)
open_out_btn.pack(side="right")
self.log = scrolledtext.ScrolledText(self, height=18, font=("Consolas", 10))
self.log.pack(fill="both", expand=True, padx=10, pady=(0,10))
def browse_script(self):
path = filedialog.askopenfilename(filetypes=[("Python files", "*.py")])
if path:
self.script_entry.delete(0, tk.END)
self.script_entry.insert(0, path)
# default output to script parent /dist
parent = os.path.dirname(path)
default_out = os.path.join(parent, "dist")
self.out_entry.delete(0, tk.END)
self.out_entry.insert(0, default_out)
def choose_output(self):
path = filedialog.askdirectory()
if path:
self.out_entry.delete(0, tk.END)
self.out_entry.insert(0, path)
def browse_icon(self):
path = filedialog.askopenfilename(filetypes=[("Icon files", "*.ico")])
if path:
self.icon_entry.delete(0, tk.END)
self.icon_entry.insert(0, path)
def clear_log(self):
self.log.delete("1.0", tk.END)
def open_output(self):
out = self.out_entry.get().strip()
if not out:
messagebox.showinfo("Output folder", "No output folder set.")
return
os.startfile(out) if os.name == "nt" else subprocess.run(["xdg-open", out])
def request_cancel(self):
if self.proc and self.proc.poll() is None:
self.stop_requested = True
# try terminate politely
try:
self.proc.terminate()
except Exception:
pass
self.log_insert("\nCancellation requested...\n")
self.cancel_btn.config(state="disabled")
def log_insert(self, text):
self.log.insert(tk.END, text)
self.log.see(tk.END)
def start_build(self):
script = self.script_entry.get().strip()
if not script or not os.path.isfile(script):
messagebox.showerror("Error", "Please choose a valid Python script to build.")
return
out_dir = self.out_entry.get().strip() or os.path.dirname(script)
os.makedirs(out_dir, exist_ok=True)
self.build_btn.config(state="disabled")
self.cancel_btn.config(state="normal")
self.stop_requested = False
self.clear_log()
# Start build on a thread to keep UI responsive
thread = threading.Thread(target=self.run_pyinstaller, args=(script, out_dir), daemon=True)
thread.start()
def run_pyinstaller(self, script, out_dir):
# Build the PyInstaller command
cmd = [sys.executable, "-m", "PyInstaller"]
if self.onefile_var.get():
cmd.append("--onefile")
else:
cmd.append("--onedir")
if self.windowed_var.get():
cmd.append("--noconsole")
icon = self.icon_entry.get().strip()
if icon:
cmd.extend(["--icon", icon])
# add additional data: user can provide pairs like "data;data" or "assets;assets"
data_spec = self.data_entry.get().strip()
if data_spec:
# support multiple separated by semicolons or vertical bars
pairs = [p for p in (data_spec.split(";") + data_spec.split("|")) if p.strip()]
# normalize pairs to PyInstaller format: src;dest (on Windows use ';' in CLI but PyInstaller expects src;dest as single argument)
for p in pairs:
# if user typed "src:dest" or "src->dest", replace with semicolon
p_fixed = p.replace(":", ";").replace("->", ";")
cmd.extend(["--add-data", p_fixed])
# user extra args (raw)
extra = self.extra_entry.get().strip()
if extra:
# split carefully
cmd.extend(shlex.split(extra))
# ensure output path goes to chosen dir: use --distpath
cmd.extend(["--distpath", out_dir])
# entry script
cmd.append(script)
self.log_insert("Running PyInstaller with command:\n" + " ".join(shlex.quote(c) for c in cmd) + "\n\n")
# spawn the process
try:
self.proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
universal_newlines=True,
)
except Exception as e:
self.log_insert(f"Failed to start PyInstaller: {e}\n")
self.build_btn.config(state="normal")
self.cancel_btn.config(state="disabled")
return
# Stream output line by line
try:
for line in self.proc.stdout:
if line:
self.log_insert(line)
if self.stop_requested:
try:
self.proc.terminate()
except Exception:
pass
break
self.proc.wait(timeout=30)
except subprocess.TimeoutExpired:
self.log_insert("Process did not exit in time after termination.\n")
except Exception as e:
self.log_insert(f"Error while running PyInstaller: {e}\n")
retcode = self.proc.returncode if self.proc else None
if self.stop_requested:
self.log_insert("\nBuild cancelled by user.\n")
elif retcode == 0:
self.log_insert("\nBuild finished successfully.\n")
else:
self.log_insert(f"\nBuild finished with return code {retcode}.\n")
# Re-enable buttons
self.build_btn.config(state="normal")
self.cancel_btn.config(state="disabled")
self.proc = None
self.stop_requested = False
if __name__ == "__main__":
app = BuilderGUI()
app.mainloop()

143
background/experiment.py Normal file
View File

@@ -0,0 +1,143 @@
import tkinter as tk
import requests
import threading
import time
import json
SETTINGS_FILE = "settings.json"
class CountdownApp:
def __init__(self, root):
self.root = root
self.root.title("Launch Control - GO/NOGO")
self.go_nogo_labels = {}
self.sheet_data = {}
self.last_data = {}
self.running = True
# Load settings
self.settings = self.load_settings()
tk.Label(root, text="GO/NOGO STATUS", font=("Arial", 16, "bold")).pack(pady=10)
# Create display area
self.frame = tk.Frame(root)
self.frame.pack(pady=10)
# Buttons
tk.Button(root, text="Add Spreadsheet", command=self.add_spreadsheet_window).pack(pady=5)
tk.Button(root, text="Stop", command=self.stop).pack(pady=5)
self.start_update_thread()
def load_settings(self):
try:
with open(SETTINGS_FILE, "r") as f:
return json.load(f)
except FileNotFoundError:
return {"spreadsheets": []}
def save_settings(self):
with open(SETTINGS_FILE, "w") as f:
json.dump(self.settings, f, indent=4)
def add_spreadsheet_window(self):
win = tk.Toplevel(self.root)
win.title("Add Spreadsheet")
tk.Label(win, text="Name:").grid(row=0, column=0)
name_entry = tk.Entry(win)
name_entry.grid(row=0, column=1)
tk.Label(win, text="Link (CSV export or share link):").grid(row=1, column=0)
link_entry = tk.Entry(win, width=60)
link_entry.grid(row=1, column=1)
tk.Label(win, text="Range cell (e.g., L2):").grid(row=2, column=0)
range_entry = tk.Entry(win)
range_entry.grid(row=2, column=1)
def save_sheet():
name = name_entry.get().strip()
link = link_entry.get().strip()
cell = range_entry.get().strip().upper()
if name and link and cell:
self.settings["spreadsheets"].append({
"name": name,
"link": link,
"cell": cell
})
self.save_settings()
self.add_go_nogo_label(name)
win.destroy()
tk.Button(win, text="Save", command=save_sheet).grid(row=3, column=0, columnspan=2, pady=10)
def add_go_nogo_label(self, name):
if name not in self.go_nogo_labels:
label = tk.Label(self.frame, text=f"{name}: ---", font=("Arial", 14), width=25)
label.pack(pady=2)
self.go_nogo_labels[name] = label
def update_labels(self):
for sheet in self.settings["spreadsheets"]:
name = sheet["name"]
link = sheet["link"]
cell = sheet["cell"]
# Convert normal sheet link to CSV export link if needed
if "/edit" in link and "export" not in link:
link = link.split("/edit")[0] + "/gviz/tq?tqx=out:csv"
try:
r = requests.get(link, timeout=5)
if r.status_code == 200:
content = r.text
if name not in self.last_data or self.last_data[name] != content:
self.last_data[name] = content
# Just read raw content and extract cell text if possible
value = self.extract_cell_value(content, cell)
self.update_label_color(name, value)
except Exception as e:
print(f"Error updating {name}: {e}")
def extract_cell_value(self, csv_data, cell):
# Simple CSV parser to get cell data like L2
try:
rows = [r.split(",") for r in csv_data.splitlines() if r.strip()]
col = ord(cell[0]) - 65
row = int(cell[1:]) - 1
return rows[row][col].strip().upper()
except Exception:
return "ERR"
def update_label_color(self, name, value):
label = self.go_nogo_labels.get(name)
if not label:
return
if "GO" in value:
label.config(text=f"{name}: GO", bg="green", fg="white")
elif "NO" in value:
label.config(text=f"{name}: NO GO", bg="red", fg="white")
else:
label.config(text=f"{name}: ---", bg="gray", fg="black")
def start_update_thread(self):
threading.Thread(target=self.update_loop, daemon=True).start()
def update_loop(self):
while self.running:
self.update_labels()
time.sleep(0.1)
def stop(self):
self.running = False
self.root.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = CountdownApp(root)
root.mainloop()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,46 +0,0 @@
This file lists modules PyInstaller was not able to find. This does not
necessarily mean this module is required for running your program. Python and
Python 3rd-party packages include a lot of conditional or optional modules. For
example the module 'ntpath' only exists on Windows, whereas the module
'posixpath' only exists on Posix systems.
Types if import:
* top-level: imported at the top-level - look at these first
* conditional: imported within an if-statement
* delayed: imported within a function
* optional: imported within a try-except-statement
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks!
missing module named pwd - imported by posixpath (delayed, conditional, optional), subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), netrc (delayed, conditional), getpass (delayed)
missing module named grp - imported by subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional)
missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed)
missing module named fcntl - imported by subprocess (optional)
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named termios - imported by getpass (optional)
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional)
missing module named resource - imported by posix (top-level)
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named pyimod02_importers - imported by C:\Users\forjn\AppData\Local\Programs\Python\Python312\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named simplejson - imported by requests.compat (conditional, optional)
missing module named dummy_threading - imported by requests.cookies (optional)
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named zstandard - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named brotlicffi - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named socks - imported by urllib3.contrib.socks (optional)
missing module named bcrypt - imported by cryptography.hazmat.primitives.serialization.ssh (optional)
missing module named cryptography.x509.UnsupportedExtension - imported by cryptography.x509 (optional), urllib3.contrib.pyopenssl (optional)
missing module named 'OpenSSL.crypto' - imported by urllib3.contrib.pyopenssl (delayed, conditional)
missing module named OpenSSL - imported by urllib3.contrib.pyopenssl (top-level)
missing module named pyodide - imported by urllib3.contrib.emscripten.fetch (top-level)
missing module named js - imported by urllib3.contrib.emscripten.fetch (top-level)

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.