I'm trying to create an PyGui Application using PysimpleGUI

78 Views Asked by At
import pyautogui
import time
import PySimpleGUI as sg


def main_app():
    try:
       layout = [[sg.Text('Enter the number of clicks to perform: '), sg.InputText()],
              [sg.Text('Enter the interval between messages (in seconds): '), sg.InputText()],
              [sg.Button('Start'), sg.Button('Stop')]]


    window = sg.Window('Automatic Clicker', layout)


    while True:
        event, values = window.read()
        if event == 'Start':
            num_clicks = int(values[0])
            type_interval = float(values[1])
            time.sleep(5)
            for i in range(num_clicks):
                pyautogui.click()
                time.sleep(10)
                pyautogui.typewrite("Hello, wORLD how are you", interval=5)
                pyautogui.press('enter')
                time.sleep(type_interval)
            time.sleep(5)

        if event == 'Stop' or event == sg.WINDOW_CLOSED:
            break

except Exception as e:
    print(f"An error occurred: {str(e)}")

finally:
    window.close()

if __name__ == "__main__":
    main_app()

I'm trying to use pysimplegui for creating GUI application,This application is very simple it has 2 buttons start, stop and two input fields where first field takes input to how may times "HELLO World How are you" should be printed. Second input defines delays between printing letters, So when start is given it starts to print the given sentence , but while pressing stop it doesn't stops. instead i have to kill the application from the terminal , how do i make the application stop by pressing stop button

2

There are 2 best solutions below

0
On

A thread, as Jason's answer shows quite well, is usually the best way to handle these kinds of long operations. I decided to try a slightly different approach. Because so much of your time is simply sleeping, I thought maybe you could get away with using timers to control the flow.

The problem you're running into is that during the time you're sleeping, everything's stopped. The GUI, responding to your input, etc.... it's all stopped.

Using the PySimpleGUI Timer API you can run the timer in the background and continue to perform actions in your GUI.

The implementation is overkill in the use of write_event_value. This is so that the code could be moved into a thread and then the thread would communicate with the GUI using the same write_event_value call.

The Timer API calls are not yet released on PyPI, so you'll have to get the latest PySimpleGUI from GitHub. This code will be released to PyPI very soon (by the end of the month or sooner).

#!/usr/bin/env python
import PySimpleGUI as sg

"""
    Demo - State Machine using timers

    State Machines are very useful when you need to perform a series of operations that you
    cannot do all at once due to a time constraint. Particularly problematic are operations
    where you would normally use "sleeps" as part of the sequence.

    In this Demo Program, we're going to use the PySimpleGUI Timer API calls to provide our
    sleep-like behavior.

    The sequence of operations we're going to run are:
        User clicks a "Send" Button to start the sequence:
            1. A "Status Window" is shown that says "Sending" and Disable SEND button
            2. Sleep for 3 seconds
            3. Close the "Status Window"
            4. Sleep for 2 seconds
            5. Enable SEND button and Go to state 1

    Control of the state machine will be through the PySimpleGUI events.  This will enable you to use threads
    for any of these states and have the threads communicate the state transitions using the same write_event_value used
    in this example.
"""


class State:
    stopped = 'stopped'
    start = 'start'
    delay_3_sec = 'delay 3 seconds'
    close_win = 'close win'
    delay_2_sec = 'delay 2 seconds'
    enable_send = 'enable send'


TIMER1 = 3000
TIMER2 = 2000
NEXT_STATE = '-NEXT-'


def make_send_window():
    layout = [[sg.Text('Send Window')],
              [sg.Text('State:'), sg.Text(key='-STATE-')]]

    # Create window a little lower on screen so windows don't overlap
    window = sg.Window('Send Window', layout, finalize=True, relative_location=(0, 150))
    return window


def main():
    layout = [[sg.Text('State Machine Example', font='_ 14')],
              [sg.Text('Click Send to begin sequence')],
              [sg.Text('State:'), sg.Text(key='-STATE-')],
              [sg.Button('Send', key='-SEND-'), sg.Button('Exit')]]

    window = sg.Window('State Machine Example', layout, font='Any 12')

    window_send = None
    state = State.stopped

    while True:
        event, values = window.read()
        if event == sg.WIN_CLOSED or event == 'Exit':
            break

        if event == '-SEND-':
            state = State.start
        elif event == NEXT_STATE:
            state = values[event]

        window['-STATE-'].update(state)
        if window_send:
            window_send.refresh()

        # ----- STATE MACHINE PROCESSING -----
        if state == State.start:
            window['-SEND-'].update(disabled=True)
            window_send = make_send_window()
            window.write_event_value(NEXT_STATE, State.delay_3_sec)
        elif event == sg.TIMER_KEY and state == State.delay_3_sec:      # be sure the if with the timer check AND state is above if with only state
            window.write_event_value(NEXT_STATE, State.close_win)
        elif state == State.delay_3_sec:
            window.timer_start(TIMER1, repeating=False)
        elif state == State.close_win:
            window_send.close()
            window_send = None
            window.write_event_value(NEXT_STATE, State.delay_2_sec)
        elif event == sg.TIMER_KEY and state == State.delay_2_sec:
            window.write_event_value(NEXT_STATE, State.enable_send)
        elif state == State.delay_2_sec:
            window.timer_start(TIMER2, repeating=False)
        elif state == State.enable_send:
            window['-SEND-'].update(disabled=False)
            window.write_event_value(NEXT_STATE, State.stopped)

    window.close()


if __name__ == '__main__':
    main()
0
On

Your script will be blocked by the for loop with the time.sleep call, also the GUI not responding, using the sub-thread for your job.

Example Code

import time
import datetime
import threading
import PySimpleGUI as sg

def job(window):
    global running
    while running:
        window.write_event_value('Event', datetime.datetime.now().strftime("%H:%M:%S"))
        time.sleep(0.1)

layout = [[sg.Button('Start'), sg.Button('Stop')]]
window = sg.Window('Threading', layout)
running, old = False, None

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED:
        running = False
        time.sleep(0.5)
        break

    elif event == 'Start' and not running:
        running = True
        window['Start'].update(disabled=True)
        threading.Thread(target=job, args=(window, ), daemon=True).start()

    elif event == 'Stop':
        running = False
        window['Start'].update(disabled=False)

    elif event == 'Event':
        now = values[event]
        if now != old:
            print(now)
            old = now

window.close()