From 7295d34df83a33d60b26c67274bf4dbb6946f34e Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Thu, 16 Aug 2018 14:16:06 -0400 Subject: [PATCH 1/9] Release of Column support --- Demo_Columns.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Demo_Columns.py diff --git a/Demo_Columns.py b/Demo_Columns.py new file mode 100644 index 00000000..ededa3f6 --- /dev/null +++ b/Demo_Columns.py @@ -0,0 +1,24 @@ +import PySimpleGUI as sg + +# Demo of how columns work +# Form has on row 1 a vertical slider followed by a COLUMN with 7 rows +# Prior to the Column element, this layout was not possible +# Columns layouts look identical to form layouts, they are a list of lists of elements. + +# sg.ChangeLookAndFeel('BlueMono') + +# Column layout +col = [[sg.Text('col Row 1', text_color='white', background_color='blue')], + [sg.Text('col Row 2', text_color='white', background_color='blue'), sg.Input('col input 1')], + [sg.Text('col Row 3', text_color='white', background_color='blue'), sg.Input('col input 2')]] + +layout = [[sg.Listbox(values=('Listbox Item 1', 'Listbox Item 2', 'Listbox Item 3'), select_mode=sg.LISTBOX_SELECT_MODE_MULTIPLE, size=(20,3)), sg.Column(col, background_color='blue')], + [sg.Input('Last input')], + [sg.OK()]] + +# Display the form and get values +# If you're willing to not use the "context manager" design pattern, then it's possible +# to collapse the form display and read down to a single line of code. +button, values = sg.FlexForm('Compact 1-line form with column').LayoutAndRead(layout) + +sg.MsgBox(button, values, line_width=200) From b0393723471358729a1bb225a8a6a0bd20fca2a9 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Thu, 16 Aug 2018 17:07:45 -0400 Subject: [PATCH 2/9] Columns, one-line GUI --- docs/cookbook.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/docs/cookbook.md b/docs/cookbook.md index 15cb14a3..4b86c222 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -535,4 +535,61 @@ Perhaps you don't want all the statistics that the EasyProgressMeter provides an ---- + ## The One-Line GUI +For those of you into super-compact code, a complete customized GUI can be specified, shown, and received the results using a single line of Python code. The way this is done is to combine the call to `FlexForm` and the call to `LayoutAndRead`. `FlexForm` returns a `FlexForm` object which has the `LayoutAndRead` method. + + +![simple](https://user-images.githubusercontent.com/13696193/44227935-ecb53b80-a161-11e8-968b-b3f963404dec.jpg) + + +Instead of + + import PySimpleGUI as sg + + layout = [[sg.Text('Filename')], + [sg.Input(), sg.FileBrowse()], + [sg.OK(), sg.Cancel()] ] + + button, (number,) = sg.FlexForm('Get filename example').LayoutAndRead(layout) + +you can write this line of code for the exact same result (OK, two lines with the import): + + import PySimpleGUI as sg + + button, (filename,) = sg.FlexForm('Get filename example'). LayoutAndRead([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ]) +-------------------- +## Multiple Columns +Starting in version 2.9 (not yet released but you can get from current GitHub) you can use the Column Element. A Column is required when you have a tall element to the left of smaller elements. + +This example uses a Column. There is a Listbox on the left that is 3 rows high. To the right of it are 3 single rows of text and input. These 3 rows are in a Column Element. + +To make it easier to see the Column in the window, the Column background has been shaded blue. The code is wordier than normal due to the blue shading. Each element in the column needs to have the color set to match blue background. + +![snap0202](https://user-images.githubusercontent.com/13696193/44234671-27749f00-a175-11e8-9e66-a3fccf6c077e.jpg) + + + import PySimpleGUI as sg + + # Demo of how columns work + # Form has on row 1 a vertical slider followed by a COLUMN with 7 rows + # Prior to the Column element, this layout was not possible + # Columns layouts look identical to form layouts, they are a list of lists of elements. + + # sg.ChangeLookAndFeel('BlueMono') + + # Column layout + col = [[sg.Text('col Row 1', text_color='white', background_color='blue')], + [sg.Text('col Row 2', text_color='white', background_color='blue'), sg.Input('col input 1')], + [sg.Text('col Row 3', text_color='white', background_color='blue'), sg.Input('col input 2')]] + + layout = [[sg.Listbox(values=('Listbox Item 1', 'Listbox Item 2', 'Listbox Item 3'), select_mode=sg.LISTBOX_SELECT_MODE_MULTIPLE, size=(20,3)), sg.Column(col, background_color='blue')], + [sg.Input('Last input')], + [sg.OK()]] + + # Display the form and get values + # If you're willing to not use the "context manager" design pattern, then it's possible + # to collapse the form display and read down to a single line of code. + button, values = sg.FlexForm('Compact 1-line form with column').LayoutAndRead(layout) + + sg.MsgBox(button, values, line_width=200) From 6b8729af4c230f299adaa6a1fab8a58e44e9167a Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Thu, 16 Aug 2018 17:12:25 -0400 Subject: [PATCH 3/9] Header typo --- docs/cookbook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cookbook.md b/docs/cookbook.md index 4b86c222..61e581c0 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -535,7 +535,7 @@ Perhaps you don't want all the statistics that the EasyProgressMeter provides an ---- - ## The One-Line GUI +## The One-Line GUI For those of you into super-compact code, a complete customized GUI can be specified, shown, and received the results using a single line of Python code. The way this is done is to combine the call to `FlexForm` and the call to `LayoutAndRead`. `FlexForm` returns a `FlexForm` object which has the `LayoutAndRead` method. From 5b78f655bfef921d967c181e59ccd633f5456d34 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Thu, 16 Aug 2018 18:17:13 -0400 Subject: [PATCH 4/9] Version 2.9 --- PySimpleGUI.py | 386 +++---------------------------------------------- docs/index.md | 11 +- readme.md | 11 +- 3 files changed, 34 insertions(+), 374 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] diff --git a/docs/index.md b/docs/index.md index 392287c2..c22c0e5a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ # PySimpleGUI - (Ver 2.8) + (Ver 2.9) [Formatted ReadTheDocs Version of this Readme](http://pysimplegui.readthedocs.io/) @@ -1655,7 +1655,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it | 2.6.0 | July 27, 2018 - auto_size_button setting. License changed to LGPL 3+ | 2.7.0 | July 30, 2018 - realtime buttons, window_location default setting | 2.8.0 | Aug 9, 2018 - New None default option for Checkbox element, text color option for all elements, return values as a dictionary, setting focus, binding return key -| 2.9.0 | Aug XX,2018 - Screen flash fix, `do_not_clear` input field option, `autosize_text` defaults to `True` now, return values as ordered dict, removed text target from progress bar, rework of return values and initial return values, removed legacy Form.Refresh() method (replaced by Form.ReadNonBlockingForm()), COLUMN elements!!, +| 2.9.0 | Aug 16,2018 - Screen flash fix, `do_not_clear` input field option, `autosize_text` defaults to `True` now, return values as ordered dict, removed text target from progress bar, rework of return values and initial return values, removed legacy Form.Refresh() method (replaced by Form.ReadNonBlockingForm()), COLUMN elements!!, colored text defaults ### Release Notes @@ -1670,14 +1670,15 @@ New debug printing capability. `sg.Print` Listboxes are still without scrollwheels. The mouse can drag to see more items. The mouse scrollwheel will also scroll the list and will `page up` and `page down` keys. 2.7 Is the "feature complete" release. Pretty much all features are done and in the code + 2.8 More text color controls. The caller has more control over things like the focus and what buttons should be clicked when enter key is pressed. Return values as a dictionary! (NICE addition) +2.9 COLUMNS! This is the biggest feature and had the biggest impact on the code base. It was a difficult feature to add, but it was worth it. Can now make even more layouts. Almost any layout is possible with this addition. + ### Upcoming Make suggestions people! Future release features -Columns. How multiple columns would be specified in the SDK interface are still being designed. - Port to other graphic engines. Hook up the front-end interface to a backend other than tkinter. Qt, WxPython, etc. @@ -1738,7 +1739,7 @@ Here are the steps to run that application To run it: Python HowDoI.py -The pip command is all there is to the setup. +The pip command is all there is to the setup. The way HowDoI works is that it uses your search term to look through stack overflow posts. It finds the best answer, gets the code from the answer, and presents it as a response. It gives you the correct answer OFTEN. It's a miracle that it work SO well. For Python questions, I simply start my query with 'Python'. Let's say you forgot how to reverse a list in Python. When you run HowDoI and ask this question, this is what you'll see. diff --git a/readme.md b/readme.md index 392287c2..c22c0e5a 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ # PySimpleGUI - (Ver 2.8) + (Ver 2.9) [Formatted ReadTheDocs Version of this Readme](http://pysimplegui.readthedocs.io/) @@ -1655,7 +1655,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it | 2.6.0 | July 27, 2018 - auto_size_button setting. License changed to LGPL 3+ | 2.7.0 | July 30, 2018 - realtime buttons, window_location default setting | 2.8.0 | Aug 9, 2018 - New None default option for Checkbox element, text color option for all elements, return values as a dictionary, setting focus, binding return key -| 2.9.0 | Aug XX,2018 - Screen flash fix, `do_not_clear` input field option, `autosize_text` defaults to `True` now, return values as ordered dict, removed text target from progress bar, rework of return values and initial return values, removed legacy Form.Refresh() method (replaced by Form.ReadNonBlockingForm()), COLUMN elements!!, +| 2.9.0 | Aug 16,2018 - Screen flash fix, `do_not_clear` input field option, `autosize_text` defaults to `True` now, return values as ordered dict, removed text target from progress bar, rework of return values and initial return values, removed legacy Form.Refresh() method (replaced by Form.ReadNonBlockingForm()), COLUMN elements!!, colored text defaults ### Release Notes @@ -1670,14 +1670,15 @@ New debug printing capability. `sg.Print` Listboxes are still without scrollwheels. The mouse can drag to see more items. The mouse scrollwheel will also scroll the list and will `page up` and `page down` keys. 2.7 Is the "feature complete" release. Pretty much all features are done and in the code + 2.8 More text color controls. The caller has more control over things like the focus and what buttons should be clicked when enter key is pressed. Return values as a dictionary! (NICE addition) +2.9 COLUMNS! This is the biggest feature and had the biggest impact on the code base. It was a difficult feature to add, but it was worth it. Can now make even more layouts. Almost any layout is possible with this addition. + ### Upcoming Make suggestions people! Future release features -Columns. How multiple columns would be specified in the SDK interface are still being designed. - Port to other graphic engines. Hook up the front-end interface to a backend other than tkinter. Qt, WxPython, etc. @@ -1738,7 +1739,7 @@ Here are the steps to run that application To run it: Python HowDoI.py -The pip command is all there is to the setup. +The pip command is all there is to the setup. The way HowDoI works is that it uses your search term to look through stack overflow posts. It finds the best answer, gets the code from the answer, and presents it as a response. It gives you the correct answer OFTEN. It's a miracle that it work SO well. For Python questions, I simply start my query with 'Python'. Let's say you forgot how to reverse a list in Python. When you run HowDoI and ask this question, this is what you'll see. From 99ae29cd77a3f46e990b1179f303cbb9044f30db Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 16:35:49 -0400 Subject: [PATCH 5/9] Tutorial checkin --- docs/tutorial.md | 253 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 docs/tutorial.md diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 00000000..51cb73c5 --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,253 @@ +# 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 likely 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()], + [sg.Text('Address', size=(15, 1)), sg.InputText()], + [sg.Text('Phone', size=(15, 1)), sg.InputText()], + [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 7392e06cea016678d0e670c63eb576c8dd7101de Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 16:45:55 -0400 Subject: [PATCH 6/9] Fix formatting --- docs/tutorial.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/tutorial.md b/docs/tutorial.md index 51cb73c5..30fab516 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -228,8 +228,10 @@ That may seem like a lot of code, but try coding this same GUI layout directly i ![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. From 9a6661954a4cf40bdb202a5712bd1e035ffd24d9 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 18 Aug 2018 21:06:45 -0400 Subject: [PATCH 7/9] Typos --- docs/tutorial.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 30fab516..edc6b619 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -12,7 +12,7 @@ PySimpleGUI attempts to address these GUI challenges by providing a super-simple ## 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 likely a stretch. +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. @@ -48,9 +48,9 @@ Let's look at the first recipe from the book layout = [ [sg.Text('Please enter your Name, Address, Phone')], - [sg.Text('Name', size=(15, 1)), sg.InputText()], - [sg.Text('Address', size=(15, 1)), sg.InputText()], - [sg.Text('Phone', size=(15, 1)), sg.InputText()], + [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()] ] From beebcbab0c8594e0d8abd8b601dd259ed0a422cf Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sun, 19 Aug 2018 20:59:08 -0400 Subject: [PATCH 8/9] Turned off 2 debug print statements, incomplete keyboard feature Also has some code for Keyboard handling, but it's incomplete --- PySimpleGUI.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 685075f7..d20da4b4 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -102,11 +102,11 @@ class MyWindows(): def Decrement(self): self.NumOpenWindows -= 1 * (self.NumOpenWindows != 0) # decrement if not 0 - print('---- DECREMENTING Num Open Windows = {} ---'.format(self.NumOpenWindows)) + # print('---- DECREMENTING Num Open Windows = {} ---'.format(self.NumOpenWindows)) def Increment(self): self.NumOpenWindows += 1 - print('++++ INCREMENTING Num Open Windows = {} ++++'.format(self.NumOpenWindows)) + # print('++++ INCREMENTING Num Open Windows = {} ++++'.format(self.NumOpenWindows)) _my_windows = MyWindows() # terrible hack using globals... means need a class for collecing windows @@ -862,7 +862,7 @@ class FlexForm: ''' Display a user defined for and return the filled in data ''' - def __init__(self, title, default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), auto_size_text=None, auto_size_buttons=None, scale=(None, None), location=(None, None), button_color=None, font=None, progress_bar_color=(None, None), background_color=None, is_tabbed_form=False, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=DEFAULT_WINDOW_ICON): + def __init__(self, title, default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), auto_size_text=None, auto_size_buttons=None, scale=(None, None), location=(None, None), button_color=None, font=None, progress_bar_color=(None, None), background_color=None, is_tabbed_form=False, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=DEFAULT_WINDOW_ICON, return_keyboard_events=False): self.AutoSizeText = auto_size_text if auto_size_text is not None else DEFAULT_AUTOSIZE_TEXT self.AutoSizeButtons = auto_size_buttons if auto_size_buttons is not None else DEFAULT_AUTOSIZE_BUTTONS self.Title = title @@ -896,6 +896,7 @@ class FlexForm: self.LastButtonClicked = None self.UseDictionary = False self.UseDefaultFocus = False + self.ReturnKeyboardEvents = return_keyboard_events # ------------------------- Add ONE Row to Form ------------------------- # def AddRow(self, *args): @@ -1013,6 +1014,8 @@ class FlexForm: _my_windows.Decrement() return BuildResults(self, False, self) + def KeyboardCallback(self, event ): + print("pressed", event) def _Close(self): try: @@ -1765,6 +1768,8 @@ def StartupTK(my_flex_form): # root.bind('', MyFlexForm.DestroyedCallback()) ConvertFlexToTK(my_flex_form) my_flex_form.SetIcon(my_flex_form.WindowIcon) + if my_flex_form.ReturnKeyboardEvents: + root.bind("", my_flex_form.KeyboardCallback) if my_flex_form.AutoClose: duration = DEFAULT_AUTOCLOSE_TIME if my_flex_form.AutoCloseDuration is None else my_flex_form.AutoCloseDuration From 3af033122b04c521aad19d8430d88f97c90093fb Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sun, 19 Aug 2018 21:05:59 -0400 Subject: [PATCH 9/9] 5-line GUI added --- docs/tutorial.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index edc6b619..ab1e798b 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -59,13 +59,31 @@ Let's look at the first recipe from the book print(button, values[0], values[1], values[2]) -It's a reasonable sized form. +It's a reasonably 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. +## The 5-line GUI + +Not all GUIs take 5 minutes. Some take 5 lines of code. This is a GUI with a custom layout contained in 5 lines of code. + + import PySimpleGUI as sg + + form = sg.FlexForm('My first GUI') + + layout = [ [sg.Text('Enter your name'), sg.InputText()], + [sg.OK()] ] + + button, (name,) = form.LayoutAndRead(layout) + + +![myfirstgui](https://user-images.githubusercontent.com/13696193/44315412-d2918c80-a3f1-11e8-9eda-0d5d9bfefb0f.jpg) + + + ## 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.