Threading improvements

This commit is contained in:
edschuy95 2026-01-22 15:06:25 -05:00
parent 6b0396a4b0
commit 40ff55f700
3 changed files with 74 additions and 58 deletions

View file

@ -2,7 +2,7 @@
a = Analysis( a = Analysis(
['RA3MP3Playback.py'], ['RA3MP3Playback.pyw'],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[], datas=[],
@ -10,7 +10,7 @@ a = Analysis(
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
runtime_hooks=[], runtime_hooks=[],
excludes=['tkinter', 'unittest', 'http', 'pydoc', 'doctest'], excludes=['unittest', 'http', 'pydoc', 'doctest'],
noarchive=False, noarchive=False,
optimize=0, optimize=0,
) )
@ -29,7 +29,7 @@ exe = EXE(
upx=True, upx=True,
upx_exclude=[], upx_exclude=[],
runtime_tmpdir=None, runtime_tmpdir=None,
console=True, console=False,
disable_windowed_traceback=False, disable_windowed_traceback=False,
argv_emulation=False, argv_emulation=False,
target_arch=None, target_arch=None,

View file

@ -603,11 +603,9 @@ def send_command(proc, cmd):
# ========================================================== # ==========================================================
# ======================== MAIN =========================== # ======================== MAIN ===========================
def main(): def game_launch(gameargs):
global playlist, ra3_maps, pty_proc, playlist_path, ra3_maps_path, arena0_blacklist, arena0_bl_path, master_fd global playlist, ra3_maps, pty_proc, playlist_path, ra3_maps_path, arena0_blacklist, arena0_bl_path, master_fd
print(f"Loading Quake 3 Arena...")
# Use debug paths if enabled # Use debug paths if enabled
game_exe = DEBUG_GAME_EXE if DEBUG else DEFAULT_GAME_EXE game_exe = DEBUG_GAME_EXE if DEBUG else DEFAULT_GAME_EXE
playlist_path = DEBUG_PLAYLIST if DEBUG else DEFAULT_PLAYLIST playlist_path = DEBUG_PLAYLIST if DEBUG else DEFAULT_PLAYLIST
@ -633,50 +631,24 @@ def main():
chosen_mod = None chosen_mod = None
run_mod = [] run_mod = []
if "fs_game" not in sys.argv[1:]: run_mod = ["+set", "fs_game", chosen_mod]
game_path = Path(game_exe)
items = find_pk3_subfolders(game_path.parent)
menu = TextMenu(
items,
width=500,
height=400,
title="Quake III Arena mod loader menu",
border_fg="red",
border_bg="black",
inside_fg="dark red",
inside_bg="black",
selected_fg="black",
selected_bg="red",
timeout=10
)
choice=menu.show()
if choice == None:
sys.exit(0)
if choice == "errmenutimeout":
chosen_mod = "baseq3"
else:
chosen_mod = choice.split("\n", 1)[0]
run_mod = ["+set", "fs_game", chosen_mod]
#sys.exit(0)
if os.name == "nt": if os.name == "nt":
pty_proc = subprocess.Popen( pty_proc = subprocess.Popen(
[game_exe, "+set", "ttycon", "1"] + run_mod + sys.argv[1:], [game_exe, "+set", "ttycon", "1"] + gameargs,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
bufsize=0, bufsize=0,
universal_newlines=False universal_newlines=False,
creationflags=subprocess.CREATE_NO_WINDOW
) )
master_fd = None master_fd = None
else: else:
master_fd, slave_fd = pty.openpty() master_fd, slave_fd = pty.openpty()
pty_proc = subprocess.Popen( pty_proc = subprocess.Popen(
[game_exe, "+set", "ttycon", "1"] + run_mod + sys.argv[1:], [game_exe, "+set", "ttycon", "1"] + gameargs,
stdin=slave_fd, stdin=slave_fd,
stdout=slave_fd, stdout=slave_fd,
stderr=slave_fd, stderr=slave_fd,
@ -718,6 +690,64 @@ def main():
pass pass
pygame.mixer.quit() pygame.mixer.quit()
def main():
global playlist, ra3_maps, pty_proc, playlist_path, ra3_maps_path, arena0_blacklist, arena0_bl_path, master_fd
game_exe = DEBUG_GAME_EXE if DEBUG else DEFAULT_GAME_EXE
chosen_mod = None
run_mod = []
if "fs_game" not in sys.argv[1:]:
game_path = Path(game_exe)
items = find_pk3_subfolders(game_path.parent)
# Create a queue to get the result from the menu thread
menu_result_queue = queue.Queue()
def run_menu():
menu = TextMenu(
items,
width=500,
height=400,
title="Quake III Arena mod loader menu",
border_fg="red",
border_bg="black",
inside_fg="dark red",
inside_bg="black",
selected_fg="black",
selected_bg="red",
timeout=10
)
result = menu.show()
menu_result_queue.put(result)
# Run the menu in a separate thread
menu_thread = threading.Thread(target=run_menu)
menu_thread.start()
menu_thread.join() # Wait for menu to complete
# Get the result from the queue
choice = menu_result_queue.get()
if choice == None:
sys.exit(0)
if choice == "errmenutimeout":
chosen_mod = "baseq3"
else:
chosen_mod = choice.split("\n", 1)[0]
run_mod = ["+set", "fs_game", chosen_mod]
#sys.exit(0)
game_args = run_mod + sys.argv[1:]
# Start game launcher thread
game_launch_thread = threading.Thread(target=game_launch,args=(game_args,), daemon=True)
game_launch_thread.start()
game_launch_thread.join()
if __name__ == "__main__": if __name__ == "__main__":
lock_fd, lock_path = acquire_single_instance("q3a_launcher.lock") lock_fd, lock_path = acquire_single_instance("q3a_launcher.lock")
if lock_fd is None: if lock_fd is None:

View file

@ -291,26 +291,12 @@ class TextMenu:
# Run the GUI event loop # Run the GUI event loop
self.root.mainloop() self.root.mainloop()
# Clean up - only destroy if root still exists # Clean up - destroy the window immediately to prevent hanging
if self.root and str(self.root) != '.': try:
try: if self.root and str(self.root) != '.':
self.root.destroy() self.root.destroy()
except tk.TclError: except tk.TclError:
# Window may already be destroyed # Window may already be destroyed
pass pass
return self.selected return self.selected
if __name__ == "__main__":
# Example usage
choices = [
"Option 1\nDescription for option 1",
"Option 2\nDescription for option 2",
"Option 3\nDescription for option 3",
"Option 4\nDescription for option 4"
]
menu = TextMenu(choices, title="Test Menu", timeout=10)
result = menu.show()
print(f"Selected: {result}")