From cf9b11e75c316d8164eba0ca97938a8bf1630b06 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 16:52:24 -0400 Subject: [PATCH 1/5] Pulling down current Master version --- PySimpleGUI.py | 386 +++---------------------------------------------- 1 file changed, 22 insertions(+), 364 deletions(-) diff --git a/PySimpleGUI.py b/PySimpleGUI.py index dda7640a..925130a0 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -269,7 +269,7 @@ class Listbox(Element): ''' Listbox Element :param values: - :param select_mode: + :param select_mode: SELECT_MODE_BROWSE, SELECT_MODE_EXTENDED, SELECT_MODE_MULTIPLE, SELECT_MODE_SINGLE :param font: :param scale: Adds multiplier to size (w,h) :param size: Size of field in characters @@ -791,6 +791,7 @@ class Column(Element): self.Rows = [] self.ParentForm = None self.TKFrame = None + bg = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR self.Layout(layout) @@ -1058,11 +1059,11 @@ class UberForm(): # ====================================================================== # # ------------------------- INPUT TEXT Element lazy functions ------------------------- # -def In(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None, do_not_clear=False, focus=False, key=None): - return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text, do_not_clear = do_not_clear, focus=focus, key=key) +def In(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None, password_char='', background_color=None, text_color=None, do_not_clear=False, key=None, focus=False): + return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text, password_char=password_char, background_color=background_color, text_color=text_color, do_not_clear=do_not_clear, focus=focus, key=key) -def Input(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None, do_not_clear = False, focus=False, key=None): - return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text, do_not_clear=do_not_clear, focus=focus, key=key) +def Input(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None, password_char='', background_color=None, text_color=None, do_not_clear=False, key=None, focus=False): + return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text, password_char=password_char, background_color=background_color, text_color=text_color, do_not_clear=do_not_clear, focus=focus, key=key) # ------------------------- INPUT COMBO Element lazy functions ------------------------- # def Combo(values, scale=(None, None), size=(None, None), auto_size_text=None, background_color=None): @@ -1311,7 +1312,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): col_frame = tk.Frame(tk_row_frame) PackFormIntoFrame(element, col_frame, toplevel_form) col_frame.pack(side=tk.LEFT) - if element.BackgroundColor is not None: + if element.BackgroundColor != COLOR_SYSTEM_DEFAULT and element.BackgroundColor is not None: col_frame.configure(background=element.BackgroundColor, highlightbackground=element.BackgroundColor, highlightcolor=element.BackgroundColor) # ------------------------- TEXT element ------------------------- # elif element_type == ELEM_TYPE_TEXT: @@ -1630,359 +1631,6 @@ def ConvertFlexToTK(MyFlexForm): master.update_idletasks() # don't forget return -def ConvertFlexToTKOld(MyFlexForm): - def CharWidthInPixels(): - return tkinter.font.Font().measure('A') # single character width - master = MyFlexForm.TKroot - # only set title on non-tabbed forms - if not MyFlexForm.IsTabbedForm: - master.title(MyFlexForm.Title) - font = MyFlexForm.Font - InitializeResults(MyFlexForm) - border_depth = MyFlexForm.BorderDepth if MyFlexForm.BorderDepth is not None else DEFAULT_BORDER_WIDTH - # --------------------------------------------------------------------------- # - # **************** Use FlexForm to build the tkinter window ********** ----- # - # Building is done row by row. # - # --------------------------------------------------------------------------- # - focus_set = False - ######################### LOOP THROUGH ROWS ######################### - # *********** ------- Loop through ROWS ------- ***********# - for row_num, flex_row in enumerate(MyFlexForm.Rows): - ######################### LOOP THROUGH ELEMENTS ON ROW ######################### - # *********** ------- Loop through ELEMENTS ------- ***********# - # *********** Make TK Row ***********# - tk_row_frame = tk.Frame(master) - for col_num, element in enumerate(flex_row): - element.ParentForm = MyFlexForm # save the button's parent form object - if MyFlexForm.Font and (element.Font == DEFAULT_FONT or not element.Font): - font = MyFlexForm.Font - elif element.Font is not None: - font = element.Font - # ------- Determine Auto-Size setting on a cascading basis ------- # - if element.AutoSizeText is not None: # if element overide - auto_size_text = element.AutoSizeText - elif MyFlexForm.AutoSizeText is not None: # if form override - auto_size_text = MyFlexForm.AutoSizeText - else: - auto_size_text = DEFAULT_AUTOSIZE_TEXT - # Determine Element size - element_size = element.Size - if (element_size == (None, None)): # user did not specify a size - element_size = MyFlexForm.DefaultElementSize - else: auto_size_text = False # if user has specified a size then it shouldn't autosize - # Apply scaling... Element scaling is higher priority than form level - if element.Scale != (None, None): - element_size = (int(element_size[0] * element.Scale[0]), int(element_size[1] * element.Scale[1])) - elif MyFlexForm.Scale != (None, None): - element_size = (int(element_size[0] * MyFlexForm.Scale[0]), int(element_size[1] * MyFlexForm.Scale[1])) - # Set foreground color - text_color = element.TextColor - # ------------------------- TEXT element ------------------------- # - element_type = element.Type - if element_type == ELEM_TYPE_TEXT: - display_text = element.DisplayText # text to display - if auto_size_text is False: - width, height=element_size - else: - lines = display_text.split('\n') - max_line_len = max([len(l) for l in lines]) - num_lines = len(lines) - if max_line_len > element_size[0]: # if text exceeds element size, the will have to wrap - width = element_size[0] - else: - width=max_line_len - height=num_lines - # ---===--- LABEL widget create and place --- # - stringvar = tk.StringVar() - element.TKStringVar = stringvar - stringvar.set(display_text) - if auto_size_text: - width = 0 - justify = tk.LEFT if element.Justification == 'left' else tk.CENTER if element.Justification == 'center' else tk.RIGHT - anchor = tk.NW if element.Justification == 'left' else tk.N if element.Justification == 'center' else tk.NE - tktext_label = tk.Label(tk_row_frame, textvariable=stringvar, width=width, height=height, justify=justify, bd=border_depth) - # tktext_label = tk.Label(tk_row_frame,anchor=tk.NW, text=display_text, width=width, height=height, justify=tk.LEFT, bd=border_depth) - # Set wrap-length for text (in PIXELS) == PAIN IN THE ASS - wraplen = tktext_label.winfo_reqwidth() # width of widget in Pixels - tktext_label.configure(anchor=anchor, font=font, wraplen=wraplen*2 ) # set wrap to width of widget - if element.BackgroundColor is not None: - tktext_label.configure(background=element.BackgroundColor) - if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None: - tktext_label.configure(fg=element.TextColor) - - tktext_label.pack(side=tk.LEFT) - # ------------------------- BUTTON element ------------------------- # - elif element_type == ELEM_TYPE_BUTTON: - element.Location = (row_num, col_num) - btext = element.ButtonText - btype = element.BType - if element.AutoSizeButton is not None: - auto_size = element.AutoSizeButton - else: auto_size = MyFlexForm.AutoSizeButtons - if auto_size is False: width=element_size[0] - else: width = 0 - height=element_size[1] - lines = btext.split('\n') - max_line_len = max([len(l) for l in lines]) - num_lines = len(lines) - if element.ButtonColor != (None, None)and element.ButtonColor != DEFAULT_BUTTON_COLOR: - bc = element.ButtonColor - elif MyFlexForm.ButtonColor != (None, None) and MyFlexForm.ButtonColor != DEFAULT_BUTTON_COLOR: - bc = MyFlexForm.ButtonColor - else: - bc = DEFAULT_BUTTON_COLOR - border_depth = element.BorderWidth - if btype != BUTTON_TYPE_REALTIME: - tkbutton = tk.Button(tk_row_frame, text=btext, width=width, height=height,command=element.ButtonCallBack, justify=tk.LEFT, bd=border_depth) - else: - tkbutton = tk.Button(tk_row_frame, text=btext, width=width, height=height, justify=tk.LEFT, bd=border_depth) - tkbutton.bind('', element.ButtonReleaseCallBack) - tkbutton.bind('', element.ButtonPressCallBack) - if bc != (None, None) and bc != COLOR_SYSTEM_DEFAULT: - tkbutton.config(foreground=bc[0], background=bc[1]) - element.TKButton = tkbutton # not used yet but save the TK button in case - wraplen = tkbutton.winfo_reqwidth() # width of widget in Pixels - if element.ImageFilename: # if button has an image on it - photo = tk.PhotoImage(file=element.ImageFilename) - if element.ImageSize != (None, None): - width, height = element.ImageSize - if element.ImageSubsample: - photo = photo.subsample(element.ImageSubsample) - else: - width, height = photo.width(), photo.height() - tkbutton.config(image=photo, width=width, height=height) - tkbutton.image = photo - tkbutton.configure(wraplength=wraplen+10, font=font) # set wrap to width of widget - tkbutton.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) - if element.Focus is True or (MyFlexForm.UseDefaultFocus and not focus_set): - focus_set = True - element.TKButton.bind('', element.ReturnKeyHandler) - element.TKButton.focus_set() - MyFlexForm.TKroot.focus_force() - # ------------------------- INPUT (Single Line) element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_TEXT: - default_text = element.DefaultText - element.TKStringVar = tk.StringVar() - element.TKStringVar.set(default_text) - show = element.PasswordCharacter if element.PasswordCharacter else "" - element.TKEntry = tk.Entry(tk_row_frame, width=element_size[0], textvariable=element.TKStringVar, bd=border_depth, font=font, show=show) - element.TKEntry.bind('', element.ReturnKeyHandler) - if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - element.TKEntry.configure(background=element.BackgroundColor) - if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: - element.TKEntry.configure(fg=text_color) - element.TKEntry.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) - if element.Focus is True or (MyFlexForm.UseDefaultFocus and not focus_set): - focus_set = True - element.TKEntry.focus_set() - # ------------------------- COMBO BOX (Drop Down) element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_COMBO: - max_line_len = max([len(str(l)) for l in element.Values]) - if auto_size_text is False: width=element_size[0] - else: width = max_line_len - element.TKStringVar = tk.StringVar() - if element.BackgroundColor and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - combostyle = ttk.Style() - try: - combostyle.theme_create('combostyle', - settings={'TCombobox': - {'configure': - {'selectbackground': element.BackgroundColor, - 'fieldbackground': element.BackgroundColor, - 'foreground': text_color, - 'background': element.BackgroundColor} - }}) - except: - try: - combostyle.theme_settings('combostyle', - settings={'TCombobox': - {'configure': - {'selectbackground': element.BackgroundColor, - 'fieldbackground': element.BackgroundColor, - 'foreground': text_color, - 'background': element.BackgroundColor} - }}) - except: pass - # ATTENTION: this applies the new style 'combostyle' to all ttk.Combobox - combostyle.theme_use('combostyle') - element.TKCombo = ttk.Combobox(tk_row_frame, width=width, textvariable=element.TKStringVar,font=font ) - # element.TKCombo['state']='readonly' - element.TKCombo['values'] = element.Values - # if element.BackgroundColor is not None: - # element.TKCombo.configure(background=element.BackgroundColor) - element.TKCombo.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) - element.TKCombo.current(0) - # ------------------------- LISTBOX element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_LISTBOX: - max_line_len = max([len(str(l)) for l in element.Values]) - if auto_size_text is False: width=element_size[0] - else: width = max_line_len - - element.TKStringVar = tk.StringVar() - element.TKListbox= tk.Listbox(tk_row_frame, height=element_size[1], width=width, selectmode=element.SelectMode, font=font) - for item in element.Values: - element.TKListbox.insert(tk.END, item) - element.TKListbox.selection_set(0,0) - if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - element.TKListbox.configure(background=element.BackgroundColor) - if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: - element.TKListbox.configure(fg=text_color) - # vsb = tk.Scrollbar(tk_row_frame, orient="vertical", command=element.TKListbox.yview) - # element.TKListbox.configure(yscrollcommand=vsb.set) - element.TKListbox.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) - # vsb.pack(side=tk.LEFT, fill='y') - # ------------------------- INPUT MULTI LINE element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_MULTILINE: - default_text = element.DefaultText - width, height = element_size - element.TKText = tk.scrolledtext.ScrolledText(tk_row_frame, width=width, height=height, wrap='word', bd=border_depth,font=font) - element.TKText.insert(1.0, default_text) # set the default text - if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - element.TKText.configure(background=element.BackgroundColor) - element.TKText.vbar.config(troughcolor=DEFAULT_SCROLLBAR_COLOR) - element.TKText.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) - if element.EnterSubmits: - element.TKText.bind('', element.ReturnKeyHandler) - if element.Focus is True or (MyFlexForm.UseDefaultFocus and not focus_set): - focus_set = True - element.TKText.focus_set() - if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: - element.TKText.configure(fg=text_color) - # ------------------------- INPUT CHECKBOX element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_CHECKBOX: - width = 0 if auto_size_text else element_size[0] - default_value = element.InitialState - element.TKIntVar = tk.IntVar() - element.TKIntVar.set(default_value if default_value is not None else 0) - element.TKCheckbutton = tk.Checkbutton(tk_row_frame, anchor=tk.NW, text=element.Text, width=width, variable=element.TKIntVar, bd=border_depth, font=font) - if default_value is None: - element.TKCheckbutton.configure(state='disable') - if element.BackgroundColor is not None: - element.TKCheckbutton.configure(background=element.BackgroundColor) - if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: - element.TKCheckbutton.configure(fg=text_color) - element.TKCheckbutton.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) - # ------------------------- PROGRESS BAR element ------------------------- # - elif element_type == ELEM_TYPE_PROGRESS_BAR: - # save this form because it must be 'updated' (refreshed) solely for the purpose of updating bar - width = element_size[0] - fnt = tkinter.font.Font() - char_width = fnt.measure('A') # single character width - progress_length = width*char_width - progress_width = element_size[1] - direction = element.Orientation - if element.BarColor != (None, None): # if element has a bar color, use it - 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, border_width=element.BorderWidth, relief=element.Relief, style=element.BarStyle ) - # element.TKProgressBar = TKProgressBar(tk_row_frame, element.MaxValue, progress_length, progress_width, orientation=direction, BarColor=bar_color, border_width=element.BorderWidth, relief=element.Relief) - element.TKProgressBar.TKProgressBarForReal.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) - # ------------------------- INPUT RADIO BUTTON element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_RADIO: - width = 0 if auto_size_text else element_size[0] - default_value = element.InitialState - ID = element.GroupID - # see if ID has already been placed - value = EncodeRadioRowCol(row_num, col_num) # value to set intvar to if this radio is selected - if ID in MyFlexForm.RadioDict: - RadVar = MyFlexForm.RadioDict[ID] - else: - RadVar = tk.IntVar() - MyFlexForm.RadioDict[ID] = RadVar - element.TKIntVar = RadVar # store the RadVar in Radio object - if default_value: # if this radio is the one selected, set RadVar to match - element.TKIntVar.set(value) - element.TKRadio = tk.Radiobutton(tk_row_frame, anchor=tk.NW, text=element.Text, width=width, - variable=element.TKIntVar, value=value, bd=border_depth, font=font) - if element.BackgroundColor is not None: - element.TKRadio.configure(background=element.BackgroundColor) - if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: - element.TKRadio.configure(fg=text_color) - element.TKRadio.pack(side=tk.LEFT, padx=element.Pad[0],pady=element.Pad[1]) - # ------------------------- INPUT SPIN Box element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_SPIN: - width, height = element_size - width = 0 if auto_size_text else element_size[0] - element.TKStringVar = tk.StringVar() - element.TKSpinBox = tk.Spinbox(tk_row_frame, values=element.Values, textvariable=element.TKStringVar, width=width, bd=border_depth) - element.TKStringVar.set(element.DefaultValue) - element.TKSpinBox.configure(font=font) # set wrap to width of widget - if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - element.TKSpinBox.configure(background=element.BackgroundColor) - element.TKSpinBox.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) - if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: - element.TKSpinBox.configure(fg=text_color) - # ------------------------- OUTPUT element ------------------------- # - elif element_type == ELEM_TYPE_OUTPUT: - width, height = element_size - element.TKOut = TKOutput(tk_row_frame, width=width, height=height, bd=border_depth, background_color=element.BackgroundColor, text_color=text_color) - element.TKOut.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) - # ------------------------- IMAGE Box element ------------------------- # - elif element_type == ELEM_TYPE_IMAGE: - photo = tk.PhotoImage(file=element.Filename) - if element_size == (None, None) or element_size == None or element_size == MyFlexForm.DefaultElementSize: - width, height = photo.width(), photo.height() - else: - width, height = element_size - tktext_label = tk.Label(tk_row_frame, image=photo, width=width, height=height, bd=border_depth) - tktext_label.image = photo - # tktext_label.configure(anchor=tk.NW, image=photo) - tktext_label.pack(side=tk.LEFT) - # ------------------------- SLIDER Box element ------------------------- # - elif element_type == ELEM_TYPE_INPUT_SLIDER: - slider_length = element_size[0] * CharWidthInPixels() - slider_width = element_size[1] - element.TKIntVar = tk.IntVar() - element.TKIntVar.set(element.DefaultValue) - if element.Orientation[0] == 'v': - range_from = element.Range[1] - range_to = element.Range[0] - else: - range_from = element.Range[0] - range_to = element.Range[1] - tkscale = tk.Scale(tk_row_frame, orient=element.Orientation, variable=element.TKIntVar, from_=range_from, to_=range_to, length=slider_length, width=slider_width , bd=element.BorderWidth, relief=element.Relief, font=font) - # tktext_label.configure(anchor=tk.NW, image=photo) - if element.BackgroundColor is not None: - tkscale.configure(background=element.BackgroundColor) - tkscale.config(troughcolor=DEFAULT_SCROLLBAR_COLOR) - if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: - tkscale.configure(fg=text_color) - tkscale.pack(side=tk.LEFT) - #............................DONE WITH ROW pack the row of widgets ..........................# - # done with row, pack the row of widgets - tk_row_frame.grid(row=row_num+2, sticky=tk.W, padx=DEFAULT_MARGINS[0]) - if MyFlexForm.BackgroundColor is not None: - tk_row_frame.configure(background=MyFlexForm.BackgroundColor) - if not MyFlexForm.IsTabbedForm: - MyFlexForm.TKroot.configure(padx=DEFAULT_MARGINS[0], pady=DEFAULT_MARGINS[1]) - else: MyFlexForm.ParentWindow.configure(padx=DEFAULT_MARGINS[0], pady=DEFAULT_MARGINS[1]) - #....................................... DONE creating and laying out window ..........................# - if MyFlexForm.IsTabbedForm: - master = MyFlexForm.ParentWindow - master.attributes('-alpha', 0) # hide window while getting info and moving - screen_width = master.winfo_screenwidth() # get window info to move to middle of screen - screen_height = master.winfo_screenheight() - if MyFlexForm.Location != (None, None): - x,y = MyFlexForm.Location - elif DEFAULT_WINDOW_LOCATION != (None, None): - x,y = DEFAULT_WINDOW_LOCATION - else: - master.update_idletasks() # don't forget - win_width = master.winfo_width() - win_height = master.winfo_height() - x = screen_width/2 -win_width/2 - y = screen_height/2 - win_height/2 - if y+win_height > screen_height: - y = screen_height-win_height - if x+win_width > screen_width: - x = screen_width-win_width - - move_string = '+%i+%i'%(int(x),int(y)) - master.geometry(move_string) - master.attributes('-alpha', 255) # Make window visible again - master.update_idletasks() # don't forget - return # ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----# def ShowTabbedForm(title, *args, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, fav_icon=DEFAULT_WINDOW_ICON): @@ -2815,13 +2463,23 @@ def ChangeLookAndFeel(index): 'GreenMono': {'BACKGROUND': '#A8C1B4', 'TEXT': 'black', 'INPUT': '#DDE0DE', 'SCROLL': '#E3E3E3','TEXT_INPUT' : 'black', 'BUTTON': ('white', '#6D9F85'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, - 'BrownBlue': {'BACKGROUND': '#64778d', 'TEXT': 'white', 'INPUT': '#f0f3f7', 'SCROLL': '#A6B2BE','TEXT_INPUT' : 'black', - 'BUTTON': ('white', '#283b5b'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, + 'BrownBlue': {'BACKGROUND': '#64778d', 'TEXT': 'white', 'INPUT': '#f0f3f7', 'SCROLL': '#A6B2BE','TEXT_INPUT' : 'black', 'BUTTON': ('white', '#283b5b'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, - 'BrightColors': {'BACKGROUND': '#b4ffb4', 'TEXT': 'black', 'INPUT': '#ffff64','SCROLL': '#ffb482','TEXT_INPUT' : 'black', - 'BUTTON': ('black', '#ffa0dc'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, + 'BrightColors': {'BACKGROUND': '#b4ffb4', 'TEXT': 'black', 'INPUT': '#ffff64','SCROLL': '#ffb482','TEXT_INPUT' : 'black', 'BUTTON': ('black', '#ffa0dc'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, - 'TealMono': {'BACKGROUND': '#a8cfdd', 'TEXT': 'black', 'INPUT': '#dfedf2','SCROLL': '#dfedf2', 'TEXT_INPUT' : 'black', 'BUTTON': ('white', '#3b7f97'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR} + 'NeutralBlue': {'BACKGROUND': '#92aa9d', 'TEXT': 'black', 'INPUT': '#fcfff6', + 'SCROLL': '#fcfff6', 'TEXT_INPUT': 'black', 'BUTTON': ('black', '#d0dbbd'), + 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, + + 'Kayak': {'BACKGROUND': '#a7ad7f', 'TEXT': 'black', 'INPUT': '#e6d3a8', + 'SCROLL': '#e6d3a8', 'TEXT_INPUT': 'black', 'BUTTON': ('white', '#5d907d'), + 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, + + 'SandyBeach': {'BACKGROUND': '#efeccb', 'TEXT': '#012f2f', 'INPUT': '#e6d3a8', + 'SCROLL': '#e6d3a8', 'TEXT_INPUT': '#012f2f', 'BUTTON': ('white', '#046380'), + 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR}, + + 'TealMono': {'BACKGROUND': '#a8cfdd', 'TEXT': 'black', 'INPUT': '#dfedf2','SCROLL': '#dfedf2', 'TEXT_INPUT' : 'black', 'BUTTON': ('white', '#183440'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR} } try: colors = look_and_feel[index] From 6ef5af67467ae77931a7a06c428ac3d41562ebf6 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 16:55:21 -0400 Subject: [PATCH 2/5] Fixes, listbox scroll bars, more button lazy funcs, Fixed output element scrollbar length Added scroll bar to listbox New FileSaveAs, SaveAs, Save, Exit button functions Fixed button width bug Fixed button outline around images on Raspberry Pi Set border width = 0 for sliders --- PySimpleGUI.py | 110 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 36 deletions(-) diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 925130a0..4c36aeec 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -100,6 +100,14 @@ class MyWindows(): self.NumOpenWindows = 0 self.user_defined_icon = None + def Decrement(self): + self.NumOpenWindows -= 1 * (self.NumOpenWindows != 0) # decrement if not 0 + print('---- DECREMENTING Num Open Windows = {} ---'.format(self.NumOpenWindows)) + + def Increment(self): + self.NumOpenWindows += 1 + print('++++ INCREMENTING Num Open Windows = {} ++++'.format(self.NumOpenWindows)) + _my_windows = MyWindows() # terrible hack using globals... means need a class for collecing windows # ====================================================================== # @@ -116,6 +124,7 @@ def RGB(red,green,blue): return '#%02x%02x%02x' % (red,green,blue) # class ButtonType(Enum): BUTTON_TYPE_BROWSE_FOLDER = 1 BUTTON_TYPE_BROWSE_FILE = 2 +BUTTON_TYPE_SAVEAS_FILE = 3 BUTTON_TYPE_CLOSES_WIN = 5 BUTTON_TYPE_READ_FORM = 7 BUTTON_TYPE_REALTIME = 9 @@ -261,7 +270,7 @@ class InputCombo(Element): # ---------------------------------------------------------------------- # -# Combo # +# Listbox # # ---------------------------------------------------------------------- # class Listbox(Element): @@ -514,16 +523,18 @@ class TKProgressBar(): # ---------------------------------------------------------------------- # class TKOutput(tk.Frame): def __init__(self, parent, width, height, bd, background_color=None, text_color=None): - tk.Frame.__init__(self, parent) - self.output = tk.Text(parent, width=width, height=height, bd=bd) + frame = tk.Frame(parent, width=width, height=height) + tk.Frame.__init__(self, frame) + self.output = tk.Text(frame, width=width, height=height, bd=bd) if background_color and background_color != COLOR_SYSTEM_DEFAULT: self.output.configure(background=background_color) if text_color and text_color != COLOR_SYSTEM_DEFAULT: self.output.configure(fg=text_color) - self.vsb = tk.Scrollbar(parent, orient="vertical", command=self.output.yview) + self.vsb = tk.Scrollbar(frame, orient="vertical", command=self.output.yview) self.output.configure(yscrollcommand=self.vsb.set) - self.output.pack(side="left", fill="both", expand=True) + self.output.pack(side="left", fill="both") self.vsb.pack(side="left", fill="y") + frame.pack(side="left") self.previous_stdout = sys.stdout self.previous_stderr = sys.stderr @@ -650,6 +661,9 @@ class Button(Element): elif self.BType == BUTTON_TYPE_BROWSE_FILE: file_name = tk.filedialog.askopenfilename(filetypes=filetypes) # show the 'get file' dialog box strvar.set(file_name) + elif self.BType == BUTTON_TYPE_SAVEAS_FILE: + file_name = tk.filedialog.asksaveasfilename(filetypes=filetypes) # show the 'get file' dialog box + strvar.set(file_name) elif self.BType == BUTTON_TYPE_CLOSES_WIN: # this is a return type button so GET RESULTS and destroy window # first, get the results table built # modify the Results table in the parent FlexForm object @@ -664,7 +678,7 @@ class Button(Element): self.ParentForm.TKroot.quit() if self.ParentForm.NonBlocking: self.ParentForm.TKroot.destroy() - # _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 + _my_windows.Decrement() elif self.BType == BUTTON_TYPE_READ_FORM: # LEAVE THE WINDOW OPEN!! DO NOT CLOSE # first, get the results table built # modify the Results table in the parent FlexForm object @@ -718,7 +732,7 @@ class ProgressBar(Element): try: self.ParentForm.TKroot.update() except: - # _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 + # _my_windows.Decrement() return False return True @@ -968,7 +982,7 @@ class FlexForm: self.TKroot.mainloop() if self.RootNeedsDestroying: self.TKroot.destroy() - _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 + _my_windows.Decrement() return BuildResults(self, False, self) def ReadNonBlocking(self, Message=''): @@ -982,7 +996,7 @@ class FlexForm: rc = self.TKroot.update() except: self.TKrootDestroyed = True - _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 + _my_windows.Decrement() return BuildResults(self, False, self) @@ -999,10 +1013,12 @@ class FlexForm: return None def CloseNonBlockingForm(self): + if self.TKrootDestroyed: + return try: self.TKroot.destroy() + _my_windows.Decrement() except: pass - _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 def OnClosingCallback(self): return @@ -1049,7 +1065,7 @@ class UberForm(): if not self.TKrootDestroyed: self.TKrootDestroyed = True self.TKroot.destroy() - _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 + _my_windows.Decrement() def __del__(self): return @@ -1082,24 +1098,36 @@ def T(display_text, scale=(None, None), size=(None, None), auto_size_text=None, return Text(display_text, scale=scale, size=size, auto_size_text=auto_size_text, font=font, text_color=text_color, justification=justification) # ------------------------- FOLDER BROWSE Element lazy function ------------------------- # -def FolderBrowse(target=(ThisRow, -1), button_text='Browse', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None): +def FolderBrowse(target=(ThisRow, -1), button_text='Browse', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, font=None): return Button(BUTTON_TYPE_BROWSE_FOLDER, target=target, button_text=button_text, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color) # ------------------------- FILE BROWSE Element lazy function ------------------------- # -def FileBrowse(target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), button_text='Browse', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None): - return Button(BUTTON_TYPE_BROWSE_FILE, target, button_text=button_text, file_types=file_types, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color) +def FileBrowse(target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), button_text='Browse', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, font=None): + return Button(BUTTON_TYPE_BROWSE_FILE, target, button_text=button_text, file_types=file_types, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font) + +# ------------------------- FILE BROWSE Element lazy function ------------------------- # +def FileSaveAs(target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), button_text='Save As...', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, font=None): + return Button(BUTTON_TYPE_SAVEAS_FILE, target, button_text=button_text, file_types=file_types, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font) + +# ------------------------- SAVE AS Element lazy function ------------------------- # +def SaveAs(target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), button_text='Save As...', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, font=None): + return Button(BUTTON_TYPE_BROWSE_FILE, target, button_text=button_text, file_types=file_types, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font) + +# ------------------------- SAVE BUTTON Element lazy function ------------------------- # +def Save(button_text='Save', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True,font=None, focus=False): + return Button(BUTTON_TYPE_CLOSES_WIN, 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) # ------------------------- SUBMIT BUTTON Element lazy function ------------------------- # -def Submit(button_text='Submit', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, focus=False): - return Button(BUTTON_TYPE_CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, bind_return_key=bind_return_key, focus=focus) +def Submit(button_text='Submit', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True,font=None, focus=False): + return Button(BUTTON_TYPE_CLOSES_WIN, 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) # ------------------------- OK BUTTON Element lazy function ------------------------- # -def OK(button_text='OK', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, focus=False): - return Button(BUTTON_TYPE_CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, bind_return_key=bind_return_key, focus=focus) +def OK(button_text='OK', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, font=None,focus=False): + return Button(BUTTON_TYPE_CLOSES_WIN, 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) # ------------------------- YES BUTTON Element lazy function ------------------------- # -def Ok(button_text='Ok', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, focus=False): - return Button(BUTTON_TYPE_CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, bind_return_key=bind_return_key, focus=focus) +def Ok(button_text='Ok', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, font=None,focus=False): + return Button(BUTTON_TYPE_CLOSES_WIN, 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) # ------------------------- CANCEL BUTTON Element lazy function ------------------------- # def Cancel(button_text='Cancel', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False): @@ -1109,13 +1137,17 @@ def Cancel(button_text='Cancel', scale=(None, None), size=(None, None), auto_siz def Quit(button_text='Quit', 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_CLOSES_WIN, 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) +# ------------------------- Exit BUTTON Element lazy function ------------------------- # +def Exit(button_text='Exit', 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_CLOSES_WIN, 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) + # ------------------------- YES BUTTON Element lazy function ------------------------- # -def Yes(button_text='Yes', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, focus=False): - return Button(BUTTON_TYPE_CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, bind_return_key=bind_return_key, focus=focus) +def Yes(button_text='Yes', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None,font=None, bind_return_key=True, focus=False): + return Button(BUTTON_TYPE_CLOSES_WIN, 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) # ------------------------- NO BUTTON Element lazy function ------------------------- # -def No(button_text='No', scale=(None, None), size=(None, None), auto_size_button=None, button_color=None, bind_return_key=False, focus=False): - return Button(BUTTON_TYPE_CLOSES_WIN, button_text=button_text, scale=scale, size=size, auto_size_button=auto_size_button, button_color=button_color, bind_return_key=bind_return_key, focus=focus) +def No(button_text='No', 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_CLOSES_WIN, 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) # ------------------------- GENERIC BUTTON Element lazy function ------------------------- # # this is the only button that REQUIRES button text field @@ -1378,6 +1410,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKButton = tkbutton # not used yet but save the TK button in case wraplen = tkbutton.winfo_reqwidth() # width of widget in Pixels if element.ImageFilename: # if button has an image on it + tkbutton.config(highlightthickness=0) photo = tk.PhotoImage(file=element.ImageFilename) if element.ImageSize != (None, None): width, height = element.ImageSize @@ -1387,7 +1420,10 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): width, height = photo.width(), photo.height() tkbutton.config(image=photo, width=width, height=height) tkbutton.image = photo - tkbutton.configure(wraplength=wraplen+10, font=font) # set wrap to width of widget + if width != 0: + tkbutton.configure(wraplength=wraplen+10, font=font) # set wrap to width of widget + else: + tkbutton.configure(font=font) # only set the font, not wraplength tkbutton.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) if element.Focus is True or (toplevel_form.UseDefaultFocus and not focus_set): focus_set = True @@ -1452,9 +1488,9 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): max_line_len = max([len(str(l)) for l in element.Values]) if auto_size_text is False: width=element_size[0] else: width = max_line_len - + listbox_frame = tk.Frame(tk_row_frame) element.TKStringVar = tk.StringVar() - element.TKListbox= tk.Listbox(tk_row_frame, height=element_size[1], width=width, selectmode=element.SelectMode, font=font) + element.TKListbox= tk.Listbox(listbox_frame, height=element_size[1], width=width, selectmode=element.SelectMode, font=font) for item in element.Values: element.TKListbox.insert(tk.END, item) element.TKListbox.selection_set(0,0) @@ -1462,10 +1498,11 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKListbox.configure(background=element.BackgroundColor) if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: element.TKListbox.configure(fg=text_color) - # vsb = tk.Scrollbar(tk_row_frame, orient="vertical", command=element.TKListbox.yview) - # element.TKListbox.configure(yscrollcommand=vsb.set) - element.TKListbox.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) - # vsb.pack(side=tk.LEFT, fill='y') + vsb = tk.Scrollbar(listbox_frame, orient="vertical", command=element.TKListbox.yview) + element.TKListbox.configure(yscrollcommand=vsb.set) + element.TKListbox.pack(side=tk.LEFT) + vsb.pack(side=tk.LEFT, fill='y') + listbox_frame.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) # ------------------------- INPUT MULTI LINE element ------------------------- # elif element_type == ELEM_TYPE_INPUT_MULTILINE: default_text = element.DefaultText @@ -1563,7 +1600,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): tktext_label = tk.Label(tk_row_frame, image=photo, width=width, height=height, bd=border_depth) tktext_label.image = photo # tktext_label.configure(anchor=tk.NW, image=photo) - tktext_label.pack(side=tk.LEFT) + tktext_label.pack(side=tk.LEFT, padx=element.Pad[0],pady=element.Pad[1]) # ------------------------- SLIDER Box element ------------------------- # elif element_type == ELEM_TYPE_INPUT_SLIDER: slider_length = element_size[0] * CharWidthInPixels() @@ -1579,12 +1616,13 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): range_to = element.Range[1] tkscale = tk.Scale(tk_row_frame, orient=element.Orientation, variable=element.TKIntVar, from_=range_from, to_=range_to, length=slider_length, width=slider_width , bd=element.BorderWidth, relief=element.Relief, font=font) # tktext_label.configure(anchor=tk.NW, image=photo) + tkscale.config(highlightthickness=0) if element.BackgroundColor is not None: tkscale.configure(background=element.BackgroundColor) tkscale.config(troughcolor=DEFAULT_SCROLLBAR_COLOR) if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT: tkscale.configure(fg=text_color) - tkscale.pack(side=tk.LEFT) + tkscale.pack(side=tk.LEFT, padx=element.Pad[0],pady=element.Pad[1]) #............................DONE WITH ROW pack the row of widgets ..........................# # done with row, pack the row of widgets tk_row_frame.grid(row=row_num+2, sticky=tk.NW, padx=DEFAULT_MARGINS[0]) @@ -1698,7 +1736,7 @@ def StartupTK(my_flex_form): root = tk.Tk() if not ow else tk.Toplevel() if my_flex_form.BackgroundColor is not None: root.configure(background=my_flex_form.BackgroundColor) - _my_windows.NumOpenWindows += 1 + _my_windows.Increment() my_flex_form.TKroot = root # root.protocol("WM_DELETE_WINDOW", MyFlexForm.DestroyedCallback()) @@ -1716,7 +1754,7 @@ def StartupTK(my_flex_form): my_flex_form.TKroot.mainloop() # print('..... BACK from MainLoop') if not my_flex_form.FormRemainedOpen: - _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 + _my_windows.Decrement() if my_flex_form.RootNeedsDestroying: my_flex_form.TKroot.destroy() my_flex_form.RootNeedsDestroying = False @@ -1992,8 +2030,8 @@ def _ProgressMeterUpdate(bar, value, text_elem, *args): bar.ParentForm._Close() if bar.ParentForm.RootNeedsDestroying: try: - _my_windows.NumOpenWindows -= 1 * (_my_windows.NumOpenWindows != 0) # decrement if not 0 bar.ParentForm.TKroot.destroy() + _my_windows.Decrement() except: pass bar.ParentForm.RootNeedsDestroying = False bar.ParentForm.__del__() From 405936978462d0fe66b6a5e75c234d03d13f1454 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 17:22:38 -0400 Subject: [PATCH 3/5] Initial Checkin --- Demo_All_Widgets.py | 65 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Demo_All_Widgets.py diff --git a/Demo_All_Widgets.py b/Demo_All_Widgets.py new file mode 100644 index 00000000..8dcf08ed --- /dev/null +++ b/Demo_All_Widgets.py @@ -0,0 +1,65 @@ +import PySimpleGUI as sg + + +def Everything(): + + with sg.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40, 1)) as form: + layout = [ + [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText()], + [sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything',size=(35,3)), + sg.Multiline(default_text='A second multi-line',size=(35,3))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 3)), + sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3', 'Listbox 4'), size=(30, 3)), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), + sg.Spin(values=('Spin Box 1', '2','3'), initial_value='Spin Box 1')], + [sg.Text('_' * 80)], + [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Default Folder'), sg.FolderBrowse()], + [sg.Submit(), sg.Cancel()] ] + + button, values = form.LayoutAndRead(layout) + + sg.MsgBox('Title', 'The results of the form.', 'The button clicked was "{}"'.format(button), 'The values are', values) + +import PySimpleGUI as sg + +sg.ChangeLookAndFeel('GreenTan') + +form = sg.FlexForm('Everything bagel', default_element_size=(40, 1)) + +column1 = [[sg.Text('Column 1', background_color='#d3dfda', justification='center', size=(10,1))], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]] +layout = [ + [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText('This is my text')], + [sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), + sg.Multiline(default_text='A second multi-line', size=(35, 3))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 3)), + sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), + sg.Column(column1, background_color='#d3dfda')], + [sg.Text('_' * 80)], + [sg.Text('Choose A Folder', size=(35, 1))], + [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), + sg.InputText('Default Folder'), sg.FolderBrowse()], + [sg.Submit(), sg.Cancel()] + ] + +button, values = form.LayoutAndRead(layout) +sg.MsgBox(button, values) + +# Everything_NoContextManager() \ No newline at end of file From ea2b401801908247233a040a9b1f68a0e903dbce Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 21:07:09 -0400 Subject: [PATCH 4/5] Checkin to match master branch --- docs/tutorial.md | 255 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 docs/tutorial.md diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 00000000..edc6b619 --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,255 @@ +# Add GUIs to your programs and scripts easily with PySimpleGUI + +## Introduction +Python has dropped the GUI ball. While the rest of the world has been enjoying the use of a mouse, most Python programs continue to be accessed via the command line. Why is this, does anybody care, and what can be done about it? + +## GUI Frameworks +There is no shortage of GUI frameworks for Python. tkinter, WxPython, Qt, Kivy are a few of the major packages. In addition, there are a good number of dumbed down GUI packages that wrap one of the major packages. These include EasyGUI, PyGUI, Pyforms, ... + +The problem is that beginners (those with experience of less than 6 weeks) are not capable of learning even the simplest of the major packages. That leaves the wrapper-packages. Users will quickly find it difficult or impossible to build a custom GUI layout. Or, if it's possible, pages of code are still required. + +PySimpleGUI attempts to address these GUI challenges by providing a super-simple, easy to understand interface to GUIs that can be customized easily. Even the most complex of GUIs are often less than 20 lines of code when PySimpleGUI is used. + +## The Secret + +What makes PySimpleGUI superior for newcomers is that the package contains the majority of the code that the user is normally expected to write. Button callbacks are handled by PySimpleGUI, not the user's code. Beginners struggle to grasp the concept of a function, expecting them to understand a call-back function in the first few weeks is a stretch. + +With most GUIs arranging the GUI Widgets often requires several lines of code.... at least one or two lines per widget. PySimpleGUI uses an "auto-packer" that creates the layout for the user automatically. There is no concept of a pack nor a grid system needed to layout a GUI Window. + +Finally, PySimpleGUI leverages the Python language constructs in clever ways that shortens the amount of code and returns the GUI data in a straightforward manner. When a Widget is created in a form layout, it is configured in-place, not several lines of code away. + +## What is a GUI? + +Most GUIs do one thing.... they collect information from the user and return it. From a programmer's viewpoint this could be summed up as a function call that looks like this: + + button, values = GUI_Display(gui_layout) + +What's expected from most GUIs is the button that was clicked (OK, cancel, save, yes, no, etc), and the values that were input by the user. The essence of a GUI can be boiled down into a single line of code. + +This is exactly how PySimpleGUI works (for these simple kinds of GUIs). When the call is made to display the GUI, execution does no return until a button is clicked that closes the form. + +There are more complex GUIs such as those that don't close after a button is clicked. These complex forms can also be created with PySimpleGUI. A remote control interface for a robot and a chat window are a couple of examples. + +## The 5-Minute GUI + +When is PySimpleGUI useful? Immediately, anytime you've got a GUI need. It will take under 5 minutes for you to create and try your GUI. With those kinds of times, what do you have to lose trying it? + +The best way to go about making your GUI in under 5 minutes is to copy one of the GUIs from the [PySimpleGUI Cookbook](https://pysimplegui.readthedocs.io/en/latest/cookbook/). Follow these steps: +* Find a GUI that looks similar to what you want to create +* Copy code from Cookbook +* Paste into your IDE and run + +Let's look at the first recipe from the book + + import PySimpleGUI as sg + + # Very basic form. Return values as a list + form = sg.FlexForm('Simple data entry form') # begin with a blank form + + layout = [ + [sg.Text('Please enter your Name, Address, Phone')], + [sg.Text('Name', size=(15, 1)), sg.InputText('name')], + [sg.Text('Address', size=(15, 1)), sg.InputText('address')], + [sg.Text('Phone', size=(15, 1)), sg.InputText('phone')], + [sg.Submit(), sg.Cancel()] + ] + + button, values = form.LayoutAndRead(layout) + + print(button, values[0], values[1], values[2]) + + +It's a reasonable sized form. + + +![super simple 2](https://user-images.githubusercontent.com/13696193/43934091-8100e29a-9c1b-11e8-8d0a-9bd2d13e6d8e.jpg) + +If you only need to collect a few values and they're all basically strings, then you would copy this recipe and modify it to suit your needs. + +## Making Your Custom GUI + +That 5-minute estimate wasn't the time it takes to copy and paste the code from the Cookbook. You should be able to modify the code within 5 minutes in order to get to your layout, assuming you've got a straightforward layout. + +Widgets are called Elements in PySimpleGUI. This list of Elements are spelled exactly as you would type it into your Python code. + +### Core Element list +``` + Text + InputText + Multiline + InputCombo + Listbox + Radio + Checkbox + Spin + Output + SimpleButton + RealtimeButton + ReadFormButton + ProgressBar + Image + Slider + Column +``` + +You can also have short-cut Elements. There are 2 types of shortcuts. One is simply other names for the exact same element (e.g. T instead of Text). The second type configures an Element with particular setting, sparing the programmer from specifying all of the parameters (e.g. Submit is a button with the text "Submit" on it). +### Shortcut list + + T = Text + Txt = Text + In = InputText + Input = IntputText + Combo = InputCombo + DropDown = InputCombo + Drop = InputCombo + +A number of common buttons have been implemented as shortcuts. These include: +### Button Shortcuts + FolderBrowse + FileBrowse + FileSaveAs + Save + Submit + OK + Ok + Cancel + Quit + Exit + Yes + No + +The more generic button functions, that are also shortcuts +### Generic Buttons + SimpleButton + ReadFormButton + RealtimeButton + +These are all of the GUI Widgets you have to choose from. If it's not in this list, it doesn't go in your form layout. + +### GUI Design Pattern + +The stuff that tends not to change in GUIs are the calls that setup and show the Window. It's the layout of the Elements that changes from one program to another. This is the code from above with the layout removed: + + import PySimpleGUI as sg + + form = sg.FlexForm('Simple data entry form') + # Define your form here (it's a list of lists) + button, values = form.LayoutAndRead(layout) + + The flow for most GUIs is: + * Create the Form object + * Define GUI as a list of lists + * Show the GUI and get results + +These are line for line what you see in design pattern. + +### GUI Layout + +To create your custom GUI, first break your form down into "rows". You'll be defining your form one row at a time. Then for each for, you'll be placing one Element after another, working from left to right. + +The result is a "list of lists" that looks something like this: + + layout = [ [Text('Row 1')], + [Text('Row 2'), Checkbox('Checkbox 1', OK()), Checkbox('Checkbox 2'), OK()] ] + +The layout produced this window: + +![tutorial2](https://user-images.githubusercontent.com/13696193/44302312-e5259c00-a2f3-11e8-9c17-63e4eb130a9e.jpg) + + +## Display GUI & Get Results + +Once you have your layout complete and you've copied over the lines of code that setup and show the form, it's time to look at how to display the form and get the values from the user. + +This is the line of code that displays the form and provides the results: + + button, values = form.LayoutAndRead(layout) + + Forms return 2 values, the text of the button that was clicked and a ***list of values*** the user entered into the form. + +If the example form was displayed and the user did nothing other than click the OK button, then the results would have been: + + button == 'OK' + values == [False, False] + +Checkbox Elements return a value of True/False. Because these checkboxes defaulted to unchecked, the values returned were both False. + +## Displaying Results + +Once you have the values from the GUI it would be nice to check what values are in the variables. Rather than print them out using a `print` statement, let's stick with the GUI idea and output to a window. + +PySimpleGUI has a number of Message Boxes to choose from. The data passed to the message box will be displayed in a window. The function takes any number of arguments. Simply indicate all the variables you would like to see in the call. + +The most-commonly used Message Box in PySimpleGUI is MsgBox. To display the results of the previous example, one would write: + + MsgBox('The GUI returned:', button, values) + +## Putting It All Together + +Now that you know the basics, let's put together a form that contains as many PySimpleGUI's elements as possible. Also, just to give it a nice look, we'll change the "look and feel" to a green and tan color scheme. + + import PySimpleGUI as sg + + sg.ChangeLookAndFeel('GreenTan') + + form = sg.FlexForm('Everything bagel', default_element_size=(40, 1)) + + column1 = [[sg.Text('Column 1', background_color='#d3dfda', justification='center', size=(10,1))], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]] + layout = [ + [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText('This is my text')], + [sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), + sg.Multiline(default_text='A second multi-line', size=(35, 3))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 3)), + sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), + sg.Column(column1, background_color='#d3dfda')], + [sg.Text('_' * 80)], + [sg.Text('Choose A Folder', size=(35, 1))], + [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), + sg.InputText('Default Folder'), sg.FolderBrowse()], + [sg.Submit(), sg.Cancel()] + ] + + button, values = form.LayoutAndRead(layout) + sg.MsgBox(button, values) + +That may seem like a lot of code, but try coding this same GUI layout directly in tkinter and you'll quickly realize that the length is tiny. + +![everything for tutorial](https://user-images.githubusercontent.com/13696193/44302997-38531b00-a303-11e8-8c45-698ea62590a8.jpg) + +The last line of code opens a message box. This is how it looks: + +![tutorial results](https://user-images.githubusercontent.com/13696193/44303004-79e3c600-a303-11e8-8311-2f3726d364ad.jpg) + + +Each parameter to the message box call is displayed on a new line. There are actually 2 lines of text in the message box. The second line is very long and wrapped a number of times + +Take a moment and pair up the results values with the GUI to get an understanding of how results are created and returned. + +## Resources + +### Installation +Requires Python 3 + + pip install PySimpleGUI + +Works on all systems that run tkinter, including the Raspberry Pi + +### Documentation +[Main manual](https://pysimplegui.readthedocs.io/en/latest/) + +[Cookbook](https://pysimplegui.readthedocs.io/en/latest/cookbook/) + +### Home Page + +www.PySimpleGUI.com From a6d375f8a19c73ae4d42fbdb3c348384b5714627 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 23:19:11 -0400 Subject: [PATCH 5/5] New Image features - load from RAM, update with new image --- PySimpleGUI.py | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 4c36aeec..685075f7 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -747,7 +747,7 @@ class ProgressBar(Element): # Image # # ---------------------------------------------------------------------- # class Image(Element): - def __init__(self, filename, scale=(None, None), size=(None, None)): + def __init__(self, filename=None, data=None,scale=(None, None), size=(None, None)): ''' Image Element :param filename: @@ -755,9 +755,23 @@ class Image(Element): :param size: Size of field in characters ''' self.Filename = filename + self.Data = data + self.tktext_label = None + + if data is None and filename is None: + print('* Warning... no image specified in Image Element! *') super().__init__(ELEM_TYPE_IMAGE, scale=scale, size=size) return + def Update(self, filename=None, data=None): + if filename is not None: + image = tk.PhotoImage(file=filename) + elif data is not None: + image = tk.PhotoImage(data=data) + else: return + self.tktext_label.configure(image=image) + self.tktext_label.image = image + def __del__(self): super().__del__() @@ -835,7 +849,7 @@ class Column(Element): for element in row: element.__del__() try: - del(self.TKroot) + del(self.TKFrame) except: pass super().__del__() @@ -1592,15 +1606,23 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKOut.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1]) # ------------------------- IMAGE Box element ------------------------- # elif element_type == ELEM_TYPE_IMAGE: - photo = tk.PhotoImage(file=element.Filename) - if element_size == (None, None) or element_size == None or element_size == toplevel_form.DefaultElementSize: - width, height = photo.width(), photo.height() + if element.Filename is not None: + photo = tk.PhotoImage(file=element.Filename) + elif element.Data is not None: + photo = tk.PhotoImage(data=element.Data) else: - width, height = element_size - tktext_label = tk.Label(tk_row_frame, image=photo, width=width, height=height, bd=border_depth) - tktext_label.image = photo - # tktext_label.configure(anchor=tk.NW, image=photo) - tktext_label.pack(side=tk.LEFT, padx=element.Pad[0],pady=element.Pad[1]) + photo = None + print('*ERROR laying out form.... Image Element has no image specified*') + + if photo is not None: + if element_size == (None, None) or element_size == None or element_size == toplevel_form.DefaultElementSize: + width, height = photo.width(), photo.height() + else: + width, height = element_size + element.tktext_label = tk.Label(tk_row_frame, image=photo, width=width, height=height, bd=border_depth) + element.tktext_label.image = photo + # tktext_label.configure(anchor=tk.NW, image=photo) + element.tktext_label.pack(side=tk.LEFT, padx=element.Pad[0],pady=element.Pad[1]) # ------------------------- SLIDER Box element ------------------------- # elif element_type == ELEM_TYPE_INPUT_SLIDER: slider_length = element_size[0] * CharWidthInPixels()