2 Commits
Author SHA1 Message Date
CosmicKillsSouls 0af6aa05a0 Adjustable Window sizes
Changed window sizes to be controlled by config.
2026-09-04 11:43:31 -05:00
CosmicKillsSouls 1aaaefd08f Adjustable Window sizes
Changed window sizes to be controlled by config.
2026-09-04 11:39:48 -05:00
2 changed files with 396 additions and 388 deletions
+379 -376
View File
@@ -1,377 +1,380 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import tkinter as tk import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext from tkinter import filedialog, messagebox, scrolledtext
import tkinter.font as tkfont import tkinter.font as tkfont
import shutil import shutil
import ctypes import ctypes
import string import string
import configparser import configparser
# ============================================================ # ============================================================
# CONFIG: # CONFIG:
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(r"C:\ProgramData\Program-Search\config.ini") config.read(r"C:\ProgramData\Program-Search\config.ini")
SEARCH_ROOT = config["Paths"]["SEARCH_ROOT"] SEARCH_WINDOW_SIZE = config["General"]["SEARCH_WINDOW_SIZE"]
TRASH_DIR = config["Paths"]["TRASH_DIR"] RESULTS_WINDOW_SIZE = config["General"]["RESULTS_WINDOW_SIZE"]
TRASH_ENABLED = config.getboolean("Features", "TRASH_ENABLED") PREVIEW_WINDOW_SIZE = config["General"]["PREVIEW_WINDOW_SIZE"]
EDITOR_ENABLED = config.getboolean("Features", "EDITOR_ENABLED") SEARCH_ROOT = config["Paths"]["SEARCH_ROOT"]
MANUEAL_REPLACE_ENABLED = config.getboolean("Features", "MANUEAL_REPLACE_ENABLED") TRASH_DIR = config["Paths"]["TRASH_DIR"]
CLOSE_ALL_ENABLED = config.getboolean("Features", "CLOSE_ALL_ENABLED") TRASH_ENABLED = config.getboolean("Features", "TRASH_ENABLED")
AUTO_CLOSE_SECONDS = config.getint("Features", "AUTO_CLOSE_SECONDS") EDITOR_ENABLED = config.getboolean("Features", "EDITOR_ENABLED")
Extension = config["Copy"]["Extension"] MANUEAL_REPLACE_ENABLED = config.getboolean("Features", "MANUEAL_REPLACE_ENABLED")
# ============================================================ CLOSE_ALL_ENABLED = config.getboolean("Features", "CLOSE_ALL_ENABLED")
AUTO_CLOSE_SECONDS = config.getint("Features", "AUTO_CLOSE_SECONDS")
Extension = config["Copy"]["Extension"]
os.makedirs(TRASH_DIR, exist_ok=True) # ============================================================
def custom_messagebox(title, message, kind="info"): os.makedirs(TRASH_DIR, exist_ok=True)
win = tk.Toplevel()
win.title(title)
win.geometry("600x300") def custom_messagebox(title, message, kind="info"):
win.grab_set() win = tk.Toplevel()
win.focus_force() win.title(title)
frame = tk.Frame(win, padx=20, pady=20) win.geometry(SEARCH_WINDOW_SIZE)
frame.pack(expand=True, fill="both") win.grab_set()
center_window(win) win.focus_force()
frame = tk.Frame(win, padx=20, pady=20)
tk.Label(frame, text=message, font=("Consolas", 24), wraplength=550).pack(pady=20) frame.pack(expand=True, fill="both")
def close(): center_window(win)
win.destroy()
tk.Label(frame, text=message, font=("Consolas", 24), wraplength=550).pack(pady=20)
if kind == "yesno": def close():
result = {"value": False} win.destroy()
IsInfo = False
def yes(): if kind == "yesno":
result["value"] = True result = {"value": False}
win.destroy() IsInfo = False
def yes():
def no(): result["value"] = True
result["value"] = False win.destroy()
win.destroy()
def no():
tk.Button(frame, text="Yes", font=("Consolas", 24), padx=10, pady=10, command=yes).pack(side="left", padx=16) result["value"] = False
tk.Button(frame, text="No", font=("Consolas", 24), padx=10, pady=10, command=no).pack(side="right", padx=16) win.destroy()
win.wait_window() tk.Button(frame, text="Yes", font=("Consolas", 24), padx=10, pady=10, command=yes).pack(side="left", padx=16)
return result["value"] tk.Button(frame, text="No", font=("Consolas", 24), padx=10, pady=10, command=no).pack(side="right", padx=16)
else: win.wait_window()
tk.Button(frame, text="OK", font=("Consolas", 24), padx=10, pady=10, command=close).pack(pady=20) return result["value"]
win.bind("<Return>", lambda event: close())
win.wait_window() else:
tk.Button(frame, text="OK", font=("Consolas", 24), padx=10, pady=10, command=close).pack(pady=20)
win.bind("<Return>", lambda event: close())
def center_window(win): win.wait_window()
win.update_idletasks()
w = win.winfo_width()
h = win.winfo_height() def center_window(win):
sw = win.winfo_screenwidth() win.update_idletasks()
sh = win.winfo_screenheight() w = win.winfo_width()
x = (sw // 2) - (w // 2) h = win.winfo_height()
y = (sh // 2) - (h // 2) sw = win.winfo_screenwidth()
win.geometry(f"{w}x{h}+{x}+{y}") sh = win.winfo_screenheight()
x = (sw // 2) - (w // 2)
y = (sh // 2) - (h // 2)
def find_external_drives(): win.geometry(f"{w}x{h}+{x}+{y}")
drives = []
DRIVE_REMOVABLE = 2
def find_external_drives():
for letter in string.ascii_uppercase: drives = []
drive = f"{letter}:\\" DRIVE_REMOVABLE = 2
if os.path.exists(drive):
dtype = ctypes.windll.kernel32.GetDriveTypeW(drive) for letter in string.ascii_uppercase:
if dtype == DRIVE_REMOVABLE: drive = f"{letter}:\\"
drives.append(drive) if os.path.exists(drive):
dtype = ctypes.windll.kernel32.GetDriveTypeW(drive)
return drives if dtype == DRIVE_REMOVABLE:
drives.append(drive)
def close_all(root): return drives
for w in root.winfo_children():
try:
w.destroy() def close_all(root):
except: for w in root.winfo_children():
pass try:
root.destroy() w.destroy()
except:
pass
def popup_search_input(): root.destroy()
win = tk.Toplevel()
win.title("Search")
win.geometry("600x300") def popup_search_input():
center_window(win) win = tk.Toplevel()
win.title("Search")
tk.Label(win, text="Enter program number, part number, or name:").pack(pady=10) win.geometry(SEARCH_WINDOW_SIZE)
entry = tk.Entry(win) center_window(win)
entry.pack(pady=5)
entry.focus_set() tk.Label(win, text="Enter program number, part number, or name:").pack(pady=10)
entry.bind("<Return>", lambda event: submit()) entry = tk.Entry(win)
win.bind("<Return>", lambda event: submit()) entry.pack(pady=5)
entry.focus_set()
result = {"value": None} entry.bind("<Return>", lambda event: submit())
win.bind("<Return>", lambda event: submit())
def submit():
result["value"] = entry.get().strip().lower() result = {"value": None}
win.destroy()
def submit():
tk.Button(win, text="Search", command=submit, padx=20, pady=20).pack(pady=60) result["value"] = entry.get().strip().lower()
win.destroy()
win.wait_window(); return result["value"]
tk.Button(win, text="Search", command=submit, padx=20, pady=20).pack(pady=60)
def search_files(term): win.wait_window(); return result["value"]
matches = []
for root, dirs, files in os.walk(SEARCH_ROOT): def search_files(term):
for fname in files: matches = []
path = os.path.join(root, fname)
fname_lower = fname.lower() for root, dirs, files in os.walk(SEARCH_ROOT):
for fname in files:
filename_match = term in fname_lower path = os.path.join(root, fname)
contents_match = False fname_lower = fname.lower()
try: filename_match = term in fname_lower
with open(path, "r", errors="ignore") as f: contents_match = False
if term in f.read().lower():
contents_match = True try:
except: with open(path, "r", errors="ignore") as f:
pass if term in f.read().lower():
contents_match = True
if filename_match or contents_match: except:
matches.append(path) pass
return matches if filename_match or contents_match:
matches.append(path)
def popup_results_list(matches): return matches
win = tk.Toplevel()
win.title("Matches Found")
win.geometry("1200x1000") def popup_results_list(matches):
center_window(win) win = tk.Toplevel()
win.title("Matches Found")
tk.Label(win, text="Select files to preview:").pack(pady=10) win.geometry(RESULTS_WINDOW_SIZE)
center_window(win)
lb = tk.Listbox(win, selectmode=tk.MULTIPLE)
lb.pack(expand=True, fill="both") tk.Label(win, text="Select files to preview:").pack(pady=10)
for m in matches: lb = tk.Listbox(win, selectmode=tk.MULTIPLE)
lb.insert(tk.END, m) lb.pack(expand=True, fill="both")
result = {"selected": []} for m in matches:
lb.insert(tk.END, m)
def select_all():
lb.select_set(0, tk.END) result = {"selected": []}
def submit():
sel = lb.curselection() def select_all():
result["selected"] = [matches[i] for i in sel] lb.select_set(0, tk.END)
win.destroy() def submit():
sel = lb.curselection()
tk.Button(win, text="Open Selected", command=submit, padx=20, pady=10).pack(side ="right", padx=20, pady=10) result["selected"] = [matches[i] for i in sel]
tk.Button(win, text="Select All", command=select_all, padx=20, pady=10).pack(side ="left", padx=20, pady=10) win.destroy()
lb.bind("<Return>", lambda event: submit()) tk.Button(win, text="Open Selected", command=submit, padx=20, pady=10).pack(side ="right", padx=20, pady=10)
win.bind("<Return>", lambda event: submit()) tk.Button(win, text="Select All", command=select_all, padx=20, pady=10).pack(side ="left", padx=20, pady=10)
win.wait_window(); return result["selected"] lb.bind("<Return>", lambda event: submit())
win.bind("<Return>", lambda event: submit())
def popup_file_preview(path, on_close_callback): win.wait_window(); return result["selected"]
win = tk.Toplevel()
win.title(path)
win.geometry("900x1000") def popup_file_preview(path, on_close_callback):
win = tk.Toplevel()
box = scrolledtext.ScrolledText(win, wrap=tk.WORD) win.title(path)
box.pack(expand=True, fill="both") win.geometry(PREVIEW_WINDOW_SIZE)
try: box = scrolledtext.ScrolledText(win, wrap=tk.WORD)
with open(path, "r", errors="ignore") as f: box.pack(expand=True, fill="both")
original_text = f.read()
box.insert(tk.END, original_text) try:
box.edit_modified(False) with open(path, "r", errors="ignore") as f:
except Exception as e: original_text = f.read()
original_text = "" box.insert(tk.END, original_text)
box.insert(tk.END, f"Error reading file:\n{e}") box.edit_modified(False)
except Exception as e:
modified = {"changed": False} original_text = ""
box.insert(tk.END, f"Error reading file:\n{e}")
def mark_modified(event=None):
if box.edit_modified(): modified = {"changed": False}
modified["changed"] = True
box.edit_modified(False) def mark_modified(event=None):
if box.edit_modified():
if EDITOR_ENABLED: modified["changed"] = True
box.bind("<<Modified>>", mark_modified) box.edit_modified(False)
else:
box.configure(state="disabled") if EDITOR_ENABLED:
box.bind("<<Modified>>", mark_modified)
frame = tk.Frame(win) else:
frame.pack(fill="x", pady=10) box.configure(state="disabled")
frame = tk.Frame(win)
def save_file(): frame.pack(fill="x", pady=10)
try:
with open(path, "w", errors="ignore") as f:
f.write(box.get("1.0", tk.END)) def save_file():
modified["changed"] = False try:
custom_messagebox("Saved", f"File saved:\n{path}") with open(path, "w", errors="ignore") as f:
except Exception as e: f.write(box.get("1.0", tk.END))
custom_messagebox("Save Error", str(e)) modified["changed"] = False
custom_messagebox("Saved", f"File saved:\n{path}")
def copy_to_external(): except Exception as e:
if EDITOR_ENABLED and modified["changed"]: custom_messagebox("Save Error", str(e))
ask = custom_messagebox("Unsaved Changes", "You have unsaved changes.\nSave before copying?", kind = "yesno")
if ask: def copy_to_external():
save_file() if EDITOR_ENABLED and modified["changed"]:
ask = custom_messagebox("Unsaved Changes", "You have unsaved changes.\nSave before copying?", kind = "yesno")
drives = find_external_drives() if ask:
if not drives: save_file()
custom_messagebox("No External Drive Found", "No external drive detected."); return
drives = find_external_drives()
base_name = os.path.splitext(os.path.basename(path))[0] if not drives:
new_name = base_name + Extension custom_messagebox("No External Drive Found", "No external drive detected."); return
copied = [] base_name = os.path.splitext(os.path.basename(path))[0]
for d in drives: new_name = base_name + Extension
try:
dest = os.path.join(d, new_name) copied = []
shutil.copy2(path, dest) for d in drives:
copied.append(dest) try:
except Exception as e: dest = os.path.join(d, new_name)
custom_messagebox("Copy Error", f"Could not copy to {d}:\n{e}") shutil.copy2(path, dest)
copied.append(dest)
if copied: except Exception as e:
custom_messagebox("Copied Successfully", "Copied to:\n\n" + "\n".join(copied)) custom_messagebox("Copy Error", f"Could not copy to {d}:\n{e}")
win.destroy()
on_close_callback() if copied:
close_all(win.master) custom_messagebox("Copied Successfully", "Copied to:\n\n" + "\n".join(copied))
win.destroy()
def move_to_trash(): on_close_callback()
confirm = custom_messagebox("Move to Trash",f"Are you sure you want to move this file to trash?\n\n{path}", kind = "yesno") close_all(win.master)
if confirm:
try: def move_to_trash():
rel_path = os.path.relpath(path, SEARCH_ROOT) confirm = custom_messagebox("Move to Trash",f"Are you sure you want to move this file to trash?\n\n{path}", kind = "yesno")
dest_path = os.path.join(TRASH_DIR, rel_path) if confirm:
os.makedirs(os.path.dirname(dest_path), exist_ok=True) try:
shutil.move(path, dest_path) rel_path = os.path.relpath(path, SEARCH_ROOT)
custom_messagebox("Trashed", f"Moved to trash:\n{dest_path}") dest_path = os.path.join(TRASH_DIR, rel_path)
win.destroy() os.makedirs(os.path.dirname(dest_path), exist_ok=True)
on_close_callback() shutil.move(path, dest_path)
custom_messagebox("Trashed", f"Moved to trash:\n{dest_path}")
except Exception as e: win.destroy()
custom_messagebox("Trash Error", str(e)) on_close_callback()
def manual_replace(): except Exception as e:
drives = find_external_drives() custom_messagebox("Trash Error", str(e))
if not drives: custom_messagebox("No External Drive Found","No external drive detected."); return
base_name = os.path.splitext(os.path.basename(path))[0] def manual_replace():
new_name = base_name + Extension drives = find_external_drives()
replacement_path = None if not drives: custom_messagebox("No External Drive Found","No external drive detected."); return
for d in drives: base_name = os.path.splitext(os.path.basename(path))[0]
candidate = os.path.join(d, new_name) new_name = base_name + Extension
if os.path.exists(candidate): replacement_path = candidate; replacement_path = None
break for d in drives:
if not replacement_path: custom_messagebox("File Not Found",f"No replacement file named:\n\n{new_name}\n\nwas found on any external drive."); return candidate = os.path.join(d, new_name)
try: if os.path.exists(candidate): replacement_path = candidate;
with open(replacement_path,"r",errors="ignore") as f: new_contents = f.read() break
except Exception as e: custom_messagebox("Read Error",f"Could not read replacement file:\n{e}"); return if not replacement_path: custom_messagebox("File Not Found",f"No replacement file named:\n\n{new_name}\n\nwas found on any external drive."); return
try: try:
with open(path,"w",errors="ignore") as f: f.write(new_contents) with open(replacement_path,"r",errors="ignore") as f: new_contents = f.read()
except Exception as e: custom_messagebox("Write Error",f"Could not overwrite original file:\n{e}"); return except Exception as e: custom_messagebox("Read Error",f"Could not read replacement file:\n{e}"); return
box.delete("1.0",tk.END); box.insert(tk.END,new_contents); box.edit_modified(False); modified["changed"] = False try:
custom_messagebox("Replaced Successfully",f"File replaced using:\n{replacement_path}") with open(path,"w",errors="ignore") as f: f.write(new_contents)
except Exception as e: custom_messagebox("Write Error",f"Could not overwrite original file:\n{e}"); return
def close(): box.delete("1.0",tk.END); box.insert(tk.END,new_contents); box.edit_modified(False); modified["changed"] = False
if EDITOR_ENABLED and modified["changed"]: custom_messagebox("Replaced Successfully",f"File replaced using:\n{replacement_path}")
ask = custom_messagebox("Unsaved Changes", "You have unsaved changes.\nSave before closing?", kind = "yesno")
if ask: def close():
save_file() if EDITOR_ENABLED and modified["changed"]:
ask = custom_messagebox("Unsaved Changes", "You have unsaved changes.\nSave before closing?", kind = "yesno")
win.destroy() if ask:
on_close_callback() save_file()
def auto_close(): win.destroy()
if win.winfo_exists(): on_close_callback()
win.destroy()
on_close_callback() def auto_close():
if win.winfo_exists():
def close_all_button(): win.destroy()
ask = custom_messagebox("WARNING!", "Are you sure you want to cloose all windows?", kind = "yesno") on_close_callback()
if ask:
close_all(win.master) def close_all_button():
ask = custom_messagebox("WARNING!", "Are you sure you want to cloose all windows?", kind = "yesno")
win.protocol("WM_DELETE_WINDOW", close) if ask:
win.after(AUTO_CLOSE_SECONDS * 1000, auto_close) close_all(win.master)
if EDITOR_ENABLED: win.protocol("WM_DELETE_WINDOW", close)
tk.Button(win, text="Save", fg="green", command=save_file, padx=15, pady=8).pack(side ="left",padx=10, pady=10) win.after(AUTO_CLOSE_SECONDS * 1000, auto_close)
if TRASH_ENABLED == True: if EDITOR_ENABLED:
tk.Button(win, text="Move to Trash", fg="red", command=move_to_trash, padx=15, pady=8).pack(side ="left",padx=10, pady=10) tk.Button(win, text="Save", fg="green", command=save_file, padx=15, pady=8).pack(side ="left",padx=10, pady=10)
tk.Button(win, text="Copy to External Drive", command=copy_to_external, padx=15, pady=8).pack(side ="left",padx=10, pady=10) if TRASH_ENABLED == True:
tk.Button(win, text="Move to Trash", fg="red", command=move_to_trash, padx=15, pady=8).pack(side ="left",padx=10, pady=10)
tk.Button(win, text="Close", command=close, padx=15, pady=8).pack(side ="right",padx=10, pady=10)
tk.Button(win, text="Copy to External Drive", command=copy_to_external, padx=15, pady=8).pack(side ="left",padx=10, pady=10)
if CLOSE_ALL_ENABLED:
tk.Button(win, text="Close All", command=lambda: close_all_button(), padx=15, pady=8).pack(side ="right",padx=10, pady=10) tk.Button(win, text="Close", command=close, padx=15, pady=8).pack(side ="right",padx=10, pady=10)
if MANUEAL_REPLACE_ENABLED == True: if CLOSE_ALL_ENABLED:
tk.Button(win, text="Replace from External Drive", command=manual_replace, padx=15, pady=8).pack(side ="right",padx=10, pady=10) tk.Button(win, text="Close All", command=lambda: close_all_button(), padx=15, pady=8).pack(side ="right",padx=10, pady=10)
if MANUEAL_REPLACE_ENABLED == True:
tk.Button(win, text="Replace from External Drive", command=manual_replace, padx=15, pady=8).pack(side ="right",padx=10, pady=10)
def main():
root = tk.Tk()
root.withdraw()
default_font = tkfont.nametofont("TkDefaultFont") def main():
default_font.configure(family="Consolas", size=18) root = tk.Tk()
root.withdraw()
text_font = tkfont.nametofont("TkTextFont") default_font = tkfont.nametofont("TkDefaultFont")
text_font.configure(family="Consolas", size=18) default_font.configure(family="Consolas", size=18)
fixed_font = tkfont.nametofont("TkFixedFont") text_font = tkfont.nametofont("TkTextFont")
fixed_font.configure(family="Consolas", size=18) text_font.configure(family="Consolas", size=18)
root.option_add("*Dialog.msg.font", "Consolas 18") fixed_font = tkfont.nametofont("TkFixedFont")
root.option_add("*Dialog.msg.width", 60) fixed_font.configure(family="Consolas", size=18)
root.option_add("*Dialog.msg.height", 10)
root.option_add("*Dialog.msg.wrapLength", 600) root.option_add("*Dialog.msg.font", "Consolas 18")
root.option_add("*Dialog.msg.width", 60)
while True: root.option_add("*Dialog.msg.height", 10)
search_term = popup_search_input() root.option_add("*Dialog.msg.wrapLength", 600)
if not search_term:
root.destroy() while True:
return search_term = popup_search_input()
if not search_term:
matches = search_files(search_term) root.destroy()
return
if matches:
break matches = search_files(search_term)
custom_messagebox("No Results", "No matching files found.") if matches:
break
selected = popup_results_list(matches)
if not selected: custom_messagebox("No Results", "No matching files found.")
root.destroy()
return selected = popup_results_list(matches)
if not selected:
root.destroy()
open_windows = {"count": len(selected)} return
def window_closed():
open_windows["count"] -= 1 open_windows = {"count": len(selected)}
if open_windows["count"] == 0:
root.destroy() def window_closed():
open_windows["count"] -= 1
if open_windows["count"] == 0:
for path in selected: root.destroy()
popup_file_preview(path, window_closed)
root.mainloop() for path in selected:
popup_file_preview(path, window_closed)
if __name__ == "__main__": root.mainloop()
if __name__ == "__main__":
main() main()
+17 -12
View File
@@ -1,13 +1,18 @@
[Paths] [General]
SEARCH_ROOT = C:\CNCPrograms\Programs SEARCH_WINDOW_SIZE = 600x300
TRASH_DIR = C:\CNCPrograms\Trash RESULTS_WINDOW_SIZE = 1200x1000
PREVIEW_WINDOW_SIZE = 900x1000
[Features]
TRASH_ENABLED = False [Paths]
EDITOR_ENABLED = False SEARCH_ROOT = C:\CNCPrograms\Programs
MANUEAL_REPLACE_ENABLED = False TRASH_DIR = C:\CNCPrograms\Trash
CLOSE_ALL_ENABLED = True
AUTO_CLOSE_SECONDS = 300 [Features]
TRASH_ENABLED = False
[Copy] EDITOR_ENABLED = False
MANUEAL_REPLACE_ENABLED = False
CLOSE_ALL_ENABLED = True
AUTO_CLOSE_SECONDS = 300
[Copy]
Extension = Extension =