diff --git a/DemoPrograms/Demo_Timer_Periodic.py b/DemoPrograms/Demo_Timer_Periodic.py new file mode 100644 index 00000000..ff051fd2 --- /dev/null +++ b/DemoPrograms/Demo_Timer_Periodic.py @@ -0,0 +1,111 @@ +import PySimpleGUI as sg +import time + +""" + Demo Program - Periodic Timer Event + + How to use a thread to generate an event every x seconds + + One method of getting periodic timer event that's more predictable than using window.read(timeout=x) + The problem with using a timeout with window.read is that if any event happens prior to the timer + expiring, the timer event will not happen. The timeout parameter is not designed to provide a "heartbeat" + type of timer but rather to guarantee you will get an event within that amount of time, be it a + user-caused event or a timeout. + + Copyright 2022 PySimpleGUI +""" + +timer_running = {} + + +def timer_status_change(timer_id, start=None, stop=None, delete=None): + """ + Encapsulates/manages the timers dictionary + + :param timer_id: ID of timer to change status + :type timer_id: int + :param start: Set to True when timer is started + :type start: bool + :param stop: Set to True when timer is stopped + :type stop: bool + :param delete: Set to True to delete a timer + :type delete bool + """ + global timer_running + + if start: + timer_running[timer_id] = True + if stop: + timer_running[timer_id] = False + if delete: + del timer_running[timer_id] + + +def timer_is_running(timer_id): + """ + + :param timer_id: The timer ID to check + :type timer_id: int + :return: True if the timer is running + :rtype: bool + """ + if timer_running[timer_id]: + return True + return False + + + +def periodic_timer_thread(window, interval, timer_id): + """ + Thread that sends messages to the GUI after some interval of time + + :param window: Window the events will be sent to + :type window: sg.Window + :param interval: How frequently to send an event + :type interval: float + :param timer_id: A timer identifier + :type timer_id: int + """ + + + while True: + time.sleep(interval) # sleep until time to send a timer event + window.write_event_value(('-THREAD-', '-TIMER EVENT-'), timer_id) + if not timer_is_running(timer_id): # If timer has been stopped, delete it and return from thread + timer_status_change(timer_id, delete=True) + return + + +def main(): + layout = [[sg.Text('Window with periodic time events')], + [sg.Text(key='-MESSAGE-')], + [sg.Text('Timer Status:'), sg.Text(key='-TIMER STATUS-')], + [sg.Text('Duration:'), sg.In(s=3, key='-DURATION-'), sg.Button('Start')], + [sg.Text('Timer ID:'), sg.In(s=3, key='-STOP ID-'), sg.Button('Stop'),], + [ sg.Button('Dummy'), sg.Button('Exit')], ] + + window = sg.Window('Blinking LED Window', layout) + + timer_counter = 0 + # --------------------- EVENT LOOP --------------------- + while True: + event, values = window.read() + if event in (sg.WIN_CLOSED, 'Exit'): + break + window['-MESSAGE-'].update(f'{event} {values}') + window['-TIMER STATUS-'].update(f'{timer_running}') + if event == 'Start': + if values['-DURATION-']: + timer_status_change(timer_counter, start=True) + window.start_thread(lambda: periodic_timer_thread(window, float(values['-DURATION-']), timer_counter), ('-THREAD-', '-THREAD ENDED-')) + timer_counter += 1 + else: + window['-MESSAGE-'].update('Please enter a numeric duration') + elif event == 'Stop': + if values['-STOP ID-']: + timer_status_change(int(values['-STOP ID-']), stop=True) + + window.close() + +if __name__ == '__main__': + main()