From 30be0e3276dbec8595e8623f867830af48c9f68e Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Mon, 3 Sep 2018 13:47:17 -0400 Subject: [PATCH 1/3] Tight layout Recipe --- docs/cookbook.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/docs/cookbook.md b/docs/cookbook.md index ad4f453f..d623d5e0 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -843,3 +843,66 @@ While there is no official support for "Tables" (e.g. there is no Table Element) layout.append([sg.T(f'{i}{j}', size=(4,1), background_color='white', pad=(1,1)) for j in range(10)]) sg.FlexForm('Table').LayoutAndRead(layout) + +## Tight Layout + +Saw this example layout written in tkinter and liked it so much I duplicated the interface. It's "tight", clean, and has a nice dark look and feel. + +This Recipe also contains code that implements the button interactions so that you'll have a template to build from. + +In other GUI frameworks this program would be most likely "event driven" with callback functions being used to communicate button events. The "event loop" would be handled by the GUI engine. If code already existed that used a call-back mechanism, the loop in the example code below could simply call these callback functions directly based on the button text it receives in the form.Read call. + +![timemanagement](https://user-images.githubusercontent.com/13696193/44996818-0f27c100-af78-11e8-8836-9ef6164efe3b.jpg) + + + import PySimpleGUI as sg + + sg.ChangeLookAndFeel('Dark') + sg.SetOptions(element_padding=(0, 0)) + + StartButton = sg.ReadFormButton('Start', button_color=('white', 'black')) + StopButton = sg.ReadFormButton('Stop', button_color=('gray34', 'black')) + ResetButton = sg.ReadFormButton('Reset', button_color=('gray', 'firebrick3')) + SubmitButton = sg.ReadFormButton('Submit', button_color=('gray34', 'springgreen4')) + + layout = [ + [sg.T('User:', pad=((3, 0), 0)), sg.OptionMenu(values=('User 1', 'User 2'), size=(20, 1)), sg.T('0', size=(8, 1))], + [sg.T('Customer:', pad=((3, 0), 0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20, 1)), + sg.T('1', size=(8, 1))], + [sg.T('Notes:', pad=((3, 0), 0)), sg.In(size=(44, 1), background_color='white', text_color='black')], + [StartButton, StopButton, ResetButton, SubmitButton] + ] + + form = sg.FlexForm("Time Tracker", default_element_size=(12, 1), text_justification='r', auto_size_text=False, + auto_size_buttons=False, + default_button_element_size=(12, 1)) + form.Layout(layout) + recording = have_data = False + while True: + button, values = form.Read() + if button is None: + exit(69) + if button is 'Start': + StartButton.Update(button_color=('gray34', 'black')) + StopButton.Update(button_color=('white', 'black')) + ResetButton.Update(button_color=('white', 'firebrick3')) + recording = True + elif button is 'Stop' and recording: + StopButton.Update(button_color=('gray34', 'black')) + StartButton.Update(button_color=('white', 'black')) + SubmitButton.Update(button_color=('white', 'springgreen4')) + recording = False + have_data = True + elif button is 'Reset': + StopButton.Update(button_color=('gray34', 'black')) + StartButton.Update(button_color=('white', 'black')) + SubmitButton.Update(button_color=('gray34', 'springgreen4')) + ResetButton.Update(button_color=('gray34', 'firebrick3')) + recording = False + have_data = False + elif button is 'Submit' and have_data: + StopButton.Update(button_color=('gray34', 'black')) + StartButton.Update(button_color=('white', 'black')) + SubmitButton.Update(button_color=('gray34', 'springgreen4')) + ResetButton.Update(button_color=('gray34', 'firebrick3')) + recording = False From c7a695d2a66d29d3ea8899b8df0823aea6fa85a7 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Mon, 3 Sep 2018 14:20:22 -0400 Subject: [PATCH 2/3] Changed button update method so can change only color if desired, tweaks to scrollable columns, fixed bug in size of OptionMeny element, initial check-in of image viewer demo by Jorj --- Demo_Img_Viewer.py | 115 +++++++++++++++++++++++++++++++++++++++++++++ PySimpleGUI.py | 14 ++++-- 2 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 Demo_Img_Viewer.py diff --git a/Demo_Img_Viewer.py b/Demo_Img_Viewer.py new file mode 100644 index 00000000..f09dc561 --- /dev/null +++ b/Demo_Img_Viewer.py @@ -0,0 +1,115 @@ +import PySimpleGUI as sg +import os +from PIL import Image, ImageTk +import io +""" +Simple Image Browser based on PySimpleGUI +-------------------------------------------- +There are some improvements compared to the PNG browser of the repository: +1. Paging is cyclic, i.e. automatically wraps around if file index is outside +2. Supports all file types that are valid PIL images +3. Limits the maximum form size to the physical screen +4. When selecting an image from the listbox, subsequent paging uses its index +5. Paging performance improved significantly because of using PIL + +Dependecies +------------ +Python v3 +PIL +""" +# Get the folder containing the images from the user +rc, folder = sg.GetPathBox('Image Browser', 'Image folder to open', default_path='') +if not rc or not folder: + sg.MsgBoxCancel('Cancelling') + raise SystemExit() + +# PIL supported image types +img_types = (".png", ".jpg", "jpeg", ".tiff", ".bmp") + +# get list of files in folder +flist0 = os.listdir(folder) + +# create sub list of image files (no sub folders, no wrong file types) +fnames = [f for f in flist0 if os.path.isfile(os.path.join(folder,f)) and f.lower().endswith(img_types)] + +num_files = len(fnames) # number of iamges found +if num_files == 0: + sg.MsgBox('No files in folder') + raise SystemExit() + +del flist0 # no longer needed + +#------------------------------------------------------------------------------ +# use PIL to read data of one image +#------------------------------------------------------------------------------ +def get_img_data(f, maxsize = (1200, 850), first = False): + """Generate image data using PIL + """ + img = Image.open(f) + img.thumbnail(maxsize) + if first: # tkinter is inactive the first time + bio = io.BytesIO() + img.save(bio, format = "PNG") + del img + return bio.getvalue() + return ImageTk.PhotoImage(img) +#------------------------------------------------------------------------------ + + +# create the form that also returns keyboard events +form = sg.FlexForm('Image Browser', return_keyboard_events=True, + location=(0, 0), use_default_focus=False) + +# make these 2 elements outside the layout as we want to "update" them later +# initialize to the first file in the list +filename = os.path.join(folder, fnames[0]) # name of first file in list +image_elem = sg.Image(data = get_img_data(filename, first = True)) +filename_display_elem = sg.Text(filename, size=(80, 3)) +file_num_display_elem = sg.Text('File 1 of {}'.format(num_files), size=(15,1)) + +# define layout, show and read the form +col = [[filename_display_elem], + [image_elem]] + +col_files = [[sg.Listbox(values = fnames, select_submits=True, size=(60,30), key='listbox')], + [sg.ReadFormButton('Next', size=(8,2)), sg.ReadFormButton('Prev', + size=(8,2)), file_num_display_elem]] + +layout = [[sg.Column(col_files), sg.Column(col)]] + +form.Layout(layout) # Shows form on screen + +# loop reading the user input and displaying image, filename +i=0 +while True: + # read the form + button, values = form.Read() + + # perform button and keyboard operations + if button is None: + break + elif button in ('Next', 'MouseWheel:Down', 'Down:40', 'Next:34'): + i += 1 + if i >= num_files: + i -= num_files + filename = os.path.join(folder, fnames[i]) + elif button in ('Prev', 'MouseWheel:Up', 'Up:38', 'Prior:33'): + i -= 1 + if i < 0: + i = num_files + i + filename = os.path.join(folder, fnames[i]) + elif button in ('Read', ''): # something from the listbox + f = values["listbox"][0] # selected filename + filename = os.path.join(folder, f) # read this file + i = fnames.index(f) # update running index + else: + filename = os.path.join(folder, fnames[i]) + + # update window with new image + image_elem.Update(data=get_img_data(filename)) + # update window with filename + filename_display_elem.Update(filename) + # update page display + file_num_display_elem.Update('File {} of {}'.format(i+1, num_files)) + + diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 7438608a..17ce24ea 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -866,9 +866,10 @@ class Button(Element): _my_windows.Decrement() return - def Update(self, new_text, button_color=(None, None)): + def Update(self, new_text=None, button_color=(None, None)): try: - self.TKButton.configure(text=new_text) + if new_text is not None: + self.TKButton.configure(text=new_text) if button_color != (None, None): self.TKButton.config(foreground=button_color[0], background=button_color[1]) except: @@ -1618,6 +1619,7 @@ def BuildResults(form, initialize_only, top_level_form): form.ReturnValuesDictionary = {} form.ReturnValuesList = [] BuildResultsForSubform(form, initialize_only, top_level_form) + top_level_form.LastButtonClicked = None return form.ReturnValues def BuildResultsForSubform(form, initialize_only, top_level_form): @@ -1801,7 +1803,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): PackFormIntoFrame(element, col_frame.TKFrame, toplevel_form) col_frame.TKFrame.update() if element.Size == (None, None): - col_frame.canvas.config(width=col_frame.TKFrame.winfo_reqwidth(),height=col_frame.TKFrame.winfo_reqheight()) + col_frame.canvas.config(width=col_frame.TKFrame.winfo_reqwidth()/2,height=col_frame.TKFrame.winfo_reqheight()/2) else: col_frame.canvas.config(width=element.Size[0],height=element.Size[1]) @@ -1971,11 +1973,13 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # ------------------------- OPTION MENU (Like ComboBox but different) element ------------------------- # elif element_type == ELEM_TYPE_INPUT_OPTION_MENU: 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() default = element.DefaultValue if element.DefaultValue else element.Values[0] element.TKStringVar.set(default) - element.TKOptionMenu = tk.OptionMenu(tk_row_frame, element.TKStringVar ,*element.Values ) - element.TKOptionMenu.config(highlightthickness=0, font=font) + element.TKOptionMenu = tk.OptionMenu(tk_row_frame, element.TKStringVar ,*element.Values) + element.TKOptionMenu.config(highlightthickness=0, font=font, width=width ) element.TKOptionMenu.config(borderwidth=border_depth) if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: element.TKOptionMenu.configure(background=element.BackgroundColor) From 29b6e4d202e1fcd34e237ad7463ba413c3f5ea4d Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Mon, 3 Sep 2018 14:32:58 -0400 Subject: [PATCH 3/3] Resolving different brach --- Demo_Img_Viewer.py | 115 --------------------------------------------- 1 file changed, 115 deletions(-) delete mode 100644 Demo_Img_Viewer.py diff --git a/Demo_Img_Viewer.py b/Demo_Img_Viewer.py deleted file mode 100644 index f09dc561..00000000 --- a/Demo_Img_Viewer.py +++ /dev/null @@ -1,115 +0,0 @@ -import PySimpleGUI as sg -import os -from PIL import Image, ImageTk -import io -""" -Simple Image Browser based on PySimpleGUI --------------------------------------------- -There are some improvements compared to the PNG browser of the repository: -1. Paging is cyclic, i.e. automatically wraps around if file index is outside -2. Supports all file types that are valid PIL images -3. Limits the maximum form size to the physical screen -4. When selecting an image from the listbox, subsequent paging uses its index -5. Paging performance improved significantly because of using PIL - -Dependecies ------------- -Python v3 -PIL -""" -# Get the folder containing the images from the user -rc, folder = sg.GetPathBox('Image Browser', 'Image folder to open', default_path='') -if not rc or not folder: - sg.MsgBoxCancel('Cancelling') - raise SystemExit() - -# PIL supported image types -img_types = (".png", ".jpg", "jpeg", ".tiff", ".bmp") - -# get list of files in folder -flist0 = os.listdir(folder) - -# create sub list of image files (no sub folders, no wrong file types) -fnames = [f for f in flist0 if os.path.isfile(os.path.join(folder,f)) and f.lower().endswith(img_types)] - -num_files = len(fnames) # number of iamges found -if num_files == 0: - sg.MsgBox('No files in folder') - raise SystemExit() - -del flist0 # no longer needed - -#------------------------------------------------------------------------------ -# use PIL to read data of one image -#------------------------------------------------------------------------------ -def get_img_data(f, maxsize = (1200, 850), first = False): - """Generate image data using PIL - """ - img = Image.open(f) - img.thumbnail(maxsize) - if first: # tkinter is inactive the first time - bio = io.BytesIO() - img.save(bio, format = "PNG") - del img - return bio.getvalue() - return ImageTk.PhotoImage(img) -#------------------------------------------------------------------------------ - - -# create the form that also returns keyboard events -form = sg.FlexForm('Image Browser', return_keyboard_events=True, - location=(0, 0), use_default_focus=False) - -# make these 2 elements outside the layout as we want to "update" them later -# initialize to the first file in the list -filename = os.path.join(folder, fnames[0]) # name of first file in list -image_elem = sg.Image(data = get_img_data(filename, first = True)) -filename_display_elem = sg.Text(filename, size=(80, 3)) -file_num_display_elem = sg.Text('File 1 of {}'.format(num_files), size=(15,1)) - -# define layout, show and read the form -col = [[filename_display_elem], - [image_elem]] - -col_files = [[sg.Listbox(values = fnames, select_submits=True, size=(60,30), key='listbox')], - [sg.ReadFormButton('Next', size=(8,2)), sg.ReadFormButton('Prev', - size=(8,2)), file_num_display_elem]] - -layout = [[sg.Column(col_files), sg.Column(col)]] - -form.Layout(layout) # Shows form on screen - -# loop reading the user input and displaying image, filename -i=0 -while True: - # read the form - button, values = form.Read() - - # perform button and keyboard operations - if button is None: - break - elif button in ('Next', 'MouseWheel:Down', 'Down:40', 'Next:34'): - i += 1 - if i >= num_files: - i -= num_files - filename = os.path.join(folder, fnames[i]) - elif button in ('Prev', 'MouseWheel:Up', 'Up:38', 'Prior:33'): - i -= 1 - if i < 0: - i = num_files + i - filename = os.path.join(folder, fnames[i]) - elif button in ('Read', ''): # something from the listbox - f = values["listbox"][0] # selected filename - filename = os.path.join(folder, f) # read this file - i = fnames.index(f) # update running index - else: - filename = os.path.join(folder, fnames[i]) - - # update window with new image - image_elem.Update(data=get_img_data(filename)) - # update window with filename - filename_display_elem.Update(filename) - # update page display - file_num_display_elem.Update('File {} of {}'.format(i+1, num_files)) - -