From d0ab0c42c5f47ac56cac2213f29d029c28557213 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sun, 12 Aug 2018 17:45:12 -0400 Subject: [PATCH] Autosize text now TRUE by default, Remove progress bar target, cleanup how return values made, ChangeLookAndFeel func --- PySimpleGUI.py | 311 +++++++++++++++++++++---------------------------- 1 file changed, 132 insertions(+), 179 deletions(-) diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 948c557f..7e277293 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -14,7 +14,7 @@ DEFAULT_WINDOW_ICON = '' DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS DEFAULT_MARGINS = (10,5) # Margins for each LEFT/RIGHT margin is first term DEFAULT_ELEMENT_PADDING = (5,3) # Padding between elements (row, col) in pixels -DEFAULT_AUTOSIZE_TEXT = False +DEFAULT_AUTOSIZE_TEXT = True DEFAULT_AUTOSIZE_BUTTONS = True DEFAULT_FONT = ("Helvetica", 10) DEFAULT_TEXT_JUSTIFICATION = 'left' @@ -603,11 +603,11 @@ class Button(Element): def ButtonReleaseCallBack(self, parm): r, c = self.Position - self.ParentForm.Results[r][c] = False # mark this button's location in results + self.ParentForm.LastButtonClicked = None def ButtonPressCallBack(self, parm): r, c = self.Position - self.ParentForm.Results[r][c] = True # mark this button's location in results + self.ParentForm.LastButtonClicked = self.ButtonText # ------- Button Callback ------- # def ButtonCallBack(self): @@ -642,7 +642,7 @@ class Button(Element): # first, get the results table built # modify the Results table in the parent FlexForm object r,c = self.Position - self.ParentForm.Results[r][c] = True # mark this button's location in results + self.ParentForm.LastButtonClicked = self.ButtonText # if the form is tabbed, must collect all form's results and destroy all forms if self.ParentForm.IsTabbedForm: self.ParentForm.UberParent._Close() @@ -656,7 +656,7 @@ class Button(Element): # first, get the results table built # modify the Results table in the parent FlexForm object r,c = self.Position - self.ParentForm.Results[r][c] = True # mark this button's location in results + self.ParentForm.LastButtonClicked = self.ButtonText self.ParentForm.TKroot.quit() # kick the users out of the mainloop return @@ -671,12 +671,11 @@ class Button(Element): # ProgreessBar # # ---------------------------------------------------------------------- # class ProgressBar(Element): - 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, border_width=None, relief=None): + def __init__(self, max_value, orientation=None, scale=(None, None), size=(None, None), auto_size_text=None, bar_color=(None, None), style=None, border_width=None, relief=None): ''' Progress Bar Element :param max_value: :param orientation: - :param target: :param scale: Adds multiplier to size (w,h) :param size: Size of field in characters :param auto_size_text: True if should shrink field to fit the default text @@ -692,7 +691,6 @@ class ProgressBar(Element): 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 = border_width if border_width else DEFAULT_PROGRESS_BAR_BORDER_WIDTH self.Relief = relief if relief else DEFAULT_PROGRESS_BAR_RELIEF self.BarExpired = False @@ -702,12 +700,6 @@ class ProgressBar(Element): def UpdateBar(self, current_count): if self.ParentForm.TKrootDestroyed: return False - target = self.Target - if target[0] != None: # if there's a target, get it and update the strvar - target_element = self.ParentForm.GetElementAtLocation(target) - strvar = target_element.TKStringVar - rc = strvar.set(self.TextToDisplay) - self.TKProgressBar.Update(current_count) try: self.ParentForm.TKroot.update() @@ -806,8 +798,10 @@ class FlexForm: self.RootNeedsDestroying = False self.Shown = False self.ReturnValues = None - self.ReturnValuesDictionary = None - self.ResultsBuilt = False + self.ReturnValuesList = [] + self.ReturnValuesDictionary = {} + self.DictionaryKeyCounter = 0 + self.LastButtonClicked = None self.UseDictionary = False self.UseDefaultFocus = False @@ -911,7 +905,7 @@ class FlexForm: if self.RootNeedsDestroying: self.TKroot.destroy() _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 - return BuildResults(self) + return BuildResults(self, False) def ReadNonBlocking(self, Message=''): if self.TKrootDestroyed: @@ -925,7 +919,7 @@ class FlexForm: except: self.TKrootDestroyed = True _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 - return BuildResults(self) + return BuildResults(self, False) # LEGACY version of ReadNonBlocking def Refresh(self, Message=''): @@ -938,14 +932,14 @@ class FlexForm: except: self.TKrootDestroyed = True _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 - return BuildResults(self) + return BuildResults(self, False) def _Close(self): try: self.TKroot.update() except: pass if not self.NonBlocking: - results = BuildResults(self) + results = BuildResults(self, False) if self.TKrootDestroyed: return None self.TKrootDestroyed = True @@ -1083,53 +1077,23 @@ def ReadFormButton(button_text, image_filename=None, image_size=(None, None),ima def RealtimeButton(button_text, image_filename=None, image_size=(None, None),image_subsample=None,border_width=None,scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False): return Button(BUTTON_TYPE_REALTIME, image_filename=image_filename, image_size=image_size, image_subsample=image_subsample, border_width=border_width, button_text=button_text, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus) -#------------------------------------------------------------------------------------------------------# -# ------- FUNCTION InitializeResults. Sets up form results matrix ------- # +##################################### ----- RESULTS ------ ################################################## + +def AddToReturnDictionary(form, element, value): + if element.Key is None: + form.ReturnValuesDictionary[form.DictionaryKeyCounter] = value + form.DictionaryKeyCounter += 1 + else: + form.ReturnValuesDictionary[element.Key] = value + +def AddToReturnList(form, value): + form.ReturnValuesList.append(value) + + +#----------------------------------------------------------------------------# +# ------- FUNCTION InitializeResults. Sets up form results matrix --------# def InitializeResults(form): - # initial results for elements are: - # TEXT - None - # INPUT - Initial value - # Button - False - results = [] - return_vals = [] - for row_num,row in enumerate(form.Rows): - r = [] - for element in row: - if element.Type == ELEM_TYPE_TEXT: - r.append(None) - if element.Type == ELEM_TYPE_IMAGE: - r.append(None) - elif element.Type == ELEM_TYPE_INPUT_TEXT: - r.append(element.TextInputDefault) - return_vals.append(None) - elif element.Type == ELEM_TYPE_INPUT_MULTILINE: - r.append(element.TextInputDefault) - return_vals.append(None) - elif element.Type == ELEM_TYPE_BUTTON: - r.append(False) - elif element.Type == ELEM_TYPE_PROGRESS_BAR: - r.append(None) - elif element.Type == ELEM_TYPE_INPUT_CHECKBOX: - r.append(element.InitialState) - return_vals.append(element.InitialState) - elif element.Type == ELEM_TYPE_INPUT_RADIO: - r.append(element.InitialState) - return_vals.append(element.InitialState) - elif element.Type == ELEM_TYPE_INPUT_COMBO: - r.append(element.TextInputDefault) - return_vals.append(None) - elif element.Type == ELEM_TYPE_INPUT_LISTBOX: - r.append(None) - return_vals.append(None) - elif element.Type == ELEM_TYPE_INPUT_SPIN: - r.append(element.DefaultValue) - return_vals.append(None) - elif element.Type == ELEM_TYPE_INPUT_SLIDER: - r.append(element.DefaultValue) - return_vals.append(None) - results.append(r) - form.Results=results - form.ReturnValues = (None, return_vals) + BuildResults(form, True) return #===== Radio Button RadVar encoding and decoding =====# @@ -1146,124 +1110,76 @@ def EncodeRadioRowCol(row, col): # ------- FUNCTION BuildResults. Form exiting so build the results to pass back ------- # # format of return values is # (Button Pressed, input_values) -def BuildResults(form): +def BuildResults(form, initialize_only): # Results for elements are: # TEXT - Nothing # INPUT - Read value from TK # Button - Button Text and position as a Tuple # Get the initialized results so we don't have to rebuild - results=form.Results button_pressed_text = None input_values = [] - input_values_dictionary = {} + form.DictionaryKeyCounter = 0 + form.ReturnValuesDictionary = {} + form.ReturnValuesList = [] key_counter = 0 for row_num,row in enumerate(form.Rows): for col_num, element in enumerate(row): - if element.Type == ELEM_TYPE_INPUT_TEXT: - value=element.TKStringVar.get() - results[row_num][col_num] = value - input_values.append(value) - if not form.NonBlocking and not element.do_not_clear: - element.TKStringVar.set('') - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value - elif element.Type == ELEM_TYPE_INPUT_CHECKBOX: - value=element.TKIntVar.get() - results[row_num][col_num] = value - input_values.append(value != 0) - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value - elif element.Type == ELEM_TYPE_INPUT_RADIO: - RadVar=element.TKIntVar.get() - this_rowcol = EncodeRadioRowCol(row_num,col_num) - value = RadVar == this_rowcol - results[row_num][col_num] = value - input_values.append(value) - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value - elif element.Type == ELEM_TYPE_BUTTON: - if results[row_num][col_num] is True: - button_pressed_text = element.ButtonText - if element.BType != BUTTON_TYPE_REALTIME: # Do not clear realtime buttons - results[row_num][col_num] = False - elif element.Type == ELEM_TYPE_INPUT_COMBO: - value=element.TKStringVar.get() - results[row_num][col_num] = value - input_values.append(value) - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value - elif element.Type == ELEM_TYPE_INPUT_LISTBOX: - items=element.TKListbox.curselection() - value = [element.Values[int(item)] for item in items] - results[row_num][col_num] = value - input_values.append(value) - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value - elif element.Type == ELEM_TYPE_INPUT_SPIN: - try: + if not initialize_only: + if element.Type == ELEM_TYPE_INPUT_TEXT: value=element.TKStringVar.get() - except: - value = 0 - results[row_num][col_num] = value - input_values.append(value) - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value - elif element.Type == ELEM_TYPE_INPUT_SLIDER: - try: - value=element.TKIntVar.get() - except: - value = 0 - results[row_num][col_num] = value - input_values.append(value) - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value - elif element.Type == ELEM_TYPE_INPUT_MULTILINE: - try: - value=element.TKText.get(1.0, tk.END) if not form.NonBlocking and not element.do_not_clear: - element.TKText.delete('1.0', tk.END) - except: - value = None - results[row_num][col_num] = value - input_values.append(value) - if element.Key is None: - input_values_dictionary[key_counter] = value - key_counter +=1 - else: - input_values_dictionary[element.Key] = value + element.TKStringVar.set('') + elif element.Type == ELEM_TYPE_INPUT_CHECKBOX: + value=element.TKIntVar.get() + elif element.Type == ELEM_TYPE_INPUT_RADIO: + RadVar=element.TKIntVar.get() + this_rowcol = EncodeRadioRowCol(row_num,col_num) + value = RadVar == this_rowcol + elif element.Type == ELEM_TYPE_BUTTON: + if form.LastButtonClicked == element.ButtonText: + button_pressed_text = form.LastButtonClicked + if element.BType != BUTTON_TYPE_REALTIME: # Do not clear realtime buttons + form.LastButtonClicked = None + elif element.Type == ELEM_TYPE_INPUT_COMBO: + value=element.TKStringVar.get() + elif element.Type == ELEM_TYPE_INPUT_LISTBOX: + items=element.TKListbox.curselection() + value = [element.Values[int(item)] for item in items] + elif element.Type == ELEM_TYPE_INPUT_SPIN: + try: + value=element.TKStringVar.get() + except: + value = 0 + elif element.Type == ELEM_TYPE_INPUT_SLIDER: + try: + value=element.TKIntVar.get() + except: + value = 0 + elif element.Type == ELEM_TYPE_INPUT_MULTILINE: + try: + value=element.TKText.get(1.0, tk.END) + if not form.NonBlocking and not element.do_not_clear: + element.TKText.delete('1.0', tk.END) + except: + value = None + else: + value = None + # if an input type element, update the results + if element.Type != ELEM_TYPE_BUTTON and element.Type != ELEM_TYPE_TEXT and element.Type != ELEM_TYPE_IMAGE and\ + element.Type != ELEM_TYPE_OUTPUT and element.Type != ELEM_TYPE_PROGRESS_BAR: + AddToReturnList(form, value) + AddToReturnDictionary(form, element, value) try: - input_values_dictionary.pop(None, None) # clean up dictionary include None was included + form.ReturnValuesDictionary.pop(None, None) # clean up dictionary include None was included except: pass if not form.UseDictionary: - form.ReturnValues = button_pressed_text, input_values + form.ReturnValues = button_pressed_text, form.ReturnValuesList else: - form.ReturnValues = button_pressed_text, input_values_dictionary - form.ReturnValuesDictionary = button_pressed_text, input_values_dictionary - form.ResultsBuilt = True + form.ReturnValues = button_pressed_text, form.ReturnValuesDictionary + return form.ReturnValues @@ -1916,7 +1832,7 @@ def ConvertArgsToSingleString(*args): # ============================== ProgressMeter =====# # ===================================================# -def ProgressMeter(title, max_value, *args, orientation=None, bar_color=(None,None), button_color=None, size=DEFAULT_PROGRESS_BAR_SIZE, scale=(None, None), border_width=None): +def _ProgressMeter(title, max_value, *args, orientation=None, bar_color=(None,None), button_color=None, size=DEFAULT_PROGRESS_BAR_SIZE, scale=(None, None), border_width=None): ''' Create and show a form on tbe caller's behalf. :param title: @@ -1932,8 +1848,7 @@ def ProgressMeter(title, max_value, *args, orientation=None, bar_color=(None,Non ''' local_orientation = DEFAULT_METER_ORIENTATION if orientation is None else orientation local_border_width = DEFAULT_PROGRESS_BAR_BORDER_WIDTH if border_width is None else border_width - target = (0,0) if local_orientation[0].lower() == 'h' else (0,1) - bar2 = ProgressBar(max_value, orientation=local_orientation, size=size, bar_color=bar_color, scale=scale, target=target, border_width=local_border_width, relief=DEFAULT_PROGRESS_BAR_RELIEF) + bar2 = ProgressBar(max_value, orientation=local_orientation, size=size, bar_color=bar_color, scale=scale, border_width=local_border_width, relief=DEFAULT_PROGRESS_BAR_RELIEF) form = FlexForm(title, auto_size_text=True) # Form using a horizontal bar @@ -1942,7 +1857,8 @@ def ProgressMeter(title, max_value, *args, orientation=None, bar_color=(None,Non bar2.TextToDisplay = single_line_message bar2.MaxValue = max_value bar2.CurrentValue = 0 - form.AddRow(Text(single_line_message, size=(width, height + 3), auto_size_text=True)) + bar_text = Text(single_line_message, size=(width, height + 3), auto_size_text=True) + form.AddRow(bar_text) form.AddRow((bar2)) form.AddRow((Cancel(button_color=button_color))) else: @@ -1950,15 +1866,16 @@ def ProgressMeter(title, max_value, *args, orientation=None, bar_color=(None,Non bar2.TextToDisplay = single_line_message bar2.MaxValue = max_value bar2.CurrentValue = 0 - form.AddRow(bar2, Text(single_line_message, size=(width, height + 3), auto_size_text=True)) + bar_text = Text(single_line_message, size=(width, height + 3), auto_size_text=True) + form.AddRow(bar2, bar_text) form.AddRow((Cancel(button_color=button_color))) form.NonBlocking = True form.Show(non_blocking= True) - return bar2 + return bar2, bar_text # ============================== ProgressMeterUpdate =====# -def ProgressMeterUpdate(bar, value, *args): +def _ProgressMeterUpdate(bar, value, text_elem, *args): ''' Update the progress meter for a form :param form: class ProgressBar @@ -1969,8 +1886,8 @@ def ProgressMeterUpdate(bar, value, *args): if bar == None: return False if bar.BarExpired: return False message, w, h = ConvertArgsToSingleString(*args) - - bar.TextToDisplay = message + text_elem.Update(message) + # bar.TextToDisplay = message bar.CurrentValue = value rc = bar.UpdateBar(value) if value >= bar.MaxValue or not rc: @@ -1998,6 +1915,7 @@ class EasyProgressMeterDataClass(): self.StatMessages = stat_messages self.ParentForm = None self.MeterID = None + self.MeterText = None # =========================== COMPUTE PROGRESS STATS ======================# def ComputeProgressStats(self): @@ -2059,7 +1977,7 @@ def EasyProgressMeter(title, current_value, max_value, *args, orientation=None, 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(max_value), message, *args, orientation=orientation, bar_color=bar_color, size=size, scale=scale, button_color=button_color, border_width=local_border_width) + EasyProgressMeter.EasyProgressMeterData.MeterID, EasyProgressMeter.EasyProgressMeterData.MeterText= _ProgressMeter(title, int(max_value), message, *args, orientation=orientation, bar_color=bar_color, size=size, scale=scale, button_color=button_color, border_width=local_border_width) EasyProgressMeter.EasyProgressMeterData.ParentForm = EasyProgressMeter.EasyProgressMeterData.MeterID.ParentForm return True # if exactly the same values as before, then ignore. @@ -2079,7 +1997,8 @@ def EasyProgressMeter(title, current_value, max_value, *args, orientation=None, message = message + str(line) + '\n' message = "\n".join(EasyProgressMeter.EasyProgressMeterData.StatMessages) args= args + (message,) - rc = ProgressMeterUpdate(EasyProgressMeter.EasyProgressMeterData.MeterID, current_value, *args) + rc = _ProgressMeterUpdate(EasyProgressMeter.EasyProgressMeterData.MeterID, current_value, + EasyProgressMeter.EasyProgressMeterData.MeterText, *args) # if counter >= max then the progress meter is all done. Indicate none running if current_value >= EasyProgressMeter.EasyProgressMeterData.MaxValue or not rc: EasyProgressMeter.EasyProgressMeterData.MeterID = None @@ -2422,6 +2341,40 @@ def SetOptions(icon=None, button_color=None, element_size=(None,None), margins=( return True + +#################### ChangeLookAndFeel ####################### +# Predefined settings that will change the colors and styles # +# of the elements. # +############################################################## +def ChangeLookAndFeel(index): + # look and feel table + look_and_feel = {'GreenTan': {'BACKGROUND' : '#9FB8AD', 'TEXT': COLOR_SYSTEM_DEFAULT, 'INPUT':'#F7F3EC', 'BUTTON': ('white', '#475841'), + 'PROGRESS':DEFAULT_PROGRESS_BAR_COLOR}, + + 'LightGreen' :{'BACKGROUND' : '#B7CECE', 'TEXT': 'black', 'INPUT':'#FDFFF7', 'BUTTON': ('white', '#658268'), 'PROGRESS':('#247BA0','#F8FAF0')}, + + 'BluePurple': {'BACKGROUND' : '#A5CADD', 'TEXT': '#6E266E', 'INPUT':'#E0F5FF', 'BUTTON': ('white', '#303952'),'PROGRESS':DEFAULT_PROGRESS_BAR_COLOR}} + try: + colors = look_and_feel[index] + + SetOptions(background_color=colors['BACKGROUND'], + text_element_background_color=colors['BACKGROUND'], + element_background_color=colors['BACKGROUND'], + text_color=colors['TEXT'], + input_elements_background_color=colors['INPUT'], + button_color=colors['BUTTON'], + progress_meter_color=colors['PROGRESS'], + border_width=0, + slider_border_width=0, + progress_meter_border_depth=0, + scrollbar_color=(colors['INPUT']), + element_text_color=colors['TEXT']) + except: # most likely an index out of range + pass + + + + # ============================== sprint ======# # Is identical to the Scrolled Text Box # # Provides a crude 'print' mechanism but in a # @@ -2443,12 +2396,12 @@ def ObjToString(obj, extra=' '): def main(): - with FlexForm('Demo form..', auto_size_text=True) as form: + with FlexForm('Demo form..') as form: form_rows = [[Text('You are running the PySimpleGUI.py file itself')], [Text('You should be importing it rather than running it\n')], [Text('Here is your sample input form....')], - [Text('Source Folder', size=(15, 1), auto_size_text=False, justification='right'), InputText('Source', focus=True),FolderBrowse()], - [Text('Destination Folder', size=(15, 1), auto_size_text=False, justification='right'), InputText('Dest'), FolderBrowse()], + [Text('Source Folder', size=(15, 1), justification='right'), InputText('Source', focus=True),FolderBrowse()], + [Text('Destination Folder', size=(15, 1), justification='right'), InputText('Dest'), FolderBrowse()], [Submit(bind_return_key=True), Cancel()]] button, (source, dest) = form.LayoutAndRead(form_rows)