From 7271786911919e15bd117a457dd58bbd3d884248 Mon Sep 17 00:00:00 2001 From: Orsiris de Jong Date: Thu, 6 Dec 2018 19:11:07 +0100 Subject: [PATCH 1/2] Threaded class methods updating progressbar --- DemoPrograms/Demo_Threaded_Progressbar.py | 132 ++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 DemoPrograms/Demo_Threaded_Progressbar.py diff --git a/DemoPrograms/Demo_Threaded_Progressbar.py b/DemoPrograms/Demo_Threaded_Progressbar.py new file mode 100644 index 00000000..5e8d69c0 --- /dev/null +++ b/DemoPrograms/Demo_Threaded_Progressbar.py @@ -0,0 +1,132 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Quick and dirty threading example for PySimpleGUI progress bar executing class methods +Written in 2018 by Orsiris de Jong, www.netpower.fr, works with Python 3+ +""" + +from threading import Thread +from concurrent.futures import Future +from time import time, sleep +import PySimpleGUI as Sg + +# Helper functions for threading class functions with return values using future from https://stackoverflow.com/a/19846691/2635443 +def call_with_future(fn, future, args, kwargs): + try: + result = fn(*args, **kwargs) + future.set_result(result) + except Exception as exc: + future.set_exception(exc) + +def threaded(fn): + def wrapper(*args, **kwargs): + future = Future() + Thread(target=call_with_future, args=(fn, future, args, kwargs)).start() + return future + return wrapper + +# Some fancy class which functions should be threaded or not using decorator +class SomeFancyClass: + def __init__(self): + self.somevar = 'Some initial class variable' + + # Adding this decoroator to thread the function below + @threaded + def func_to_be_threaded(self): + print(self.somevar) + sleep(7) + self.somevar = 'New value' + return('Return from func_to_be_threaded is ' + self.somevar) + + + @threaded + def another_thread_function(self): + print(self.somevar) + sleep(3) + return ('Return from another_thread_function is ' + self.somevar) + + def non_threaded_function(self): + print('waiting') + sleep(5) + print('finished waiting') + +# The main progress bar method +def progressbar(myClass): + maxwait = 10 # Wait for 10 seconds max with the progress bar before asking to cancel + progress = 0 + startTime = 0 + currentTime = 0 + + function_one = None + function_two = None + function_one_done = False + function_two_done = False + + # layout of the progress bar window + layout = [[Sg.Text('Launching threads')], + [Sg.ProgressBar(100, orientation='h', size=(20, 20), key='progressbar')], + [Sg.Cancel()]] + + # create the progress bar + window = Sg.Window('Init', text_justification='center').Layout(layout) + + startTime = time() + + while True: + event, values = window.Read(timeout=1) + if event == 'Cancel' or event is None: + window.Close() + exit() + + if function_one == None: + # Launch first threaded function + function_one = myClass.func_to_be_threaded() + + if function_two == None: + # Launch second threaded function + function_two = myClass.another_thread_function() + + print('function_one is done: ' + str(function_one.done())) + print('function_two is done: ' + str(function_two.done())) + + if function_one.done() == True and function_one_done == False: + function_one_done = True + print(function_one.result()) + progress += 70 + + if function_two.done() == True and function_two_done == False: + function_two_done = True + print(function_two.result()) + progress += 30 + + window.FindElement('progressbar').UpdateBar(progress) + sleep(.3) # Arbitrary time between loops so UI stays snappy + + currentTime = time() + if (currentTime - startTime) > maxwait: + action = Sg.Popup('Seems that it takes too long, shall we continue the program',custom_text=('No', 'Yes')) + if action == 'No': + function_one.cancel() + function_two.cancel() + break + elif action == 'Yes': + startTime = time() # Lets give another 10 seconds or check if functions must be stopped + """ + TODO: We could relaunch the functions with + function_one.cancel() + if function_one.cancelled(): + function_one = myClass.func_to_be_threaded() + """ + + if progress >= 100: + break + window.Close() + +def main(): + myClass = SomeFancyClass() + progressbar(myClass) + + +if __name__ == '__main__': + main() From 6fee2e0edde1749b5f22558712084c11b7d951cc Mon Sep 17 00:00:00 2001 From: Orsiris de Jong Date: Thu, 6 Dec 2018 20:59:26 +0100 Subject: [PATCH 2/2] Minor enhancements --- DemoPrograms/Demo_Threaded_Progressbar.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/DemoPrograms/Demo_Threaded_Progressbar.py b/DemoPrograms/Demo_Threaded_Progressbar.py index 5e8d69c0..5acb07db 100644 --- a/DemoPrograms/Demo_Threaded_Progressbar.py +++ b/DemoPrograms/Demo_Threaded_Progressbar.py @@ -9,7 +9,7 @@ Written in 2018 by Orsiris de Jong, www.netpower.fr, works with Python 3+ from threading import Thread from concurrent.futures import Future from time import time, sleep -import PySimpleGUI as Sg +import PySimpleGUI as sg # Helper functions for threading class functions with return values using future from https://stackoverflow.com/a/19846691/2635443 def call_with_future(fn, future, args, kwargs): @@ -64,18 +64,22 @@ def progressbar(myClass): function_two_done = False # layout of the progress bar window - layout = [[Sg.Text('Launching threads')], - [Sg.ProgressBar(100, orientation='h', size=(20, 20), key='progressbar')], - [Sg.Cancel()]] + layout = [[sg.Text('Launching threads')], + [sg.ProgressBar(100, orientation='h', size=(20, 20), key='progressbar')], + [sg.Cancel()]] # create the progress bar - window = Sg.Window('Init', text_justification='center').Layout(layout) + window = sg.Window('Init', text_justification='center').Layout(layout) startTime = time() while True: - event, values = window.Read(timeout=1) + event, values = window.Read(timeout=300) if event == 'Cancel' or event is None: + if function_one != None: + function_one.cancel() + if function_two != None: + function_two.cancel() window.Close() exit() @@ -101,11 +105,10 @@ def progressbar(myClass): progress += 30 window.FindElement('progressbar').UpdateBar(progress) - sleep(.3) # Arbitrary time between loops so UI stays snappy currentTime = time() if (currentTime - startTime) > maxwait: - action = Sg.Popup('Seems that it takes too long, shall we continue the program',custom_text=('No', 'Yes')) + action = sg.Popup('Seems that it takes too long, shall we continue the program',custom_text=('No', 'Yes')) if action == 'No': function_one.cancel() function_two.cancel() @@ -120,6 +123,7 @@ def progressbar(myClass): """ if progress >= 100: + sg.Popup('Execution finished') break window.Close() @@ -129,4 +133,4 @@ def main(): if __name__ == '__main__': - main() + main()