From 0ec43ac11264815f4240e1d221994e921e205cd2 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Mon, 16 Jul 2018 14:52:16 -0400 Subject: [PATCH] Renamed ALL oprtional parameters Switched from CamelCase to all_lower_case --- Demo DisplayHash1and256.py | 8 +- Demo DuplicateFileFinder.py | 10 +- Demo High Level APIs.py | 3 +- Demo HowDoI.py | 21 +- Demo Recipes.py | 48 ++-- PySimpleGUI.py | 555 ++++++++++++++++++------------------ readme.md | 420 +++++++++++++-------------- 7 files changed, 531 insertions(+), 534 deletions(-) diff --git a/Demo DisplayHash1and256.py b/Demo DisplayHash1and256.py index fa4545cc..dbc47afc 100644 --- a/Demo DisplayHash1and256.py +++ b/Demo DisplayHash1and256.py @@ -51,7 +51,7 @@ def compute_sha256_hash_for_file(filename): # ---------------------------------------------------------------------- # def HashManuallyBuiltGUI(): # ------- Form design ------- # - with SG.FlexForm('SHA-1 & 256 Hash', AutoSizeText=True) as form: + with SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True) as form: form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')], [SG.InputText(), SG.FileBrowse()], [SG.Submit(), SG.Cancel()]] @@ -61,14 +61,14 @@ def HashManuallyBuiltGUI(): if source_filename != '': hash_sha1 = compute_sha1_hash_for_file(source_filename).upper() hash_sha256 = compute_sha256_hash_for_file(source_filename).upper() - SG.MsgBox( 'Display A Hash in PySimpleGUI', 'The SHA-1 Hash for the file\n', source_filename, hash_sha1, 'SHA-256 is', hash_sha256, LineWidth=75) + SG.MsgBox( 'Display A Hash in PySimpleGUI', 'The SHA-1 Hash for the file\n', source_filename, hash_sha1, 'SHA-256 is', hash_sha256, line_width=75) else: SG.MsgBoxError('Display A Hash in PySimpleGUI', 'Illegal filename') else: SG.MsgBoxError('Display A Hash in PySimpleGUI', '* Cancelled *') def HashManuallyBuiltGUINonContext(): # ------- Form design ------- # - form = SG.FlexForm('SHA-1 & 256 Hash', AutoSizeText=True) + form = SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True) form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')], [SG.InputText(), SG.FileBrowse()], [SG.Submit(), SG.Cancel()]] @@ -78,7 +78,7 @@ def HashManuallyBuiltGUINonContext(): if source_filename != '': hash_sha1 = compute_sha1_hash_for_file(source_filename).upper() hash_sha256 = compute_sha256_hash_for_file(source_filename).upper() - SG.MsgBox( 'Display A Hash in PySimpleGUI', 'The SHA-1 Hash for the file\n', source_filename, hash_sha1, 'SHA-256 is', hash_sha256, LineWidth=75) + SG.MsgBox( 'Display A Hash in PySimpleGUI', 'The SHA-1 Hash for the file\n', source_filename, hash_sha1, 'SHA-256 is', hash_sha256, line_width=75) else: SG.MsgBoxError('Display A Hash in PySimpleGUI', 'Illegal filename') else: SG.MsgBoxError('Display A Hash in PySimpleGUI', '* Cancelled *') diff --git a/Demo DuplicateFileFinder.py b/Demo DuplicateFileFinder.py index b32f8cf1..49b061bb 100644 --- a/Demo DuplicateFileFinder.py +++ b/Demo DuplicateFileFinder.py @@ -1,6 +1,5 @@ import hashlib import os -import win32clipboard import PySimpleGUI as gg @@ -10,15 +9,13 @@ import PySimpleGUI as gg def FindDuplicatesFilesInFolder(path): shatab = [] total = 0 - small = (1024) small_count, dup_count, error_count = 0,0,0 pngdir = path if not os.path.exists(path): - gg.MsgBox('De-Dupe', '** Folder doesn\'t exist***', path) + gg.MsgBox('Duplicate Finder', '** Folder doesn\'t exist***', path) return pngfiles = os.listdir(pngdir) total_files = len(pngfiles) - not_cancelled = True for idx, f in enumerate(pngfiles): if not gg.EasyProgressMeter('Counting Duplicates', idx+1, total_files, 'Counting Duplicate Files'): break @@ -32,6 +29,7 @@ def FindDuplicatesFilesInFolder(path): m.update(x) f_sha = m.digest() if f_sha in shatab: + # uncomment next line to remove duplicate files # os.remove(fname) dup_count += 1 continue @@ -50,9 +48,9 @@ def FindDuplicatesFilesInFolder(path): if __name__ == '__main__': source_folder = None - rc, source_folder = gg.GetPathBox('DeDuplicate a Folder\'s image files', 'Enter path to folder you wish to find duplicates in') + rc, source_folder = gg.GetPathBox('Duplicate Finder - Count number of duplicate files', 'Enter path to folder you wish to find duplicates in') if rc is True and source_folder is not None: FindDuplicatesFilesInFolder(source_folder) else: - gg.MsgBox('Cancelling', '*** Cancelling ***') + gg.MsgBoxCancel('Cancelling', '*** Cancelling ***') exit(0) diff --git a/Demo High Level APIs.py b/Demo High Level APIs.py index 2f9c3bec..92bd53ed 100644 --- a/Demo High Level APIs.py +++ b/Demo High Level APIs.py @@ -1,5 +1,6 @@ import PySimpleGUI as sg +sg.MsgBox('Title', 'My first message... Is the length the same?') rc, number = sg.GetTextBox('Title goes here', 'Enter a number') if not rc: sg.MsgBoxError('You have cancelled') @@ -7,4 +8,4 @@ if not rc: msg = '\n'.join([f'{i}' for i in range(0,int(number))]) -sg.ScrolledTextBox(msg, Height=10) \ No newline at end of file +sg.ScrolledTextBox(msg, height=10) \ No newline at end of file diff --git a/Demo HowDoI.py b/Demo HowDoI.py index 2fb20c98..3565b1a6 100644 --- a/Demo HowDoI.py +++ b/Demo HowDoI.py @@ -15,24 +15,21 @@ def HowDoI(): :return: never returns ''' # ------- Make a new FlexForm ------- # - form = SG.FlexForm('How Do I ??', AutoSizeText=True, DefaultElementSize=(30, 2), Icon=DEFAULT_ICON) - form.AddRow(SG.Text('Ask and your answer will appear here....', Size=(40, 1))) - form.AddRow(SG.Output(Size=(90, 20))) - form.AddRow(SG.Multiline(Size=(90, 5), EnterSubmits=True), - SG.ReadFormButton('SEND', ButtonColor=(SG.YELLOWS[0], SG.BLUES[0])), - SG.SimpleButton('EXIT', ButtonColor=(SG.YELLOWS[0], SG.GREENS[0]))) + form = SG.FlexForm('How Do I ??', auto_size_text=True, default_element_size=(30, 2), icon=DEFAULT_ICON) + form.AddRow(SG.Text('Ask and your answer will appear here....', size=(40, 1))) + form.AddRow(SG.Output(size=(90, 20))) + form.AddRow(SG.Multiline(size=(90, 5), enter_submits=True), + SG.ReadFormButton('SEND', button_color=(SG.YELLOWS[0], SG.BLUES[0])), + SG.SimpleButton('EXIT', button_color=(SG.YELLOWS[0], SG.GREENS[0]))) - # ---===--- Loop taking in user input and using it to query HowDoI web oracle --- # + # ---===--- Loop taking in user input and using it to query HowDoI --- # while True: (button, value) = form.Read() if button == 'SEND': - command = value[0][:-1] - QueryHowDoI(command) + QueryHowDoI(value[0][:-1]) # send string without carriage return on end else: - print(button, 'pressed') - break + break # exit button clicked - print('Exiting the app now') exit(69) def QueryHowDoI(Query): diff --git a/Demo Recipes.py b/Demo Recipes.py index 890454c9..170eea73 100644 --- a/Demo Recipes.py +++ b/Demo Recipes.py @@ -1,12 +1,12 @@ -import PySimpleGUI_local as g +import PySimpleGUI as g def SourceDestFolders(): - with g.FlexForm('Demo Source / Destination Folders', AutoSizeText=True) as form: + with g.FlexForm('Demo Source / Destination Folders', auto_size_text=True) as form: form_rows = [[g.Text('Enter the Source and Destination folders')], [g.Text('Choose Source and Destination Folders')], - [g.Text('Source Folder', Size=(15, 1), AutoSizeText=False), g.InputText('Source'), + [g.Text('Source Folder', size=(15, 1), auto_size_text=False), g.InputText('Source'), g.FolderBrowse()], - [g.Text('Destination Folder', Size=(15, 1), AutoSizeText=False), g.InputText('Dest'), + [g.Text('Destination Folder', size=(15, 1), auto_size_text=False), g.InputText('Dest'), g.FolderBrowse()], [g.Submit(), g.Cancel()]] @@ -18,46 +18,44 @@ def SourceDestFolders(): g.MsgBoxError('Cancelled', 'User Cancelled') def Everything(): - with g.FlexForm('Everything bagel', AutoSizeText=True, DefaultElementSize=(40,1)) as form: - layout = [[g.Text('All graphic widgets in one form!', Size=(30,1), Font=("Helvetica", 25), TextColor='blue')], + with g.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40,1)) as form: + layout = [[g.Text('All graphic widgets in one form!', size=(30,1), font=("Helvetica", 25), text_color='blue')], [g.Text('Here is some text.... and a place to enter text')], [g.InputText()], - [g.Checkbox('My first checkbox!'), g.Checkbox('My second checkbox!', Default=True)], - [g.Radio('My first Radio!', "RADIO1", Default=True), g.Radio('My second Radio!', "RADIO1")], - [g.Multiline(DefaultText='This is the DEFAULT Text should you decide not to type anything', Scale=(2, 10))], - [g.InputCombo(['choice 1', 'choice 2'], Size=(20, 3))], - [g.Text('_' * 100, Size=(90, 1))], - [g.Text('Choose Source and Destination Folders', Size=(35,1))], - [g.Text('Source Folder', Size=(15, 1), AutoSizeText=False), g.InputText('Source'), g.FolderBrowse()], - [g.Text('Destination Folder', Size=(15, 1), AutoSizeText=False), g.InputText('Dest'), g.FolderBrowse()], - [g.SimpleButton('Your very own button', ButtonColor=('white', 'green'))], + [g.Checkbox('My first checkbox!'), g.Checkbox('My second checkbox!', default=True)], + [g.Radio('My first Radio!', "RADIO1", default=True), g.Radio('My second Radio!', "RADIO1")], + [g.Multiline(default_text='This is the default Text should you decide not to type anything', scale=(2,10))], + [g.InputCombo(['choice 1', 'choice 2'], size=(20,3))], + [g.Text('_' * 100, size=(70,1))], + [g.Text('Choose Source and Destination Folders', size=(35,1))], + [g.Text('Source Folder', size=(15,1), auto_size_text=False), g.InputText('Source'), g.FolderBrowse()], + [g.Text('Destination Folder', size=(15,1), auto_size_text=False), g.InputText('Dest'), g.FolderBrowse()], + [g.SimpleButton('Your very own button', button_color=('white', 'green'))], [g.Submit(), g.Cancel()]] (button, (values)) = form.LayoutAndShow(layout) - g.MsgBox('Title', 'Typical message box', 'The results of the form are a lot of data! Get ready... ', 'The button clicked was "{}"'.format(button), 'The values are', values, AutoClose=True) + g.MsgBox('Title', 'Typical message box', 'The results of the form are a lot of data! Get ready... ', 'The button clicked was "{}"'.format(button), 'The values are', values, auto_close=True) # example of an Asynchronous form def ChatBot(): - with g.FlexForm('Chat Window', AutoSizeText=True, DefaultElementSize=(30, 2)) as form: - form.AddRow(g.Text('This is where standard out is being routed', Size=[40,1])) - form.AddRow(g.Output(Size=(80, 20))) - form.AddRow(g.Multiline(Size=(70, 5), EnterSubmits=True), g.ReadFormButton('SEND', ButtonColor=(g.YELLOWS[0], g.BLUES[0])), g.SimpleButton('EXIT', ButtonColor=(g.YELLOWS[0], g.GREENS[0]))) + with g.FlexForm('Chat Window', auto_size_text=True, default_element_size=(30, 2)) as form: + form.AddRow(g.Text('This is where standard out is being routed', size=[40,1])) + form.AddRow(g.Output(size=(80, 20))) + form.AddRow(g.Multiline(size=(70, 5), enter_submits=True), g.ReadFormButton('SEND', button_color=(g.YELLOWS[0], g.BLUES[0])), g.SimpleButton('EXIT', button_color=(g.YELLOWS[0], g.GREENS[0]))) # ---===--- Loop taking in user input and using it to query HowDoI web oracle --- # while True: (button, value) = form.Read() if button == 'SEND': - print(value) + print(value, end="") else: - print('Exiting the form now') break - print('Exiting the chatbot....') def main(): - # SourceDestFolders() + SourceDestFolders() Everything() - # ChatBot() + ChatBot() if __name__ == '__main__': main() diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 1e45abc4..aaf030a0 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -120,13 +120,13 @@ MSG_BOX_OK = 0 # Element CLASS # # ------------------------------------------------------------------------- # class Element(): - def __init__(self, Type, Scale=(None, None), Size=(None, None), AutoSizeText=None, Font=None): - self.Size = Size - self.Type = Type - self.AutoSizeText = AutoSizeText - self.Scale = Scale + def __init__(self, type, scale=(None, None), size=(None, None), auto_size_text=None, font=None): + self.Size = size + self.Type = type + self.AutoSizeText = auto_size_text + self.Scale = scale self.Pad = DEFAULT_ELEMENT_PADDING - self.Font = Font + self.Font = font self.TKStringVar = None self.TKIntVar = None @@ -161,9 +161,9 @@ class Element(): # ---------------------------------------------------------------------- # class InputText(Element): - def __init__(self, DefaultText = '', Scale=(None, None), Size=(None, None), AutoSizeText=None): - self.DefaultText = DefaultText - super().__init__(INPUT_TEXT, Scale, Size, AutoSizeText) + def __init__(self, default_text ='', scale=(None, None), size=(None, None), auto_size_text=None): + self.DefaultText = default_text + super().__init__(INPUT_TEXT, scale, size, auto_size_text) return def ReturnKeyHandler(self, event): @@ -183,10 +183,10 @@ class InputText(Element): # ---------------------------------------------------------------------- # class InputCombo(Element): - def __init__(self, Values, Scale=(None, None), Size=(None, None), AutoSizeText=None): - self.Values = Values + def __init__(self, values, scale=(None, None), size=(None, None), auto_size_text=None): + self.Values = values self.TKComboBox = None - super().__init__(INPUT_COMBO, Scale, Size, AutoSizeText) + super().__init__(INPUT_COMBO, scale, size, auto_size_text) return def __del__(self): @@ -200,13 +200,13 @@ class InputCombo(Element): # Radio # # ---------------------------------------------------------------------- # class Radio(Element): - def __init__(self, Text, GroupID, Default=False, Scale=(None, None), Size=(None, None), AutoSizeText=None,Font=None): - self.InitialState = Default - self.Text = Text + def __init__(self, text, group_id, default=False, scale=(None, None), size=(None, None), auto_size_text=None, font=None): + self.InitialState = default + self.Text = text self.TKRadio = None - self.GroupID = GroupID + self.GroupID = group_id self.Value = None - super().__init__(INPUT_RADIO, Scale, Size, AutoSizeText, Font) + super().__init__(INPUT_RADIO, scale, size, auto_size_text, font) return def __del__(self): @@ -220,13 +220,13 @@ class Radio(Element): # Checkbox # # ---------------------------------------------------------------------- # class Checkbox(Element): - def __init__(self, Text, Default=False, Scale=(None, None), Size=(None, None), AutoSizeText=None, Font=None): - self.Text = Text - self.InitialState = Default + def __init__(self, text, default=False, scale=(None, None), size=(None, None), auto_size_text=None, font=None): + self.Text = text + self.InitialState = default self.Value = None self.TKCheckbox = None - super().__init__(INPUT_CHECKBOX, Scale, Size, AutoSizeText, Font) + super().__init__(INPUT_CHECKBOX, scale, size, auto_size_text, font) return def __del__(self): @@ -243,11 +243,11 @@ class Checkbox(Element): class Spin(Element): # Values = None # TKSpinBox = None - def __init__(self, Values, InitialValue=None, Scale=(None, None), Size=(None, None), AutoSizeText=None, Font=None): - self.Values = Values - self.DefaultValue = InitialValue + def __init__(self, values, initial_value=None, scale=(None, None), size=(None, None), auto_size_text=None, font=None): + self.Values = values + self.DefaultValue = initial_value self.TKSpinBox = None - super().__init__(INPUT_SPIN, Scale, Size, AutoSizeText, Font=Font) + super().__init__(INPUT_SPIN, scale, size, auto_size_text, font=font) return def __del__(self): @@ -261,10 +261,10 @@ class Spin(Element): # Multiline # # ---------------------------------------------------------------------- # class Multiline(Element): - def __init__(self, DefaultText='', EnterSubmits = False, Scale=(None, None), Size=(None, None), AutoSizeText=None): - self.DefaultText = DefaultText - self.EnterSubmits = EnterSubmits - super().__init__(INPUT_MULTILINE, Scale, Size, AutoSizeText) + def __init__(self, default_text='', enter_submits = False, scale=(None, None), size=(None, None), auto_size_text=None): + self.DefaultText = default_text + self.EnterSubmits = enter_submits + super().__init__(INPUT_MULTILINE, scale, size, auto_size_text) return def ReturnKeyHandler(self, event): @@ -284,12 +284,12 @@ class Multiline(Element): # Text # # ---------------------------------------------------------------------- # class Text(Element): - def __init__(self, Text, Scale=(None, None), Size=(None, None), AutoSizeText=None, Font=None, TextColor=None): - self.DisplayText = Text - self.TextColor = TextColor if TextColor else 'black' + def __init__(self, text, scale=(None, None), size=(None, None), auto_size_text=None, font=None, text_color=None): + self.DisplayText = text + self.TextColor = text_color if text_color else 'black' # self.Font = Font if Font else DEFAULT_FONT # i=1/0 - super().__init__(TEXT, Scale, Size, AutoSizeText, Font=Font if Font else DEFAULT_FONT) + super().__init__(TEXT, scale, size, auto_size_text, font=font if font else DEFAULT_FONT) return def Update(self, NewValue): @@ -307,41 +307,41 @@ class Text(Element): # ---------------------------------------------------------------------- # class TKProgressBar(): - def __init__(self, root, Max, Length=400, Width=20, Highlightt=0, Relief='sunken', Borderwidth=4, Orientation='horizontal', BarColor=DEFAULT_PROGRESS_BAR_COLOR): - self.Length = Length - self.Width = Width - self.Max = Max - self.Orientation = Orientation + def __init__(self, root, max, length=400, width=20, highlightt=0, relief='sunken', border_width=4, orientation='horizontal', BarColor=DEFAULT_PROGRESS_BAR_COLOR): + self.Length = length + self.Width = width + self.Max = max + self.Orientation = orientation self.Count = None self.PriorCount = 0 - if Orientation[0].lower() == 'h': - self.TKCanvas = tk.Canvas(root, width=Length, height=Width, highlightt=Highlightt, relief=Relief, borderwidth=Borderwidth) - self.TKRect = self.TKCanvas.create_rectangle(0, 0, -(Length * 1.5), Width * 1.5, fill=BarColor[0], tags='bar') + if orientation[0].lower() == 'h': + self.TKCanvas = tk.Canvas(root, width=length, height=width, highlightt=highlightt, relief=relief, borderwidth=border_width) + self.TKRect = self.TKCanvas.create_rectangle(0, 0, -(length * 1.5), width * 1.5, fill=BarColor[0], tags='bar') # self.canvas.pack(padx='10') else: - self.TKCanvas = tk.Canvas(root, width=Width, height=Length, highlightt=Highlightt, relief=Relief, borderwidth=Borderwidth) - self.TKRect = self.TKCanvas.create_rectangle(Width * 1.5, 2 * Length + 40, 0, Length * .5, fill=BarColor[0], tags='bar') + self.TKCanvas = tk.Canvas(root, width=width, height=length, highlightt=highlightt, relief=relief, borderwidth=border_width) + self.TKRect = self.TKCanvas.create_rectangle(width * 1.5, 2 * length + 40, 0, length * .5, fill=BarColor[0], tags='bar') # self.canvas.pack() - def Update(self,Count): - if Count > self.Max: return + def Update(self, count): + if count > self.Max: return if self.Orientation[0].lower() == 'h': try: - if Count != self.PriorCount: - delta = Count - self.PriorCount + if count != self.PriorCount: + delta = count - self.PriorCount self.TKCanvas.move(self.TKRect, delta*(self.Length / self.Max), 0) if 0: self.TKCanvas.update() except: return False # the window was closed by the user on us else: try: - if Count != self.PriorCount: - delta = Count - self.PriorCount + if count != self.PriorCount: + delta = count - self.PriorCount self.TKCanvas.move(self.TKRect, 0, delta*(-self.Length / self.Max)) if 0: self.TKCanvas.update() except: return False # the window was closed by the user on us - self.PriorCount = Count + self.PriorCount = count return True def __del__(self): @@ -395,9 +395,9 @@ in the text pane.''' sys.stdout = self.previous_stdout class Output(Element): - def __init__(self, Scale=(None, None), Size=(None, None)): + def __init__(self, scale=(None, None), size=(None, None)): self.TKOut = None - super().__init__(OUTPUT, Scale, Size) + super().__init__(OUTPUT, scale, size) def __del__(self): try: @@ -410,15 +410,15 @@ class Output(Element): # Button Class # # ---------------------------------------------------------------------- # class Button(Element): - def __init__(self, ButtonType=CLOSES_WIN, Target=(None, None), ButtonText='', FileTypes=(("ALL Files", "*.*"),), Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None, Font=None): - self.BType = ButtonType - self.FileTypes = FileTypes + def __init__(self, button_type=CLOSES_WIN, target=(None, None), button_text='', file_types=(("ALL Files", "*.*"),), scale=(None, None), size=(None, None), auto_size_text=None, button_color=None, font=None): + self.BType = button_type + self.FileTypes = file_types self.TKButton = None - self.Target = Target - self.ButtonText = ButtonText - self.ButtonColor = ButtonColor if ButtonColor else DEFAULT_BUTTON_COLOR + self.Target = target + self.ButtonText = button_text + self.ButtonColor = button_color if button_color else DEFAULT_BUTTON_COLOR self.UserData = None - super().__init__(BUTTON, Scale, Size, AutoSizeText, Font=Font) + super().__init__(BUTTON, scale, size, auto_size_text, font=font) return # ------- Button Callback ------- # @@ -493,22 +493,22 @@ class Button(Element): # ProgreessBar # # ---------------------------------------------------------------------- # class ProgressBar(Element): - def __init__(self, MaxValue, Orientation=None, Target=(None,None), Scale=(None, None), Size=(None, None), AutoSizeText=None, BarColor=(None,None), Style=None, BorderWidth=None, Relief=None): - self.MaxValue = MaxValue + def __init__(self, max_value, orientation=None, target=(None, None), scale=(None, None), size=(None, None), auto_size_text=None, bar_color=(None, None), style=None, broder_width=None, relief=None): + self.MaxValue = max_value self.TKProgressBar = None self.Cancelled = False self.NotRunning = True - self.Orientation = Orientation if Orientation else DEFAULT_METER_ORIENTATION - self.BarColor = BarColor - self.BarStyle = Style if Style else DEFAULT_PROGRESS_BAR_STYLE - self.Target = Target - self.BorderWidth = BorderWidth if BorderWidth else DEFAULT_PROGRESS_BAR_BORDER_WIDTH - self.Relief = Relief if Relief else DEFAULT_PROGRESS_BAR_RELIEF + self.Orientation = orientation if orientation else DEFAULT_METER_ORIENTATION + self.BarColor = bar_color + self.BarStyle = style if style else DEFAULT_PROGRESS_BAR_STYLE + self.Target = target + self.BorderWidth = broder_width if broder_width else DEFAULT_PROGRESS_BAR_BORDER_WIDTH + self.Relief = relief if relief else DEFAULT_PROGRESS_BAR_RELIEF self.BarExpired = False - super().__init__(PROGRESS_BAR, Scale, Size, AutoSizeText) + super().__init__(PROGRESS_BAR, scale, size, auto_size_text) return - def UpdateBar(self, CurrentCount): + def UpdateBar(self, current_count): if self.ParentForm.TKrootDestroyed: return False target = self.Target @@ -519,7 +519,7 @@ class ProgressBar(Element): # update the progress bar counter # self.TKProgressBar['value'] = self.CurrentValue - self.TKProgressBar.Update(CurrentCount) + self.TKProgressBar.Update(current_count) try: self.ParentForm.TKroot.update() except: @@ -538,8 +538,8 @@ class ProgressBar(Element): # Row CLASS # # ------------------------------------------------------------------------- # class Row(): - def __init__(self, AutoSizeText = None): - self.AutoSizeText = AutoSizeText # Setting to override the form's policy on autosizing. + def __init__(self, auto_size_text = None): + self.AutoSizeText = auto_size_text # Setting to override the form's policy on autosizing. self.Elements = [] # List of Elements in this Rrow return @@ -563,44 +563,44 @@ class FlexForm: ''' Display a user defined for and return the filled in data ''' - def __init__(self, title, DefaultElementSize=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), AutoSizeText=DEFAULT_AUTOSIZE_TEXT, Scale=(None, None),Size=(None, None), Location=(None, None), ButtonColor=None, Font=None, ProgressBarColor=(None,None), IsTabbedForm=False,BorderDepth=None, AutoClose=False, AutoCloseDuration=DEFAULT_AUTOCLOSE_TIME, Icon=DEFAULT_WINDOW_ICON): - self.AutoSizeText = AutoSizeText + def __init__(self, title, default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), auto_size_text=DEFAULT_AUTOSIZE_TEXT, scale=(None, None), size=(None, None), location=(None, None), button_color=None, font=None, progress_bar_color=(None, None), is_tabbed_form=False, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=DEFAULT_WINDOW_ICON): + self.AutoSizeText = auto_size_text self.Title = title self.Rows = [] # a list of ELEMENTS for this row - self.DefaultElementSize = DefaultElementSize - self.Size = Size - self.Scale = Scale - self.Location = Location - self.ButtonColor = ButtonColor if ButtonColor else DEFAULT_BUTTON_COLOR - self.IsTabbedForm = IsTabbedForm + self.DefaultElementSize = default_element_size + self.Size = size + self.Scale = scale + self.Location = location + self.ButtonColor = button_color if button_color else DEFAULT_BUTTON_COLOR + self.IsTabbedForm = is_tabbed_form self.ParentWindow = None - self.Font = Font if Font else DEFAULT_FONT + self.Font = font if font else DEFAULT_FONT self.RadioDict = {} - self.BorderDepth = BorderDepth - self.WindowIcon = Icon if not _my_windows.user_defined_icon else _my_windows.user_defined_icon - self.AutoClose = AutoClose + self.BorderDepth = border_depth + self.WindowIcon = icon if not _my_windows.user_defined_icon else _my_windows.user_defined_icon + self.AutoClose = auto_close self.NonBlocking = False self.TKroot = None self.TKrootDestroyed = False self.TKAfterID = None - self.ProgressBarColor = ProgressBarColor - self.AutoCloseDuration = AutoCloseDuration + self.ProgressBarColor = progress_bar_color + self.AutoCloseDuration = auto_close_duration self.UberParent = None self.RootNeedsDestroying = False self.Shown = False self.ReturnValues = None # ------------------------- Add ONE Row to Form ------------------------- # - def AddRow(self, *args,AutoSizeText=None): + def AddRow(self, *args, auto_size_text=None): ''' Parms are a variable number of Elements ''' NumRows = len(self.Rows) # number of existing rows is our row number CurrentRowNumber = NumRows # this row's number - CurrentRow = Row(AutoSizeText) # start with a blank row and build up + CurrentRow = Row(auto_size_text) # start with a blank row and build up # ------------------------- Add the elements to a row ------------------------- # for i, element in enumerate(args): # Loop through list of elements and add them to the row element.Position = (CurrentRowNumber, i) CurrentRow.Elements.append(element) - CurrentRow.AutoSizeText = AutoSizeText + CurrentRow.AutoSizeText = auto_size_text # ------------------------- Append the row to list of Rows ------------------------- # self.Rows.append(CurrentRow) @@ -615,26 +615,26 @@ class FlexForm: return self.ReturnValues # ------------------------- ShowForm THIS IS IT! ------------------------- # - def Show(self, NonBlocking=False): + def Show(self, non_blocking=False): self.Shown = True # Compute num rows & num cols (it'll come in handy debugging) self.NumRows = len(self.Rows) self.NumCols = max(len(row.Elements) for row in self.Rows) - self.NonBlocking=NonBlocking + self.NonBlocking=non_blocking # -=-=-=-=-=-=-=-=- RUN the GUI -=-=-=-=-=-=-=-=- ## StartupTK(self) return self.ReturnValues # ------------------------- SetIcon - set the window's fav icon ------------------------- # - def SetIcon(self, Icon): - self.WindowIcon = Icon + def SetIcon(self, icon): + self.WindowIcon = icon try: - self.TKroot.iconbitmap(Icon) + self.TKroot.iconbitmap(icon) except: pass - def GetElementAtLocation(self,Location): - (row_num,col_num) = Location + def GetElementAtLocation(self, location): + (row_num,col_num) = location row = self.Rows[row_num] element = row.Elements[col_num] return element @@ -720,8 +720,8 @@ class UberForm(): self.TKroot = None self.TKrootDestroyed = False - def AddForm(self, Form): - self.FormList.append(Form) + def AddForm(self, form): + self.FormList.append(form) def Close(self): self.FormReturnValues = [] @@ -740,60 +740,60 @@ class UberForm(): # ====================================================================== # # ------------------------- INPUT TEXT Element lazy functions ------------------------- # -def In(DefaultText = '', Scale=(None, None), Size=(None, None), AutoSizeText=None): - return InputText(DefaultText=DefaultText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText) +def In(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None): + return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text) -def Input(DefaultText = '', Scale=(None, None), Size=(None, None), AutoSizeText=None): - return InputText(DefaultText=DefaultText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText) +def Input(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None): + return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text) # ------------------------- TEXT Element lazy functions ------------------------- # -def Txt(DisplayText, Scale=(None, None), Size=(None, None), AutoSizeText=None, Font=None, TextColor=None): - return Text(DisplayText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, Font=Font, TextColor=TextColor) +def Txt(display_text, scale=(None, None), size=(None, None), auto_size_text=None, font=None, text_color=None): + return Text(display_text, scale=scale, size=size, auto_size_text=auto_size_text, font=font, text_color=text_color) -def T(DisplayText, Scale=(None, None), Size=(None, None), AutoSizeText=None, Font=None, TextColor=None): - return Text(DisplayText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, Font=Font, TextColor=TextColor) +def T(display_text, scale=(None, None), size=(None, None), auto_size_text=None, font=None, text_color=None): + return Text(display_text, scale=scale, size=size, auto_size_text=auto_size_text, font=font, text_color=text_color) # ------------------------- FOLDER BROWSE Element lazy function ------------------------- # -def FolderBrowse(Target=(ThisRow, -1), ButtonText='Browse', Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None): - return Button(BROWSE_FOLDER, Target=Target, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor) +def FolderBrowse(target=(ThisRow, -1), button_text='Browse', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None): + return Button(BROWSE_FOLDER, target=target, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color) # ------------------------- FILE BROWSE Element lazy function ------------------------- # -def FileBrowse(Target=(ThisRow, -1), FileTypes=(("ALL Files", "*.*"),),ButtonText='Browse',Scale=(None, None),Size=(None, None), AutoSizeText=None, ButtonColor=None): - return Button(BROWSE_FILE, Target, ButtonText=ButtonText, FileTypes=FileTypes, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor) +def FileBrowse(target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), button_text='Browse', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None): + return Button(BROWSE_FILE, target, button_text=button_text, file_types=file_types, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color) # ------------------------- SUBMIT BUTTON Element lazy function ------------------------- # -def Submit(ButtonText='Submit', Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None): - return Button(CLOSES_WIN, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor) +def Submit(button_text='Submit', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None): + return Button(CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color) # ------------------------- OK BUTTON Element lazy function ------------------------- # -def OK(ButtonText='OK', Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None): - return Button(CLOSES_WIN, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor) +def OK(button_text='OK', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None): + return Button(CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color) # ------------------------- YES BUTTON Element lazy function ------------------------- # -def Ok(ButtonText='Ok', Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None): - return Button(CLOSES_WIN, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor) +def Ok(button_text='Ok', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None): + return Button(CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color) # ------------------------- CANCEL BUTTON Element lazy function ------------------------- # -def Cancel(ButtonText='Cancel', Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None, Font=None): - return Button(CLOSES_WIN, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor, Font=Font) +def Cancel(button_text='Cancel', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None, font=None): + return Button(CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color, font=font) # ------------------------- YES BUTTON Element lazy function ------------------------- # -def Yes(ButtonText='Yes', Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None): - return Button(CLOSES_WIN, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor) +def Yes(button_text='Yes', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None): + return Button(CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color) # ------------------------- NO BUTTON Element lazy function ------------------------- # -def No(ButtonText='No', Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None): - return Button(CLOSES_WIN, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor) +def No(button_text='No', scale=(None, None), size=(None, None), auto_size_text=None, button_color=None): + return Button(CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color) # ------------------------- GENERIC BUTTON Element lazy function ------------------------- # # this is the only button that REQUIRES button text field -def SimpleButton(ButtonText, Scale=(None, None), Size=(None, None), AutoSizeText=None, ButtonColor=None, Font=None): - return Button(CLOSES_WIN, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor, Font=Font) +def SimpleButton(button_text, scale=(None, None), size=(None, None), auto_size_text=None, button_color=None, font=None): + return Button(CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color, font=font) # ------------------------- GENERIC BUTTON Element lazy function ------------------------- # # this is the only button that REQUIRES button text field -def ReadFormButton(ButtonText, Scale=(None, None),Size=(None, None), AutoSizeText=None, ButtonColor=None, Font=None): - return Button(READ_FORM, ButtonText=ButtonText, Scale=Scale, Size=Size, AutoSizeText=AutoSizeText, ButtonColor=ButtonColor, Font=Font) +def ReadFormButton(button_text, scale=(None, None), size=(None, None), auto_size_text=None, button_color=None, Font=None): + return Button(READ_FORM, button_text=button_text, scale=scale, size=size, auto_size_text=auto_size_text, button_color=button_color, font=Font) #------------------------------------------------------------------------------------------------------# # ------- FUNCTION InitializeResults. Sets up form results matrix ------- # @@ -843,8 +843,8 @@ def DecodeRadioRowCol(RadValue): col = RadValue%1000 return row,col -def EncodeRadioRowCol(Row, Col): - RadValue = Row*1000 + Col +def EncodeRadioRowCol(row, col): + RadValue = row * 1000 + col return RadValue # ------- FUNCTION BuildResults. Form exiting so build the results to pass back ------- # @@ -1066,7 +1066,7 @@ def ConvertFlexToTK(MyFlexForm): bar_color = element.BarColor else: bar_color = DEFAULT_PROGRESS_BAR_COLOR - element.TKProgressBar = TKProgressBar(tk_row_frame, element.MaxValue, progress_length, progress_width, Orientation=direction, BarColor=bar_color, Borderwidth=element.BorderWidth, Relief=element.Relief) + element.TKProgressBar = TKProgressBar(tk_row_frame, element.MaxValue, progress_length, progress_width, orientation=direction, BarColor=bar_color, border_width=element.BorderWidth, relief=element.Relief) s = ttk.Style() element.TKProgressBar.TKCanvas.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) # ------------------------- INPUT RADIO BUTTON element ------------------------- # @@ -1131,14 +1131,14 @@ def ConvertFlexToTK(MyFlexForm): return # ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----# -def ShowTabbedForm(Title, *args,AutoClose=False, AutoCloseDuration=DEFAULT_AUTOCLOSE_TIME,FavIcon=DEFAULT_WINDOW_ICON): +def ShowTabbedForm(title, *args, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, fav_icon=DEFAULT_WINDOW_ICON): global _my_windows uber = UberForm() root = tk.Tk() uber.TKroot = root - if Title is not None: - root.title(Title) + if title is not None: + root.title(title) if not len(args): ('******************* SHOW TABBED FORMS ERROR .... no arguments') return @@ -1160,8 +1160,8 @@ def ShowTabbedForm(Title, *args,AutoClose=False, AutoCloseDuration=DEFAULT_AUTOC uber.FormReturnValues.append(form.ReturnValues) # dangerous?? or clever? use the final form as a callback for autoclose - id = root.after(AutoCloseDuration*1000, form.AutoCloseAlarmCallback) if AutoClose else 0 - icon = FavIcon if not _my_windows.user_defined_icon else _my_windows.user_defined_icon + id = root.after(auto_close_duration * 1000, form.AutoCloseAlarmCallback) if auto_close else 0 + icon = fav_icon if not _my_windows.user_defined_icon else _my_windows.user_defined_icon try: uber.TKroot.iconbitmap(icon) except: pass @@ -1172,31 +1172,31 @@ def ShowTabbedForm(Title, *args,AutoClose=False, AutoCloseDuration=DEFAULT_AUTOC return uber.FormReturnValues # ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----# -def StartupTK(MyFlexForm): +def StartupTK(my_flex_form): global _my_windows ow = _my_windows.NumOpenWindows root = tk.Tk() if not ow else tk.Toplevel() _my_windows.NumOpenWindows += 1 - MyFlexForm.TKroot = root + my_flex_form.TKroot = root # root.protocol("WM_DELETE_WINDOW", MyFlexForm.DestroyedCallback()) # root.bind('', MyFlexForm.DestroyedCallback()) - ConvertFlexToTK(MyFlexForm) - MyFlexForm.SetIcon(MyFlexForm.WindowIcon) + ConvertFlexToTK(my_flex_form) + my_flex_form.SetIcon(my_flex_form.WindowIcon) - if MyFlexForm.AutoClose: - duration = DEFAULT_AUTOCLOSE_TIME if MyFlexForm.AutoCloseDuration is None else MyFlexForm.AutoCloseDuration - MyFlexForm.TKAfterID = root.after(duration*1000, MyFlexForm.AutoCloseAlarmCallback) - if MyFlexForm.NonBlocking: - MyFlexForm.TKroot.protocol("WM_WINDOW_DESTROYED", MyFlexForm.OnClosingCallback()) + if my_flex_form.AutoClose: + duration = DEFAULT_AUTOCLOSE_TIME if my_flex_form.AutoCloseDuration is None else my_flex_form.AutoCloseDuration + my_flex_form.TKAfterID = root.after(duration * 1000, my_flex_form.AutoCloseAlarmCallback) + if my_flex_form.NonBlocking: + my_flex_form.TKroot.protocol("WM_WINDOW_DESTROYED", my_flex_form.OnClosingCallback()) pass else: # it's a blocking form - MyFlexForm.TKroot.mainloop() + my_flex_form.TKroot.mainloop() _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 - if MyFlexForm.RootNeedsDestroying: - MyFlexForm.TKroot.destroy() - MyFlexForm.RootNeedsDestroying = False + if my_flex_form.RootNeedsDestroying: + my_flex_form.TKroot.destroy() + my_flex_form.RootNeedsDestroying = False return @@ -1225,24 +1225,24 @@ def _GetNumLinesNeeded(text, max_line_width): # Exits via an OK button2 press # # Returns nothing # # ===================================================# -def MsgBox(*args, ButtonColor=None, ButtonType=MSG_BOX_OK, AutoClose=False, AutoCloseDuration=None, Icon=DEFAULT_WINDOW_ICON, LineWidth=MESSAGE_BOX_LINE_WIDTH, Font=None): +def MsgBox(*args, button_color=None, button_type=MSG_BOX_OK, auto_close=False, auto_close_duration=None, icon=DEFAULT_WINDOW_ICON, line_width=MESSAGE_BOX_LINE_WIDTH, font=None): ''' Show message box. Displays one line per user supplied argument. Takes any Type of variable to display. :param args: - :param ButtonColor: - :param ButtonType: - :param AutoClose: - :param AutoCloseDuration: - :param Icon: - :param LineWidth: - :param Font: + :param button_color: + :param button_type: + :param auto_close: + :param auto_close_duration: + :param icon: + :param line_width: + :param font: :return: ''' if not args: args_to_print = [''] else: args_to_print = args - with FlexForm(args_to_print[0], AutoSizeText=True, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration, Icon=Icon, Font=Font) as form: + with FlexForm(args_to_print[0], auto_size_text=True, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, icon=icon, font=font) as form: max_line_total, total_lines = 0,0 for message in args_to_print: # fancy code to check if string and convert if not is not need. Just always convert to string :-) @@ -1251,32 +1251,33 @@ def MsgBox(*args, ButtonColor=None, ButtonType=MSG_BOX_OK, AutoClose=False, Aut if message.count('\n'): message_wrapped = message else: - message_wrapped = textwrap.fill(message, LineWidth) + message_wrapped = textwrap.fill(message, line_width) message_wrapped_lines = message_wrapped.count('\n')+1 longest_line_len = max([len(l) for l in message.split('\n')]) - width_used = min(longest_line_len, LineWidth) + width_used = min(longest_line_len, line_width) max_line_total = max(max_line_total, width_used) # height = _GetNumLinesNeeded(message, width_used) height = message_wrapped_lines - form.AddRow(Text(message_wrapped, AutoSizeText=True)) + form.AddRow(Text(message_wrapped, auto_size_text=True)) total_lines += height pad = max_line_total-15 if max_line_total > 15 else 1 pad =1 # show either an OK or Yes/No depending on paramater - if ButtonType is MSG_BOX_YES_NO: - form.AddRow(Text('', Size=(pad,1), AutoSizeText=False), Yes(ButtonColor=ButtonColor), No(ButtonColor=ButtonColor)) + if button_type is MSG_BOX_YES_NO: + form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Yes(button_color=button_color), No( + button_color=button_color)) (button_text, values) = form.Show() return button_text == 'Yes' - elif ButtonType is MSG_BOX_CANCELLED: - form.AddRow(Text('', Size=(pad,1), AutoSizeText=False), SimpleButton('Cancelled', ButtonColor=ButtonColor)) - elif ButtonType is MSG_BOX_ERROR: - form.AddRow(Text('', Size=(pad,1), AutoSizeText=False), SimpleButton('ERROR', Size=(5,1), ButtonColor=ButtonColor)) - elif ButtonType is MSG_BOX_OK_CANCEL: - form.AddRow(Text('', Size=(pad,1), AutoSizeText=False), SimpleButton('OK', Size=(5,1), ButtonColor=ButtonColor), - SimpleButton('Cancel', Size=(5, 1), ButtonColor=ButtonColor)) + elif button_type is MSG_BOX_CANCELLED: + form.AddRow(Text('', size=(pad, 1), auto_size_text=False), SimpleButton('Cancelled', button_color=button_color)) + elif button_type is MSG_BOX_ERROR: + form.AddRow(Text('', size=(pad, 1), auto_size_text=False), SimpleButton('ERROR', size=(5, 1), button_color=button_color)) + elif button_type is MSG_BOX_OK_CANCEL: + form.AddRow(Text('', size=(pad, 1), auto_size_text=False), SimpleButton('OK', size=(5, 1), button_color=button_color), + SimpleButton('Cancel', size=(5, 1), button_color=button_color)) else: - form.AddRow(Text('', Size=(pad,1), AutoSizeText=False), SimpleButton('OK', Size=(5,1), ButtonColor=ButtonColor)) + form.AddRow(Text('', size=(pad, 1), auto_size_text=False), SimpleButton('OK', size=(5, 1), button_color=button_color)) button, values = form.Show() return button @@ -1284,99 +1285,99 @@ def MsgBox(*args, ButtonColor=None, ButtonType=MSG_BOX_OK, AutoClose=False, Aut # ============================== MsgBoxAutoClose====# # Lazy function. Same as calling MsgBox with parms # # ===================================================# -def MsgBoxAutoClose(*args, ButtonColor=None,AutoClose=True, AutoCloseDuration=DEFAULT_AUTOCLOSE_TIME, Font=None): +def MsgBoxAutoClose(*args, button_color=None, auto_close=True, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, font=None): ''' Display a standard MsgBox that will automatically close after a specified amount of time :param args: - :param ButtonColor: - :param AutoClose: - :param AutoCloseDuration: - :param Font: + :param button_color: + :param auto_close: + :param auto_close_duration: + :param font: :return: ''' - MsgBox(*args, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration, Font=Font) + MsgBox(*args, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font) return # ============================== MsgBoxError =====# # Like MsgBox but presents RED BUTTONS # # ===================================================# -def MsgBoxError(*args, ButtonColor=DEFAULT_ERROR_BUTTON_COLOR,AutoClose=False, AutoCloseDuration=None, Font=None): +def MsgBoxError(*args, button_color=DEFAULT_ERROR_BUTTON_COLOR, auto_close=False, auto_close_duration=None, Font=None): ''' Display a MsgBox with a red button :param args: - :param ButtonColor: - :param AutoClose: - :param AutoCloseDuration: + :param button_color: + :param auto_close: + :param auto_close_duration: :param Font: :return: ''' - MsgBox(*args, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration, Font=Font) + MsgBox(*args, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=Font) return # ============================== MsgBoxCancel =====# # # # ===================================================# -def MsgBoxCancel(*args,ButtonColor=DEFAULT_CANCEL_BUTTON_COLOR,AutoClose=False, AutoCloseDuration=None, Font=None): +def MsgBoxCancel(*args, button_color=DEFAULT_CANCEL_BUTTON_COLOR, auto_close=False, auto_close_duration=None, font=None): ''' Display a MsgBox with a single "Cancel" button. :param args: - :param ButtonColor: - :param AutoClose: - :param AutoCloseDuration: - :param Font: + :param button_color: + :param auto_close: + :param auto_close_duration: + :param font: :return: ''' - MsgBox(*args, ButtonType=MSG_BOX_CANCELLED, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration, Font=Font) + MsgBox(*args, button_type=MSG_BOX_CANCELLED, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font) return # ============================== MsgBoxOK =====# # Like MsgBox but only 1 button # # ===================================================# -def MsgBoxOK(*args,ButtonColor=('white', 'black'),AutoClose=False, AutoCloseDuration=None, Font=None): +def MsgBoxOK(*args, button_color=('white', 'black'), auto_close=False, auto_close_duration=None, font=None): ''' Display a MsgBox with a single buttoned labelled "OK" :param args: - :param ButtonColor: - :param AutoClose: - :param AutoCloseDuration: - :param Font: + :param button_color: + :param auto_close: + :param auto_close_duration: + :param font: :return: ''' - MsgBox(*args, ButtonType=MSG_BOX_OK, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration, Font=Font) + MsgBox(*args, button_type=MSG_BOX_OK, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font) return # ============================== MsgBoxOKCancel ====# # Like MsgBox but presents OK and Cancel buttons # # ===================================================# -def MsgBoxOKCancel(*args,ButtonColor=None,AutoClose=False, AutoCloseDuration=None, Font=None): +def MsgBoxOKCancel(*args, button_color=None, auto_close=False, auto_close_duration=None, font=None): ''' Display MsgBox with 2 buttons, "OK" and "Cancel" :param args: - :param ButtonColor: - :param AutoClose: - :param AutoCloseDuration: - :param Font: + :param button_color: + :param auto_close: + :param auto_close_duration: + :param font: :return: ''' - result = MsgBox(*args, ButtonType=MSG_BOX_OK_CANCEL, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration, Font=Font) + result = MsgBox(*args, button_type=MSG_BOX_OK_CANCEL, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font) return result # ==================================== YesNoBox=====# # Like MsgBox but presents Yes and No buttons # # Returns True if Yes was pressed else False # # ===================================================# -def MsgBoxYesNo(*args,ButtonColor=None,AutoClose=False, AutoCloseDuration=None, Font=None): +def MsgBoxYesNo(*args, button_color=None, auto_close=False, auto_close_duration=None, font=None): ''' Display MsgBox with 2 buttons, "Yes" and "No" :param args: - :param ButtonColor: - :param AutoClose: - :param AutoCloseDuration: - :param Font: + :param button_color: + :param auto_close: + :param auto_close_duration: + :param font: :return: ''' - result = MsgBox(*args,ButtonType=MSG_BOX_YES_NO, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration, Font=Font) + result = MsgBox(*args, button_type=MSG_BOX_YES_NO, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font) return result # ============================== PROGRESS METER ========================================== # @@ -1400,52 +1401,52 @@ def ConvertArgsToSingleString(*args): # ============================== ProgressMeter =====# # ===================================================# -def ProgressMeter(Title, MaxValue, *args, Orientation=None, BarColor=DEFAULT_PROGRESS_BAR_COLOR, ButtonColor=None,Size=DEFAULT_PROGRESS_BAR_SIZE, Scale=(None, None), BorderWidth=DEFAULT_PROGRESS_BAR_BORDER_WIDTH): +def ProgressMeter(title, max_value, *args, Orientation=None, bar_color=DEFAULT_PROGRESS_BAR_COLOR, button_color=None, size=DEFAULT_PROGRESS_BAR_SIZE, scale=(None, None), border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH): ''' Create and show a form on tbe caller's behalf. - :param Title: - :param MaxValue: + :param title: + :param max_value: :param args: ANY number of arguments the caller wants to display :param Orientation: - :param BarColor: - :param Size: - :param Scale: + :param bar_color: + :param size: + :param scale: :param Style: :param StyleOffset: :return: ProgressBar object that is in the form ''' orientation = DEFAULT_METER_ORIENTATION if Orientation is None else Orientation target = (0,0) if orientation[0].lower() == 'h' else (0,1) - bar2 = ProgressBar(MaxValue, Orientation=orientation, Size=Size, BarColor=BarColor, Scale=Scale, Target=target, BorderWidth=BorderWidth) - form = FlexForm(Title, AutoSizeText=True) + bar2 = ProgressBar(max_value, orientation=orientation, size=size, bar_color=bar_color, scale=scale, target=target, broder_width=border_width) + form = FlexForm(title, auto_size_text=True) # Form using a horizontal bar if orientation[0].lower() == 'h': single_line_message, width, height = ConvertArgsToSingleString(*args) bar2.TextToDisplay = single_line_message - bar2.MaxValue = MaxValue + bar2.MaxValue = max_value bar2.CurrentValue = 0 - form.AddRow(Text(single_line_message,Size=(width+20, height+3), AutoSizeText=True)) + form.AddRow(Text(single_line_message, size=(width + 20, height + 3), auto_size_text=True)) form.AddRow((bar2)) - form.AddRow((Cancel(ButtonColor=ButtonColor))) + form.AddRow((Cancel(button_color=button_color))) else: single_line_message, width, height = ConvertArgsToSingleString(*args) bar2.TextToDisplay = single_line_message - bar2.MaxValue = MaxValue + bar2.MaxValue = max_value bar2.CurrentValue = 0 - form.AddRow(bar2, Text(single_line_message,Size=(width+20, height+3), AutoSizeText=True)) - form.AddRow((Cancel(ButtonColor=ButtonColor))) + form.AddRow(bar2, Text(single_line_message, size=(width + 20, height + 3), auto_size_text=True)) + form.AddRow((Cancel(button_color=button_color))) form.NonBlocking = True - form.Show(NonBlocking = True) + form.Show(non_blocking= True) return bar2 # ============================== ProgressMeterUpdate =====# -def ProgressMeterUpdate(bar, Value, *args): +def ProgressMeterUpdate(bar, value, *args): ''' Update the progress meter for a form :param form: class ProgressBar - :param Value: int + :param value: int :return: True if not cancelled, OK....False if Error ''' global _my_windows @@ -1455,9 +1456,9 @@ def ProgressMeterUpdate(bar, Value, *args): bar.TextToDisplay = message - bar.CurrentValue = Value - rc = bar.UpdateBar(Value) - if Value >= bar.MaxValue or not rc: + bar.CurrentValue = value + rc = bar.UpdateBar(value) + if value >= bar.MaxValue or not rc: bar.BarExpired = True bar.ParentForm.Close() if bar.ParentForm.RootNeedsDestroying: @@ -1474,12 +1475,12 @@ def ProgressMeterUpdate(bar, Value, *args): # ============================== EASY PROGRESS METER ========================================== # # class to hold the easy meter info (a global variable essentialy) class EasyProgressMeterDataClass(): - def __init__(self, Title='', CurrentValue=1, MaxValue=10, StartTime=None, StatMessages=()): - self.Title = Title - self.CurrentValue = CurrentValue - self.MaxValue = MaxValue - self.StartTime = StartTime - self.StatMessages = StatMessages + def __init__(self, title='', current_value=1, max_value=10, start_time=None, stat_messages=()): + self.Title = title + self.CurrentValue = current_value + self.MaxValue = max_value + self.StartTime = start_time + self.StatMessages = stat_messages self.ParentForm = None self.MeterID = None @@ -1514,18 +1515,18 @@ class EasyProgressMeterDataClass(): # ============================== EasyProgressMeter =====# -def EasyProgressMeter(Title, CurrentValue, MaxValue,*args, Orientation=None, BarColor=DEFAULT_PROGRESS_BAR_COLOR, ButtonColor=None, Size=DEFAULT_PROGRESS_BAR_SIZE, Scale=(None, None),BorderWidth=DEFAULT_PROGRESS_BAR_BORDER_WIDTH): +def EasyProgressMeter(title, current_value, max_value, *args, orientation=None, bar_color=DEFAULT_PROGRESS_BAR_COLOR, button_color=None, size=DEFAULT_PROGRESS_BAR_SIZE, scale=(None, None), border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH): ''' A ONE-LINE progress meter. Add to your code where ever you need a meter. No need for a second function call before your loop. You've got enough code to write! - :param Title: Title will be shown on the window - :param CurrentValue: Current count of your items - :param MaxValue: Max value your count will ever reach. This indicates it should be closed + :param title: Title will be shown on the window + :param current_value: Current count of your items + :param max_value: Max value your count will ever reach. This indicates it should be closed :param args: VARIABLE number of arguements... you request it, we'll print it no matter what the item! - :param Orientation: - :param BarColor: - :param Size: - :param Scale: + :param orientation: + :param bar_color: + :param size: + :param scale: :param Style: :param StyleOffset: :return: False if should stop the meter @@ -1536,34 +1537,34 @@ def EasyProgressMeter(Title, CurrentValue, MaxValue,*args, Orientation=None, Bar EasyProgressMeter.EasyProgressMeterData = getattr(EasyProgressMeter, 'EasyProgressMeterData', EasyProgressMeterDataClass()) # if no meter currently running if EasyProgressMeter.EasyProgressMeterData.MeterID is None: # Starting a new meter - if int(CurrentValue) >= int(MaxValue): + if int(current_value) >= int(max_value): return False del(EasyProgressMeter.EasyProgressMeterData) - EasyProgressMeter.EasyProgressMeterData = EasyProgressMeterDataClass(Title, 1, int(MaxValue), datetime.datetime.utcnow(), []) + EasyProgressMeter.EasyProgressMeterData = EasyProgressMeterDataClass(title, 1, int(max_value), datetime.datetime.utcnow(), []) EasyProgressMeter.EasyProgressMeterData.ComputeProgressStats() message = "\n".join([line for line in EasyProgressMeter.EasyProgressMeterData.StatMessages]) - EasyProgressMeter.EasyProgressMeterData.MeterID = ProgressMeter(Title, int(MaxValue), message, *args, Orientation=Orientation, BarColor=BarColor, Size=Size, Scale=Scale, ButtonColor=ButtonColor,BorderWidth=BorderWidth) + EasyProgressMeter.EasyProgressMeterData.MeterID = ProgressMeter(title, int(max_value), message, *args, Orientation=orientation, bar_color=bar_color, size=size, scale=scale, button_color=button_color, border_width=border_width) EasyProgressMeter.EasyProgressMeterData.ParentForm = EasyProgressMeter.EasyProgressMeterData.MeterID.ParentForm return True # if exactly the same values as before, then ignore. - if EasyProgressMeter.EasyProgressMeterData.MaxValue == MaxValue and EasyProgressMeter.EasyProgressMeterData.CurrentValue == CurrentValue: + if EasyProgressMeter.EasyProgressMeterData.MaxValue == max_value and EasyProgressMeter.EasyProgressMeterData.CurrentValue == current_value: return True - if EasyProgressMeter.EasyProgressMeterData.MaxValue != int(MaxValue): + if EasyProgressMeter.EasyProgressMeterData.MaxValue != int(max_value): EasyProgressMeter.EasyProgressMeterData.MeterID = None EasyProgressMeter.EasyProgressMeterData.ParentForm = None del(EasyProgressMeter.EasyProgressMeterData) EasyProgressMeter.EasyProgressMeterData = EasyProgressMeterDataClass() # setup a new progress meter return True # HAVE to return TRUE or else the new meter will thing IT is failing when it hasn't - EasyProgressMeter.EasyProgressMeterData.CurrentValue = int(CurrentValue) - EasyProgressMeter.EasyProgressMeterData.MaxValue = int(MaxValue) + EasyProgressMeter.EasyProgressMeterData.CurrentValue = int(current_value) + EasyProgressMeter.EasyProgressMeterData.MaxValue = int(max_value) EasyProgressMeter.EasyProgressMeterData.ComputeProgressStats() message = '' for line in EasyProgressMeter.EasyProgressMeterData.StatMessages: message = message + str(line) + '\n' message = "\n".join(EasyProgressMeter.EasyProgressMeterData.StatMessages) - rc = ProgressMeterUpdate(EasyProgressMeter.EasyProgressMeterData.MeterID, CurrentValue,*args, message ) + rc = ProgressMeterUpdate(EasyProgressMeter.EasyProgressMeterData.MeterID, current_value, *args, message) # if counter >= max then the progress meter is all done. Indicate none running - if CurrentValue >= EasyProgressMeter.EasyProgressMeterData.MaxValue or not rc: + if current_value >= EasyProgressMeter.EasyProgressMeterData.MaxValue or not rc: EasyProgressMeter.EasyProgressMeterData.MeterID = None del(EasyProgressMeter.EasyProgressMeterData) EasyProgressMeter.EasyProgressMeterData = EasyProgressMeterDataClass() # setup a new progress meter @@ -1571,11 +1572,11 @@ def EasyProgressMeter(Title, CurrentValue, MaxValue,*args, Orientation=None, Bar return rc # return whatever the update told us -def EasyProgressMeterCancel(Title, *args): +def EasyProgressMeterCancel(title, *args): EasyProgressMeter.EasyProgressMeterData = getattr(EasyProgressMeter, 'EasyProgressMeterData', EasyProgressMeterDataClass()) if EasyProgressMeter.EasyProgressMeterData.MeterID is not None: # tell the normal meter update that we're at max value which will close the meter - rc = EasyProgressMeter(Title, EasyProgressMeter.EasyProgressMeterData.MaxValue, EasyProgressMeter.EasyProgressMeterData.MaxValue, ' *** CANCELLING ***', 'Caller requested a cancel', *args) + rc = EasyProgressMeter(title, EasyProgressMeter.EasyProgressMeterData.MaxValue, EasyProgressMeter.EasyProgressMeterData.MaxValue, ' *** CANCELLING ***', 'Caller requested a cancel', *args) return rc return True @@ -1609,10 +1610,10 @@ def GetComplimentaryHex(color): # ======================== Scrolled Text Box =====# # ===================================================# -def ScrolledTextBox(*args, ButtonColor=None, YesNo=False, AutoClose=False, AutoCloseDuration=None, Height=None): +def ScrolledTextBox(*args, button_color=None, yes_no=False, auto_close=False, auto_close_duration=None, height=None): if not args: return - with FlexForm(args[0], AutoSizeText=True, ButtonColor=ButtonColor, AutoClose=AutoClose, AutoCloseDuration=AutoCloseDuration) as form: - max_line_total, max_line_width, total_lines, height = 0,0,0,0 + with FlexForm(args[0], auto_size_text=True, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration) as form: + max_line_total, max_line_width, total_lines, height_computed = 0,0,0,0 complete_output = '' for message in args: # fancy code to check if string and convert if not is not need. Just always convert to string :-) @@ -1623,21 +1624,21 @@ def ScrolledTextBox(*args, ButtonColor=None, YesNo=False, AutoClose=False, AutoC max_line_total = max(max_line_total, width_used) max_line_width = MESSAGE_BOX_LINE_WIDTH lines_needed = _GetNumLinesNeeded(message, width_used) - height += lines_needed + height_computed += lines_needed complete_output += message + '\n' total_lines += lines_needed - height = MAX_SCROLLED_TEXT_BOX_HEIGHT if height > MAX_SCROLLED_TEXT_BOX_HEIGHT else height - if Height: - height = Height - form.AddRow(Multiline(complete_output, Size=(max_line_width, height)), AutoSizeText=True) + height_computed = MAX_SCROLLED_TEXT_BOX_HEIGHT if height_computed > MAX_SCROLLED_TEXT_BOX_HEIGHT else height_computed + if height: + height_computed = height + form.AddRow(Multiline(complete_output, size=(max_line_width, height_computed)), auto_size_text=True) pad = max_line_total-15 if max_line_total > 15 else 1 # show either an OK or Yes/No depending on paramater - if YesNo: - form.AddRow(Text('', Size=(pad,1), AutoSizeText=False), Yes(), No()) + if yes_no: + form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Yes(), No()) (button_text, values) = form.Show() return button_text == 'Yes' else: - form.AddRow(Text('', Size=(pad,1), AutoSizeText=False), SimpleButton('OK', Size=(5,1), ButtonColor=ButtonColor)) + form.AddRow(Text('', size=(pad, 1), auto_size_text=False), SimpleButton('OK', size=(5, 1), button_color=button_color)) form.Show() @@ -1652,10 +1653,10 @@ def ScrolledTextBox(*args, ButtonColor=None, YesNo=False, AutoClose=False, AutoC # True/False, path # # (True if Submit was pressed, false otherwise) # # ---------------------------------------------------------------------- # -def GetPathBox(Title, Message, DefaultPath='', ButtonColor=None, Size=(None,None)): - with FlexForm(Title, AutoSizeText=True, ButtonColor=ButtonColor) as form: - layout = [[Text(Message,AutoSizeText=True)], - [InputText(DefaultText=DefaultPath, Size=Size), FolderBrowse()], +def GetPathBox(title, message, default_path='', button_color=None, size=(None, None)): + with FlexForm(title, auto_size_text=True, button_color=button_color) as form: + layout = [[Text(message, auto_size_text=True)], + [InputText(default_text=default_path, size=size), FolderBrowse()], [Submit(), Cancel()]] (button, input_values) = form.LayoutAndShow(layout) @@ -1668,10 +1669,10 @@ def GetPathBox(Title, Message, DefaultPath='', ButtonColor=None, Size=(None,None # ============================== GetFileBox =========# # Like the Get folder box but for files # # ===================================================# -def GetFileBox(Title, Message, DefaultPath='',FileTypes=(("ALL Files", "*.*"),), ButtonColor=None, Size=(None,None)): - with FlexForm(Title, AutoSizeText=True, ButtonColor=ButtonColor) as form: - layout = [[Text(Message,AutoSizeText=True)], - [InputText(DefaultText=DefaultPath, Size=Size), FileBrowse(FileTypes=FileTypes)], +def GetFileBox(title, message, default_path='', file_types=(("ALL Files", "*.*"),), button_color=None, size=(None, None)): + with FlexForm(title, auto_size_text=True, button_color=button_color) as form: + layout = [[Text(message, auto_size_text=True)], + [InputText(default_text=default_path, size=size), FileBrowse(file_types=file_types)], [Submit(), Cancel()]] (button, input_values) = form.LayoutAndShow(layout) @@ -1685,10 +1686,10 @@ def GetFileBox(Title, Message, DefaultPath='',FileTypes=(("ALL Files", "*.*"),), # ============================== GetTextBox =========# # Get a single line of text # # ===================================================# -def GetTextBox(Title, Message, Default='', ButtonColor=None, Size=(None, None)): - with FlexForm(Title, AutoSizeText=True, ButtonColor=ButtonColor) as form: - layout = [[Text(Message,AutoSizeText=True)], - [InputText(DefaultText=Default, Size=Size)], +def GetTextBox(title, message, Default='', button_color=None, size=(None, None)): + with FlexForm(title, auto_size_text=True, button_color=button_color) as form: + layout = [[Text(message, auto_size_text=True)], + [InputText(default_text=Default, size=size)], [Submit(), Cancel()]] (button, input_values) = form.LayoutAndShow(layout) @@ -1701,21 +1702,21 @@ def GetTextBox(Title, Message, Default='', ButtonColor=None, Size=(None, None)): # ============================== SetGlobalIcon ======# # Sets the icon to be used by default # # ===================================================# -def SetGlobalIcon(Icon): +def SetGlobalIcon(icon): global _my_windows try: - with open(Icon, 'r') as icon_file: + with open(icon, 'r') as icon_file: pass except: raise FileNotFoundError - _my_windows.user_defined_icon = Icon + _my_windows.user_defined_icon = icon return True -# ============================== SetGlobalIcon ======# -# Sets the icon to be used by default # +# ============================== SetButtonColor =====# +# Sets the defaul button color # # ===================================================# def SetButtonColor(foreground, background): global DEFAULT_BUTTON_COLOR diff --git a/readme.md b/readme.md index 9f139bf7..2926d40d 100644 --- a/readme.md +++ b/readme.md @@ -1,69 +1,69 @@ -# PySimpleGUI - -This really is a simple GUI, but also powerfully customizable. +# PySimpleGUI + +This really is a simple GUI, but also powerfully customizable. ![GetTextBox](https://user-images.githubusercontent.com/13696193/42592930-1ca1370a-8519-11e8-907e-ad73e9be7749.jpg) -I was frustrated by having to deal with the dos prompt when I had a powerful Windows machine right in front of me. Why is it SO difficult to do even the simplest of input/output to a window in Python?? +I was frustrated by having to deal with the dos prompt when I had a powerful Windows machine right in front of me. Why is it SO difficult to do even the simplest of input/output to a window in Python?? With a simple GUI, it becomes practical to "associate" .py files with the python interpreter on Windows. Double click a py file and up pops a GUI window, a more pleasant experience than opening a dos Window and typing a command line. - + Python itself doesn't have a simple GUI solution... nor did the *many* GUI packages I tried. Most tried to do TOO MUCH, making it impossible for users to get started quickly. Others were just plain broken, requiring multiple files or other packages that were missing. - -The PySimpleGUI solution is focused on the ***developer***. How can the desired result be achieved in as little and as simple code as possible? This was the mantra used to create PySimpleGUI. - + +The PySimpleGUI solution is focused on the ***developer***. How can the desired result be achieved in as little and as simple code as possible? This was the mantra used to create PySimpleGUI. + You can add a GUI to your command line with a single line of code. With 3 or 4 lines of code you can add a fully customized GUI. And for you Machine Learning folks out there, a **single line** progress meter call that you can drop into any loop. ![progress meter 2](https://user-images.githubusercontent.com/13696193/42695896-a37eff5c-8684-11e8-8fbb-3d756655a44b.jpg) - The customization is via the form/dialog box builder that enables users to experience all of the normal GUI widgets without having to write a lot of code. - + The customization is via the form/dialog box builder that enables users to experience all of the normal GUI widgets without having to write a lot of code. - Features of PySimpleGUI include: - Text - Single Line Input - Buttons including these types: + + Features of PySimpleGUI include: + Text + Single Line Input + Buttons including these types: File Browse Folder Browse Non-closing return - Close form - Checkboxes - Radio Buttons - Icons - Multi-line Text Input - Scroll-able Output - Progress Bar - Async/Non-Blocking Windows + Close form + Checkboxes + Radio Buttons + Icons + Multi-line Text Input + Scroll-able Output + Progress Bar + Async/Non-Blocking Windows Tabbed forms - Persistent Windows - Redirect Python Output/Errors to scrolling Window + Persistent Windows + Redirect Python Output/Errors to scrolling Window 'Higher level' APIs (e.g. MessageBox, YesNobox, ...) - + An example of many widgets used on a single form. A little further down you'll find the FIFTEEN lines of code required to create this complex form. ![all widgets](https://user-images.githubusercontent.com/13696193/42604818-adb1dd5c-8542-11e8-94cb-575881590f21.jpg) - -## Getting Started with PySimpleGUI - -### Installing - - pip install PySimpleGUI - or -Simply download the file - PySimpleGUI.py and import it into your code - - -### Prerequisites - -Python 3 -tkinter - -Should run on all Python platforms that have tkinter running on them. Has been thoroughly tested on Windows. While not tested elsewhere, should work on Linux, Mac, Pi, etc. - + +## Getting Started with PySimpleGUI + +### Installing + + pip install PySimpleGUI + or +Simply download the file - PySimpleGUI.py and import it into your code + + +### Prerequisites + +Python 3 +tkinter + +Should run on all Python platforms that have tkinter running on them. Has been thoroughly tested on Windows. While not tested elsewhere, should work on Linux, Mac, Pi, etc. + ### Using To use in your code, simply import.... @@ -106,18 +106,18 @@ This feature of the Python language is utilized ***heavily*** as a method of cus Here is the function definition for the MsgBox function. The details aren't important. What is important is seeing that there is a long list of potential tweaks that a caller can make. However, they don't have to be specified on each and every call. def MsgBox(*args, - ButtonColor=None, - ButtonType=MSG_BOX_OK, - AutoClose=False, - AutoCloseDuration=None, - Icon=DEFAULT_WINDOW_ICON, - LineWidth=MESSAGE_BOX_LINE_WIDTH, - Font=None): + button_color=None, + button_type=MSG_BOX_OK, + auto_close=False, + auto_close_duration=None, + icon=DEFAULT_WINDOW_ICON, + line_width=MESSAGE_BOX_LINE_WIDTH, + font=None): If the caller wanted to change the button color to be black on yellow, the call would look something like this: SG.MsgBox('This box has a custom button color', - ButtonColor=('black', 'yellow')) + button_color=('black', 'yellow')) ![custombuttoncolor](https://user-images.githubusercontent.com/13696193/42599212-84f3fe2e-852c-11e8-8a60-4aad669a1fd6.jpg) @@ -194,15 +194,15 @@ We all have loops in our code. 'Isn't it joyful waiting, watching a counter scr ![progress meter 3](https://user-images.githubusercontent.com/13696193/42696332-dca3ca6e-8685-11e8-846b-6bee8362ee5f.jpg) EasyProgressMeter(Title, - CurrentValue, - MaxValue, + current_value, + max_value, *args, - Orientation=None, - BarColor=DEFAULT_PROGRESS_BAR_COLOR, - ButtonColor=None, - Size=DEFAULT_PROGRESS_BAR_SIZE, - Scale=(None, None), - BorderWidth=DEFAULT_PROGRESS_BAR_BORDER_WIDTH): + orientation=None, + bar_color=DEFAULT_PROGRESS_BAR_COLOR, + button_color=None, + size=DEFAULT_PROGRESS_BAR_SIZE, + scale=(None, None), + border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH): Here's the one-line Progress Meter in action! @@ -226,7 +226,7 @@ It's both not enjoyable nor helpful to immediately jump into tweaking each and e ## COPY THIS DESIGN PATTERN! - with SG.FlexForm('SHA-1 & 256 Hash', AutoSizeText=True) as form: + with SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True) as form: form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')], [SG.InputText(), SG.FileBrowse()], [SG.Submit(), SG.Cancel()]] @@ -247,7 +247,7 @@ Some elements are shortcuts, again meant to make it easy on the programmer. Rat Going through each line of code - with SG.FlexForm('SHA-1 & 256 Hash', AutoSizeText=True) as form: + with SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True) as form: This creates a new form, storing it in the variable `form`. form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')], @@ -285,20 +285,20 @@ If you have a SINGLE value being returned, it is written this way: ## All Widgets / Elements This code utilizes as many of the elements in one form as possible. - with FlexForm('Everything bagel', AutoSizeText=True, DefaultElementSize=(30,1)) as form: - layout = [[Text('Here they all are!', Size=(30,1), Font=("Helvetica", 25), TextColor='red')], - [Text('Here is some text with font sizing', Font=("Helvetica", 15))], + with FlexForm('Everything bagel', auto_size_text=True, default_element_size=(30,1)) as form: + layout = [[Text('Here they all are!', size=(30,1), font=("Helvetica", 25), text_color='red')], + [Text('Here is some text with font sizing', font=("Helvetica", 15))], [InputText()], - [Checkbox('My first checkbox!'), Checkbox('My second checkbox!', Default=True)], - [Radio('My first Radio!', "RADIO1", Default=True), Radio('My second checkbox!', "RADIO1")], - [Multiline(DefaultText='This is the DEFAULT text should you decide not to type anything', Scale=(2, 10))], - [InputCombo(['choice 1', 'choice 2'], Size=(20, 3))], - [Text('_' * 90, Size=(60, 1))], - [Text('Choose Source and Destination Folders', Size=(35,1))], - [Text('Source Folder', Size=(15, 1), AutoSizeText=False), InputText('Source'), FolderBrowse()], - [Text('Destination Folder', Size=(15, 1), AutoSizeText=False), InputText('Dest'), FolderBrowse()], + [Checkbox('My first checkbox!'), Checkbox('My second checkbox!', default=True)], + [Radio('My first Radio!', "RADIO1", default=True), Radio('My second checkbox!', "RADIO1")], + [Multiline(DefaultText='This is the DEFAULT text should you decide not to type anything', scale=(2, 10))], + [InputCombo(['choice 1', 'choice 2'], size=(20, 3))], + [Text('_' * 90, size=(60, 1))], + [Text('Choose Source and Destination Folders', size=(35,1))], + [Text('Source Folder', size=(15, 1), auto_size_text=False), InputText('Source'), FolderBrowse()], + [Text('Destination Folder', size=(15, 1), auto_size_text=False), InputText('Dest'), FolderBrowse()], [SimpleButton('Your Button with any text you want')], - [SimpleButton('Big Text', Size=(12,1), Font=("Helvetica", 20))], + [SimpleButton('Big Text', size=(12,1), font=("Helvetica", 20))], [Submit(), Cancel()]] (button, (values)) = form.LayoutAndShow(layout) @@ -306,7 +306,7 @@ This code utilizes as many of the elements in one form as possible. - MsgBox('Results', 'You clicked {}'.format(button),'The values returned from form', values , Font = ("Helvetica", 15)) + MsgBox('Results', 'You clicked {}'.format(button),'The values returned from form', values , font = ("Helvetica", 15)) This is a somewhat complex form with quite a bit of custom sizing to make things line up well. This is code you only have to write once. When looking at the code, remember that what you're seeing is a list of lists. Each row contains a list of Graphical Elements that are used to create the form. @@ -336,27 +336,27 @@ You've already seen a number of examples above that use blocking forms. Anytime NON-BLOCKING form call: - form.Show(NonBlocking=True) + form.Show(non_blocking=True) ### Beginning a Form The first step is to create the form object using the desired form customization. - with FlexForm('Everything bagel', AutoSizeText=True, DefaultElementSize=(30,1)) as form: + with FlexForm('Everything bagel', auto_size_text=True, default_element_size=(30,1)) as form: Let's go through the options available when creating a form. def __init__(self, title, - DefaultElementSize=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), - AutoSizeText=DEFAULT_AUTOSIZE_TEXT, - Scale=(None, None), - Size=(None, None), - Location=(None, None), - ButtonColor=None,Font=None, - ProgressBarColor=(None,None), - IsTabbedForm=False, - BorderDepth=None, - AutoClose=False, - AutoCloseDuration=DEFAULT_AUTOCLOSE_TIME, - Icon=DEFAULT_WINDOW_ICON): + default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), + auto_size_text=DEFAULT_AUTOSIZE_TEXT, + scale=(None, None), + size=(None, None), + location=(None, None), + button_color=None,Font=None, + progress_bar_color=(None,None), + is_tabbed_form=False, + border_depth=None, + auto_close=False, + auto_close_duration=DEFAULT_AUTOCLOSE_TIME, + icon=DEFAULT_WINDOW_ICON): #### Sizes @@ -364,25 +364,25 @@ Note several variables that deal with "size". Element sizes are measured in cha The default Element size for PySimpleGUI is `(45,1)`. -Sizes can be set at the element level, or in this case, the size variables apply to all elements in the form. Setting `Size=(20,1)` in the form creation call will set all elements in the form to that size. +Sizes can be set at the element level, or in this case, the size variables apply to all elements in the form. Setting `size=(20,1)` in the form creation call will set all elements in the form to that size. -In addition to `size` there is a `scale` option. Scale will take the Element's size and scale it up or down depending on the scale value. `scale=(1,1)` doesn't change the Element's size. `scale=(2,1)` will set the Element's size to be twice as wide as the size setting. +In addition to `size` there is a `scale` option. `scale` will take the Element's size and scale it up or down depending on the scale value. `scale=(1,1)` doesn't change the Element's size. `scale=(2,1)` will set the Element's size to be twice as wide as the size setting. #### FlexForm - form-level variables overview A summary of the variables that can be changed when a FlexForm is created - DefaultElementSize - set default size for all elements in the form - AutoSizeText - true/false autosizing turned on / off - Scale - set scale value for all elements - ButtonColor - default button color (foreground, background) - Font - font name and size for all text items - ProgressBarColor - progress bar colors - IsTabbedForm - true/false indicates form is a tabbed or normal form - BorderDepth - style setting for buttons, input fields - AutoClose - true/false indicates if form will automatically close - AutoCloseDuration - how long in seconds before closing form - Icon - filename for icon that's displayed on the window on taskbar + default_element_size - set default size for all elements in the form + auto_size_text- true/false autosizing turned on / off + scale - set scale value for all elements + button_color- default button color (foreground, background) + font - font name and size for all text items + progress_bar_color - progress bar colors + is_tabbed_form - true/false indicates form is a tabbed or normal form + border_depth - style setting for buttons, input fields + auto_close - true/false indicates if form will automatically close + auto_close_duration - how long in seconds before closing form + icon - filename for icon that's displayed on the window on taskbar ## Elements @@ -424,11 +424,11 @@ The code is a crude representation of the GUI, laid out in text. The most basic element is the Text element. It simply displays text. Many of the 'options' that can be set for a Text element are shared by other elements. Size, Scale are a couple that you will see in every element. Text(Text, - Scale=(None, None), - Size=(None, None), - AutoSizeText=None, - Font=None, - TextColor=None) + scale=(None, None), + size=(None, None), + auto_size_text=None, + font=None, + text_color=None) Some commonly used elements have 'shorthand' versions of the functions to make the code more compact. The functions `T` and `Txt` are the same as calling `Text`. @@ -448,8 +448,8 @@ The values foreground and background can be the color names or the hex value for "#RRGGBB" -**AutoSizeText** -A `True` value for `AutoSizeText`, when placed on any Element, indicates that the width of the Element should be shrunk do the width of the text. This is particularly useful with `Buttons` as fixed-width buttons are somewhat crude looking. The default value is `False`. You will often see this setting on FlexForm definitions. +**auto_size_text** +A `True` value for `auto_size_text`, when placed on any Element, indicates that the width of the Element should be shrunk do the width of the text. This is particularly useful with `Buttons` as fixed-width buttons are somewhat crude looking. The default value is `False`. You will often see this setting on FlexForm definitions. **Shorthand functions** The shorthand functions for `Text` are `Txt` and `T` @@ -457,55 +457,55 @@ The shorthand functions for `Text` are `Txt` and `T` #### Multiline Text Element - layout = [[SG.Multiline('This is what a Multi-line Text Element looks like', Size=(45,5))]] + layout = [[SG.Multiline('This is what a Multi-line Text Element looks like', size=(45,5))]] ![multiline text](https://user-images.githubusercontent.com/13696193/42670464-0824c754-8629-11e8-9741-6ed08f924618.jpg) This Element doubles as both an input and output Element. The `DefaultText` optional parameter is used to indicate what to output to the window. - Multiline(DefaultText='', - EnterSubmits = False, - Scale=(None, None), - Size=(None, None), - AutoSizeText=None) + Multiline(default_text='', + enter_submits = False, + scale=(None, None), + size=(None, None), + auto_size_text=None) . - DefaultText - Text to display in the text box - EnterSubmits - Bool. If True, pressing Enter key submits form - Scale - Element's scale - Size - Element's size - AutoSizeText - Bool. Change width to match size of text + default_text - Text to display in the text box + enter_submits - Bool. If True, pressing Enter key submits form + scale - Element's scale + size - Element's size + auto_size_text - Bool. Change width to match size of text #### Output Element Output re-routes `Stdout` to a scrolled text box. It's used with Async forms. More on this later. - form.AddRow(gg.Output(Size=(100,20))) + form.AddRow(gg.Output(size=(100,20))) ![output element](https://user-images.githubusercontent.com/13696193/42704820-5446959c-869f-11e8-849e-047ea280387a.jpg) - Output(Scale=(None, None), - Size=(None, None)) + Output(scale=(None, None), + size=(None, None)) . - Scale - How much to scale size of element - Size - Size of element (width, height) in characters + scale - How much to scale size of element + size - Size of element (width, height) in characters ### Input Elements - These make up the majority of the form definition. Optional variables at the Element level override the Form level values (e.g. `Size` is specified in the Element). All input Elements create an entry in the list of return values. A Text Input Element creates a string in the list of items returned. + These make up the majority of the form definition. Optional variables at the Element level override the Form level values (e.g. `size` is specified in the Element). All input Elements create an entry in the list of return values. A Text Input Element creates a string in the list of items returned. #### Text Input Element layout = [[SG.InputText('Default text')]] ![inputtext](https://user-images.githubusercontent.com/13696193/42693515-610a716c-867d-11e8-9a00-7e7fcf771230.jpg) - def InputText(DefaultText = '', - Scale=(None, None), - Size=(None, None), - AutoSizeText=None) + def InputText(default_text = '', + scale=(None, None), + size=(None, None), + auto_size_text=None) . - DefaultText - Text initially shown in the input box - Scale - Amount size is scaled by - Size - (width, height) of element in characters - AutoSizeText - Bool. True is element should be sized to fit text + default_text - Text initially shown in the input box + scale - Amount size is scaled by + size - (width, height) of element in characters + auto_size_text- Bool. True is element should be sized to fit text Shorthand functions that are equivalent to `InputText` are `Input` and `In` @@ -517,88 +517,88 @@ Also known as a drop-down list. Only required parameter is the list of choices. ![combo](https://user-images.githubusercontent.com/13696193/42694431-631c4108-8680-11e8-8e99-c1a642734464.jpg) - InputCombo(Values, - Scale=(None, None), - Size=(None, None), - AutoSizeText=None) + InputCombo(values, + scale=(None, None), + size=(None, None), + auto_size_text=None) . - Values Choices to be displayed. List of strings - Scale - Amount to scale size by - Size - (width, height) of element in characters - AutoSizeText - Bool. True if size should fit the text length + values - Choices to be displayed. List of strings + scale - Amount to scale size by + size - (width, height) of element in characters + auto_size_text - Bool. True if size should fit the text length #### Radio Button Element Creates one radio button that is assigned to a group of radio buttons. Only 1 of the buttons in the group can be selected at any one time. - layout = [[SG.Radio('My first Radio!', "RADIO1", Default=True), SG.Radio('My second radio!', "RADIO1")]] + layout = [[SG.Radio('My first Radio!', "RADIO1", default=True), SG.Radio('My second radio!', "RADIO1")]] ![radio element](https://user-images.githubusercontent.com/13696193/42705705-327b4b6c-86a2-11e8-81a7-740e57646ba8.jpg) - Radio(Text, - GroupID, - Default=False, - Scale=(None, None), - Size=(None, None), - AutoSizeText=None, - Font=None) + Radio(text, + group_id, + default=False, + scale=(None, None), + size=(None, None), + auto_size_text=None, + font=None) . - Text - Text to display next to button - GroupID - Groups together multiple Radio Buttons. Can be any value - Default - Bool. Initial state - Scale - Amount to scale size of element - Size - (width, height) size of element in characters - AutoSizeText - Bool. True if should size width to fit text - Font - Font type and size for text display + text - Text to display next to button + group_id - Groups together multiple Radio Buttons. Can be any value + default - Bool. Initial state + scale - Amount to scale size of element + size- (width, height) size of element in characters + auto_size_text - Bool. True if should size width to fit text + font - Font type and size for text display #### Checkbox Element Checkbox elements are like Radio Button elements. They return a bool indicating whether or not they are checked. - layout = [[SG.Checkbox('My first Checkbox!', Default=True), SG.Checkbox('My second Checkbox!')]] + layout = [[SG.Checkbox('My first Checkbox!', default=True), SG.Checkbox('My second Checkbox!')]] ![checkbox element](https://user-images.githubusercontent.com/13696193/42717015-655d73d2-86cc-11e8-9c69-3c810f48e578.jpg) - Checkbox(Text, - Default=False, - Scale=(None, None), - Size=(None, None), - AutoSizeText=None, - Font=None): + Checkbox(text, + default=False, + scale=(None, None), + size=(None, None), + auto_size_text=None, + font=None): . - Text - Text to display next to checkbox - Default - Bool. Initial state - Scale - Amount to scale size of element - Size - (width, height) size of element in characters - AutoSizeText - Bool. True if should size width to fit text - Font - Font type and size for text display + text - Text to display next to checkbox + default- Bool. Initial state + scale - Amount to scale size of element + size - (width, height) size of element in characters + auto_size_text- Bool. True if should size width to fit text + font- Font type and size for text display #### Spin Element An up/down spinner control. The valid values are passed in as a list. - layout = [[SG.Spin([i for i in range(1,11)], InitialValue=1), SG.Text('Volume level')]] + layout = [[SG.Spin([i for i in range(1,11)], initial_value=1), SG.Text('Volume level')]] ![spin element](https://user-images.githubusercontent.com/13696193/42717231-8ddb51d4-86cd-11e8-827a-75f2237477fa.jpg) - Spin(Values, - InitialValue=None, - Scale=(None, None), - Size=(None, None), - AutoSizeText=None, - Font=None) + Spin(values, + intiial_value=None, + scale=(None, None), + size=(None, None), + auto_size_text=None, + font=None) . - Values - List of valid values - InitialValue - String with initial value - Scale - Amount to scale size of element - Size - (width, height) size of element in characters - AutoSizeText - Bool. True if should size width to fit text - Font - Font type and size for text display + values - List of valid values + initial_value - String with initial value + scale - Amount to scale size of element + size - (width, height) size of element in characters + auto_size_text - Bool. True if should size width to fit text + font - Font type and size for text display #### Button Element Buttons are the most important element of all! They cause the majority of the action to happen. After all, it's a button press that will get you out of a form, whether it but Submit or Cancel, one way or another a button is involved in all forms. The only exception is to this is when the user closes the window using the "X" in the upper corner which means no button was involved. @@ -620,12 +620,12 @@ Read Form - This is an async form button that will read a snapshot of all of the While it's possible to build forms using the Button Element directly, you should never need to do that. There are pre-made buttons and shortcuts that will make life much easier. The most basic Button element call to use is `SimpleButton` - SimpleButton(Text, - Scale=(None, None), - Size=(None, None), - AutoSizeText=None, - ButtonColor=None, - Font=None) + SimpleButton(text, + scale=(None, None), + size=(None, None), + auto_size_text=None, + button_color=None, + font=None) Pre-made buttons include: @@ -667,16 +667,16 @@ layout = [[SG.SimpleButton('My Button')]] ![singlebutton](https://user-images.githubusercontent.com/13696193/42718281-9453deca-86d5-11e8-83c7-4b6d33720858.jpg) -All buttons can have their text changed by changing the `ButtonText` variable. +All buttons can have their text changed by changing the `button_text` variable. **File Types** -The `FileBrowse` button has an additional setting named `FileTypes`. This variable is used to filter the files shown in the file dialog box. The default value for this setting is +The `FileBrowse` button has an additional setting named `file_types`. This variable is used to filter the files shown in the file dialog box. The default value for this setting is FileTypes=(("ALL Files", "*.*"),) This code produces a form where the Browse button only shows files of type .TXT - layout = [[SG.In() ,SG.FileBrowse(FileTypes=(("Text Files", "*.txt"),))]] + layout = [[SG.In() ,SG.FileBrowse(file_types=(("Text Files", "*.txt"),))]] ***The ENTER key*** The ENTER key is an important part of data entry for forms. There's a long tradition of the enter key being used to quickly submit forms. PySimpleGUI implements this tying the ENTER key to the first button that closes or reads a form. If there are more than 1 button on a form, the FIRST button that is of type Close Form or Read Form is used. First is determined by scanning the form, top to bottom and left to right. Keep this in mind when designing forms. @@ -693,23 +693,23 @@ If you want a bit more customization of your meter, then you can go up 1 level a You setup the progress meter by calling - my_meter = ProgressMeter(Title, - MaxValue, + my_meter = ProgressMeter(title, + max_value, *args, - Orientation=None, - BarColor=DEFAULT_PROGRESS_BAR_COLOR, - ButtonColor=None, - Size=DEFAULT_PROGRESS_BAR_SIZE, - Scale=(None, None), - BorderWidth=DEFAULT_PROGRESS_BAR_BORDER_WIDTH) + orientantion=None, + bar_color=DEFAULT_PROGRESS_BAR_COLOR, + button_color=None, + size=DEFAULT_PROGRESS_BAR_SIZE, + scale=(None, None), + border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH) Then to update the bar within your loop return_code = ProgressMeterUpdate(my_meter, - Value, + value, *args): Putting it all together you get this design pattern - my_meter = SG.ProgressMeter('Meter Title', 100000, Orientation='Vert') + my_meter = SG.ProgressMeter('Meter Title', 100000, orentation='Vert') for i in range(0, 100000): SG.ProgressMeterUpdate(my_meter, i+1, 'Some variable', 'Another variable') @@ -720,17 +720,17 @@ The final way of using a Progress Meter with PySimpleGUI is to build a custom fo #### Output The Output Element is a re-direction of Stdout. Anything "printed" will be displayed in this element. - Output(Scale=(None, None), - Size=(None, None)) + Output(scale=(None, None), + size=(None, None)) Here's a complete solution for a chat-window using an Async form with an Output Element import PySimpleGUI as g - with g.FlexForm('Chat Window', AutoSizeText=True, DefaultElementSize=(30, 2)) as form: - form.AddRow(g.Text('This is where standard out is being routed', Size=[40,1])) - form.AddRow(g.Output(Size=(80, 20))) - form.AddRow(g.Multiline(Size=(70, 5), EnterSubmits=True), g.ReadFormButton('SEND', ButtonColor=(g.YELLOWS[0], g.BLUES[0])), g.SimpleButton('EXIT', ButtonColor=(g.YELLOWS[0], g.GREENS[0]))) + with g.FlexForm('Chat Window', auto_size_text=True, default_element_size=(30, 2)) as form: + form.AddRow(g.Text('This is where standard out is being routed', size=[40,1])) + form.AddRow(g.Output(size=(80, 20))) + form.AddRow(g.Multiline(size=(70, 5), enter_submits=True), g.ReadFormButton('SEND', button_color=(g.YELLOWS[0], g.BLUES[0])), g.SimpleButton('EXIT', button_color=(g.YELLOWS[0], g.GREENS[0]))) # ---===--- Loop taking in user input and printing it --- # while True: @@ -773,9 +773,11 @@ While not an "issue" this is a *stern warning* A MikeTheWatchGuy production... entirely responsible for this code.... unless it causes you trouble in which case I'm not at all responsible. ## Versioning - -1.0.9 - July 10, 2018 - Initial Release -1.0.21 - July 13, 2018 - Readme updates +|Version | Description | +|--|--| +| 1.0.9 | July 10, 2018 - Initial Release | +| 1.0.21 | July 13, 2018 - Readme updates | +| 2.0 | July 16, 2018 - ALL optional parameters renamed from CamelCase to all_lower_case ## Code Condition