diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 00000000..ac3aada9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,18 @@ +--- +name: Custom issue template +about: PySimpleGUI custom template +title: "[FILL IN PREFIX] Description" +labels: '' +assignees: '' + +--- + +### Issue Title Prefixes - [Bug], [Question], [Enhancement] + +### What is the import statement in your program that imports the PySimpleGUI package? + +### Your PySimpleGUI version if known: + +### Operating System and version: + +### What's up? diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2ea215f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +venv +__pycache__ diff --git a/DemoPrograms/Demo_Desktop_Widget_CPU_Graph.py b/DemoPrograms/Demo_Desktop_Widget_CPU_Graph.py index 801cd61e..dff14e66 100644 --- a/DemoPrograms/Demo_Desktop_Widget_CPU_Graph.py +++ b/DemoPrograms/Demo_Desktop_Widget_CPU_Graph.py @@ -1,6 +1,5 @@ #!/usr/bin/env python import sys -import sys if sys.version_info[0] >= 3: import PySimpleGUI as sg else: diff --git a/DemoPrograms/Demo_Threaded_Progressbar.py b/DemoPrograms/Demo_Threaded_Progressbar.py new file mode 100644 index 00000000..5acb07db --- /dev/null +++ b/DemoPrograms/Demo_Threaded_Progressbar.py @@ -0,0 +1,136 @@ +#! /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=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() + + 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) + + 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: + sg.Popup('Execution finished') + break + window.Close() + +def main(): + myClass = SomeFancyClass() + progressbar(myClass) + + +if __name__ == '__main__': + main() diff --git a/PySimpleGUI.py b/PySimpleGUI.py index a9680209..4cfd96ce 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -1548,6 +1548,8 @@ class Button(Element): folder_name = tk.filedialog.askdirectory(initialdir=self.InitialFolder) # show the 'get folder' dialog box if folder_name != '': try: + if sys.platform == 'win32': + folder_name = folder_name.replace("/", "\\") strvar.set(folder_name) self.TKStringVar.set(folder_name) except: @@ -1558,6 +1560,8 @@ class Button(Element): else: file_name = tk.filedialog.askopenfilename(filetypes=filetypes, initialdir=self.InitialFolder) # show the 'get file' dialog box if file_name != '': + if sys.platform == 'win32': + file_name = file_name.replace("/", "\\") strvar.set(file_name) self.TKStringVar.set(file_name) elif self.BType == BUTTON_TYPE_COLOR_CHOOSER: @@ -1572,6 +1576,8 @@ class Button(Element): file_name = tk.filedialog.askopenfilenames(filetypes=filetypes, initialdir=self.InitialFolder) if file_name != '': file_name = ';'.join(file_name) + if sys.platform == 'win32': + file_name = file_name.replace("/", "\\") strvar.set(file_name) self.TKStringVar.set(file_name) elif self.BType == BUTTON_TYPE_SAVEAS_FILE: @@ -1581,6 +1587,8 @@ class Button(Element): file_name = tk.filedialog.asksaveasfilename(filetypes=filetypes, initialdir=self.InitialFolder) # show the 'get file' dialog box if file_name != '': + if sys.platform == 'win32': + file_name = file_name.replace("/", "\\") strvar.set(file_name) self.TKStringVar.set(file_name) elif self.BType == BUTTON_TYPE_CLOSES_WIN: # this is a return type button so GET RESULTS and destroy window diff --git a/PySimpleGUIWx/PySimpleGUIWx.py b/PySimpleGUIWx/PySimpleGUIWx.py index bcf09323..e15c4c48 100644 --- a/PySimpleGUIWx/PySimpleGUIWx.py +++ b/PySimpleGUIWx/PySimpleGUIWx.py @@ -2985,7 +2985,7 @@ class Window: self.Font = font if font else DEFAULT_FONT self.RadioDict = {} self.BorderDepth = border_depth - self.WindowIcon = icon if icon is not None else Window.user_defined_icon + self.WindowIcon = Window.user_defined_icon if Window.user_defined_icon is not None else icon if icon is not None else DEFAULT_WINDOW_ICON self.AutoClose = auto_close self.NonBlocking = False self.TKroot = None @@ -5803,15 +5803,8 @@ def SetOptions(icon=None, button_color=None, element_size=(None, None), button_e global DEFAULT_ELEMENT_TEXT_COLOR global DEFAULT_INPUT_TEXT_COLOR global DEFAULT_TOOLTIP_TIME - global _my_windows - if icon: - try: - with open(icon, 'r') as icon_file: - pass - except: - raise FileNotFoundError - _my_windows.user_defined_icon = icon + Window.user_defined_icon = icon if button_color != None: DEFAULT_BUTTON_COLOR = button_color diff --git a/exemaker/readme.md b/exemaker/readme.md index 5bf9a2ce..6ba398bf 100644 --- a/exemaker/readme.md +++ b/exemaker/readme.md @@ -1,4 +1,4 @@ -# PySimpleGUI-HowDoI +# PySimpleGUI-exemaker ## Introduction This package contains a GUI front-end to PyInstaller. Use this tool to create EXE files from your python programs