You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PySimpleGUI结合线程实现循环的手动终止?

Fixing GUI Freeze + Adding Manual Stop for Your Meetup Auto-Message Tool

Hey there! I totally get the frustration with the beach ball on Mac—this happens because your heavy auto-sending loop is running in the main thread, which blocks PySimpleGUI's event handling (the thing that makes your window responsive). Let's fix this with threading, and add a proper manual stop feature too.

The Core Issue

PySimpleGUI relies on the main thread to process window events (clicks, updates, etc.). When you run that long loop with time.sleep() and pyautogui calls directly after clicking "Ok", you're hogging the main thread—so the window can't respond to any user input until the loop finishes, hence the freeze.

The Solution: Threading + Stop Flag

We'll split the work:

  1. Keep the GUI event loop in the main thread (where it belongs)
  2. Move the auto-sending logic to a separate worker thread
  3. Add a global "stop" flag to let the user terminate the loop at any time

Modified Full Code

Here's the updated version with all fixes. I've commented the key changes:

import webbrowser
import pyautogui
import random
from random import choice
import time
import PySimpleGUI as sg
import threading

# Global flag to control the worker thread
stop_thread = False

font = ("Arial", 13)
layout = [
    [sg.Text('Select your .txt profiles file. Must be a list of URLs with each URL on a new line', font=("Arial", 16))],
    [sg.Combo(sorted(sg.user_settings_get_entry('-filenames-', [])), default_value=sg.user_settings_get_entry('-last filename-', ''), size=(50, 1), key='-FILENAME-', font="Arial, 16"), sg.FileBrowse(font="Arial, 16"), sg.B('Clear History', font="Arial, 16")],
    [sg.Button(' Ok ', bind_return_key=True, font="Arial, 16", button_color='green'), sg.Button('Cancel', font="Arial, 16")]
]
window = sg.Window('Meetup Auto Message - Profiles selection', layout)
lastFile = ""
while True:
    event, values = window.read()
    if event in (sg.WIN_CLOSED, 'Cancel'):
        break
    if event == ' Ok ':
        # Update settings as before
        sg.user_settings_set_entry('-filenames-', list(set(sg.user_settings_get_entry('-filenames-', []) + [values['-FILENAME-'], ])))
        sg.user_settings_set_entry('-last filename-', values['-FILENAME-'])
        lastFile=values['-FILENAME-']
        break
    elif event == 'Clear History':
        sg.user_settings_set_entry('-filenames-', [])
        sg.user_settings_set_entry('-last filename-', '')
        window['-FILENAME-'].update(values=[], value='')
window.close()

# Process the profile links as before
profileLink = open(lastFile).read().splitlines()
profileLink = [item.replace("+","name=") for item in profileLink]
newLinks = [item.replace("%","name=") for item in profileLink]
nameList = [i.split("name=")[1] for i in newLinks]
num_lines = sum(1 for line in open(lastFile))
BAR_MAX = num_lines

# Worker function: runs in a separate thread to handle auto-messaging
def auto_send_messages(message_options, names, links, window):
    global stop_thread
    for i, (name, link) in enumerate(zip(names, links)):
        # Check if user requested stop before each iteration
        if stop_thread:
            break
        
        # Your original messaging logic
        message = f"Hi {name}, \n{choice(message_options)}"
        webbrowser.open(link)
        time.sleep(15)
        pyautogui.press("tab")
        pyautogui.write(message, interval=random.uniform(0.03, 0.15))
        with pyautogui.hold("command"):
            pyautogui.press("w")
        
        # Update progress bar (safe in this case, but for strict thread safety, use write_event_value)
        window['-PROG-'].update(i+1)
        
        # Long sleep after 10th message—check stop flag here too
        if i == 10:
            for _ in range(300):  # Check stop every second instead of sleeping straight
                if stop_thread:
                    break
                time.sleep(1)

layout1 = [
    [sg.Text('This program will take your messages, rotate them, add random delays and automatically message people from your profile list. You can now use your computer (or stop the program) while it runs!', font="Arial, 18")],
    [sg.Text('Enter your messages below just the content, we handle the "Hi Name" part.', font="Arial, 18")],
    [sg.Multiline(size=(100,10), key='textBox1', font=font)],
    [sg.Multiline(size=(100,10), key='textBox2', font=font)],
    [sg.Multiline(size=(100,10), key='textBox3', font=font)],
    [sg.Multiline(size=(100,10), key='textBox4', font=font)],
    [sg.Multiline(size=(100,10), key='textBox5', font=font)],
    [sg.Button(' Ok ', bind_return_key=True, font="Arial, 24", button_color='green'), sg.Button(' Cancel ', font="Arial, 24")],
    [sg.Text('Progress Bar', font="Arial, 15")],
    [sg.ProgressBar(BAR_MAX, orientation='h', size=(20,20), key='-PROG-')]
]
window=sg.Window('Meetup Auto Message', layout1, element_justification='c')
worker_thread = None

while True:
    event, values = window.read()
    
    if event in (sg.WIN_CLOSED, ' Cancel '):
        # Set stop flag to terminate worker thread
        stop_thread = True
        # Wait for thread to finish (optional but clean)
        if worker_thread and worker_thread.is_alive():
            worker_thread.join()
        break
    
    if event == ' Ok ':
        # Reset stop flag in case user runs multiple times
        stop_thread = False
        # Grab message options from GUI
        messageOptions = [values['textBox1'], values['textBox2'], values['textBox3'], values['textBox4'], values['textBox5']]
        # Start worker thread (daemon=True means it exits when main thread does)
        worker_thread = threading.Thread(
            target=auto_send_messages,
            args=(messageOptions, nameList, profileLink, window),
            daemon=True
        )
        worker_thread.start()

window.close()

Key Changes Explained

  1. Global Stop Flag: stop_thread acts as a signal to the worker thread to exit its loop. We check this flag before every major operation, including during the 5-minute sleep (split into 1-second checks so the stop is responsive).
  2. Worker Thread: The auto_send_messages function holds all your original messaging logic, but runs in a separate thread. This leaves the main thread free to handle GUI events.
  3. Thread Safety: I split the 300-second sleep into 1-second chunks with stop checks—this ensures the program responds quickly when you click Cancel. For strict GUI thread safety, you could use window.write_event_value to send a progress update event to the main thread, but the direct update() works here for simplicity.
  4. Clean Exit: When you click Cancel or close the window, we set the stop flag and wait for the worker thread to finish before closing the window, preventing any leftover processes.

Testing This

  • Run the program, select your profile file, enter messages, click Ok. The main window will stay responsive—you can move it, click Cancel at any time.
  • Click Cancel, and the auto-sending loop will stop immediately (or within 1 second if it's in the long sleep).

This should eliminate the Mac beach ball freeze and give you the manual stop functionality you need!

内容的提问来源于stack exchange,提问作者dan.rad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 16:07:29