Removed all externally created elements - replaced with calls to form.FindElement. Many recipes updated to new preferred design patterns

This commit is contained in:
MikeTheWatchGuy 2018-09-10 12:37:45 -04:00
parent 9f831a9f9c
commit a8f674a44a
1 changed files with 237 additions and 211 deletions

View File

@ -115,87 +115,102 @@ Browse to get 2 file names that can be then compared. Uses a context manager
--------------- ---------------
## Nearly All Widgets with Green Color Theme with Context Manager ## Nearly All Widgets with Green Color Theme with Context Manager
Example of nearly all of the widgets in a single form. Uses a customized color scheme. This recipe uses a context manager, the preferred method. Example of nearly all of the widgets in a single form. Uses a customized color scheme. This recipe uses a context manager, ***the preferred method***.
![green everything](https://user-images.githubusercontent.com/13696193/43937043-7d0794be-9c29-11e8-8591-31373ddd5c34.jpg) ![cookbook all elements](https://user-images.githubusercontent.com/13696193/45308495-9bddfc00-b4ef-11e8-9c6a-32edf6b1d925.jpg)
# Green & tan color scheme
sg.SetOptions(background_color='#9FB8AD',
text_element_background_color='#9FB8AD',
element_background_color='#9FB8AD',
input_elements_background_color='#F7F3EC',
button_color=('white', '#475841'),
border_width=0,
slider_border_width=0,
progress_meter_border_depth=0,
scrollbar_color='#F7F3EC')
with sg.FlexForm('Everything bagel', 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(), sg.SimpleButton('Customized', button_color=('black', '#EDE5B7'))]]
button, values = form.LayoutAndRead(layout)
-------------
### All Widgets No Context Manager
![green everything](https://user-images.githubusercontent.com/13696193/43937043-7d0794be-9c29-11e8-8591-31373ddd5c34.jpg)
import PySimpleGUI as sg import PySimpleGUI as sg
# Green & tan color scheme sg.ChangeLookAndFeel('GreenTan')
sg.SetOptions(background_color='#9FB8AD',
text_element_background_color='#9FB8AD',
element_background_color='#9FB8AD',
input_elements_background_color='#F7F3EC',
button_color=('white', '#475841'),
border_width=0,
slider_border_width=0,
progress_meter_border_depth=0,
scrollbar_color='#F7F3EC')
form = sg.FlexForm('Everything bagel', default_element_size=(40, 1)) with sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False) as form:
layout = [
column1 = [[sg.Text('Column 1', background_color='#F7F3EC', 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('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.Text('Here is some text.... and a place to enter text')],
[sg.InputText('This is my text')], [sg.InputText('This is my text')],
[sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)], [sg.Checkbox('Checkbox'), sg.Checkbox('My second checkbox!', default=True)],
[sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], [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='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.Multiline(default_text='A second multi-line', size=(35, 3))],
[sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 3)), [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)),
sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)],
[sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))],
[sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), [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=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=75),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), 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.Column(column1, background_color='#F7F3EC')],
[sg.Text('_' * 80)], [sg.Text('_' * 80)],
[sg.Text('Choose A Folder', size=(35, 1))], [sg.Text('Choose A Folder', size=(35, 1))],
[sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'),
sg.InputText('Default Folder'), sg.FolderBrowse()], sg.InputText('Default Folder'), sg.FolderBrowse()],
[sg.Submit(), sg.Cancel(), sg.SimpleButton('Customized', button_color=('white', '#7E6C92'))] [sg.Submit(), sg.Cancel()]
] ]
button, values = form.LayoutAndRead(layout)
-------------
### All Widgets No Context Manager
Same form as above with no Context Manager. Use this pattern when you are in a hurry, don't have time to do things right, or it's throw-away code.... the legit use of this design is when you have non-blocking forms that update the form far away in the code from the form creation. Perhaps you show your form, get some input, then down in your event loop you want to execute another read or have an event loop where form.ReadNonBlocking is called.
Turning your form into forms that use a Contact Manage is quite easy. Change your FlexForm call from
form = sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False)
to
with sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False) as form:
Be sure and place the with statement at the top of your GUI code and indent you code under it.
import PySimpleGUI as sg
sg.ChangeLookAndFeel('GreenTan')
form = sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False)
column1 = [[sg.Text('Column 1', background_color='#F7F3EC', 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('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, 1)),
sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)],
[sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))],
[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='#F7F3EC')],
[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) button, values = form.LayoutAndRead(layout)
---- -----------
## Non-Blocking Form With Periodic Update ## Non-Blocking Form With Periodic Update
An async form that has a button read loop. A Text Element is updated periodically with a running timer. There is no context manager for this recipe because the loop that reads the form is likely to be some distance away from where the form was initialized. An async form that has a button read loop. A Text Element is updated periodically with a running timer. There is no context manager for this recipe because the loop that reads the form is likely to be some distance away from where the form was initialized.
@ -206,10 +221,8 @@ An async form that has a button read loop. A Text Element is updated periodical
form = sg.FlexForm('Running Timer') form = sg.FlexForm('Running Timer')
# create a text element that will be updated periodically # create a text element that will be updated periodically
text_element = sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center') form_rows = [[sg.Text('Stopwatch', size=(20, 2), justification='center')],
[sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center', key='output')],
form_rows = [[sg.Text('Stopwatch', size=(20,2), justification='center')],
[text_element],
[sg.T(' ' * 5), sg.ReadFormButton('Start/Stop', focus=True), sg.Quit()]] [sg.T(' ' * 5), sg.ReadFormButton('Start/Stop', focus=True), sg.Quit()]]
form.LayoutAndRead(form_rows, non_blocking=True) form.LayoutAndRead(form_rows, non_blocking=True)
@ -220,42 +233,19 @@ An async form that has a button read loop. A Text Element is updated periodical
while True: while True:
i += 1 * (timer_running is True) i += 1 * (timer_running is True)
button, values = form.ReadNonBlocking() button, values = form.ReadNonBlocking()
if values is None or button == 'Quit': # if user closed the window using X or clicked Quit button if values is None or button == 'Quit': # if user closed the window using X or clicked Quit button
break break
elif button == 'Start/Stop': elif button == 'Start/Stop':
timer_running = not timer_running timer_running = not timer_running
text_element.Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100))
form.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100))
time.sleep(.01) time.sleep(.01)
# if the loop finished then need to close the form for the user
form.CloseNonBlockingForm()
del (form)
----
## Async Form (Non-Blocking) with Context Manager
Like the previous recipe, this form is an async form. The difference is that this form uses a context manager.
![non-blocking 2](https://user-images.githubusercontent.com/13696193/43955456-4d5d9ef8-9c6e-11e8-8598-80dddf8eba6f.jpg)
import PySimpleGUI as sg
import time
with sg.FlexForm('Running Timer') as form: --------
text_element = sg.Text('', size=(10, 2), font=('Helvetica', 20), text_color='red', justification='center')
layout = [[sg.Text('Non blocking GUI with updates', justification='center')],
[text_element],
[sg.T(' ' * 15), sg.Quit()]]
form.LayoutAndRead(layout, non_blocking=True)
for i in range(1, 500):
text_element.Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100))
button, values = form.ReadNonBlocking()
if values is None or button == 'Quit': # if user closed the window using X
break
time.sleep(.01)
else:
# if the loop finished then need to close the form for the user
form.CloseNonBlockingForm()
----
## Callback Function Simulation ## Callback Function Simulation
The architecture of some programs works better with button callbacks instead of handling in-line. While button callbacks are part of the PySimpleGUI implementation, they are not directly exposed to the caller. The way to get the same result as callbacks is to simulate them with a recipe like this one. The architecture of some programs works better with button callbacks instead of handling in-line. While button callbacks are part of the PySimpleGUI implementation, they are not directly exposed to the caller. The way to get the same result as callbacks is to simulate them with a recipe like this one.
@ -389,15 +379,12 @@ Buttons can have PNG of GIF images on them. This Media Player recipe requires 4
image_next = './ButtonGraphics/Next.png' image_next = './ButtonGraphics/Next.png'
image_exit = './ButtonGraphics/Exit.png' image_exit = './ButtonGraphics/Exit.png'
# A text element that will be changed to display messages in the GUI
TextElem = sg.Text('', size=(15, 2), font=("Helvetica", 14))
# Open a form, note that context manager can't be used generally speaking for async forms # Open a form, note that context manager can't be used generally speaking for async forms
form = sg.FlexForm('Media File Player', auto_size_text=True, default_element_size=(20, 1), form = sg.FlexForm('Media File Player', auto_size_text=True, default_element_size=(20, 1),
font=("Helvetica", 25)) font=("Helvetica", 25))
# define layout of the rows # define layout of the rows
layout = [[sg.Text('Media File Player', size=(17, 1), font=("Helvetica", 25))], layout = [[sg.Text('Media File Player', size=(17, 1), font=("Helvetica", 25))],
[TextElem], [sg.Text('', size=(15, 2), font=("Helvetica", 14), key='output')],
[sg.ReadFormButton('Restart Song', button_color=(background, background), [sg.ReadFormButton('Restart Song', button_color=(background, background),
image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0), image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0),
sg.Text(' ' * 2), sg.Text(' ' * 2),
@ -437,7 +424,7 @@ Buttons can have PNG of GIF images on them. This Media Player recipe requires 4
break break
# If a button was pressed, display it on the GUI by updating the text element # If a button was pressed, display it on the GUI by updating the text element
if button: if button:
TextElem.Update(button) form.FindElement('output).Update(button)
---- ----
## Script Launcher - Persistent Form ## Script Launcher - Persistent Form
This form doesn't close after button clicks. To achieve this the buttons are specified as `sg.ReadFormButton` instead of `sg.SimpleButton`. The exception to this is the EXIT button. Clicking it will close the form. This program will run commands and display the output in the scrollable window. This form doesn't close after button clicks. To achieve this the buttons are specified as `sg.ReadFormButton` instead of `sg.SimpleButton`. The exception to this is the EXIT button. Clicking it will close the form. This program will run commands and display the output in the scrollable window.
@ -495,15 +482,7 @@ A standard non-blocking GUI with lots of inputs.
import PySimpleGUI as sg import PySimpleGUI as sg
# Green & tan color scheme # Green & tan color scheme
sg.SetOptions(background_color='#9FB8AD', sg.ChangeLookAndFeel('GreenTan')
text_element_background_color='#9FB8AD',
element_background_color='#9FB8AD',
input_elements_background_color='#F7F3EC',
button_color=('white', '#475841'),
border_width=0,
slider_border_width=0,
progress_meter_border_depth=0,
scrollbar_color='#F7F3EC')
sg.SetOptions(text_justification='right') sg.SetOptions(text_justification='right')
@ -539,28 +518,26 @@ Perhaps you don't want all the statistics that the EasyProgressMeter provides an
import PySimpleGUI as sg import PySimpleGUI as sg
def CustomMeter(): # layout the form
# create the progress bar element layout = [[sg.Text('A custom progress meter')],
progress_bar = sg.ProgressBar(10000, orientation='h', size=(20,20)) [sg.ProgressBar(10000, orientation='h', size=(20, 20), key='progbar')],
# layout the form [sg.Cancel()]]
layout = [[sg.Text('A custom progress meter')],
[progress_bar],
[sg.Cancel()]]
# create the form # create the form
form = sg.FlexForm('Custom Progress Meter') form = sg.FlexForm('Custom Progress Meter')
# display the form as a non-blocking form # display the form as a non-blocking form
form.LayoutAndRead(layout, non_blocking=True) form.LayoutAndRead(layout, non_blocking=True)
# loop that would normally do something useful # loop that would normally do something useful
for i in range(10000): for i in range(10000):
# check to see if the cancel button was clicked and exit loop if clicked # check to see if the cancel button was clicked and exit loop if clicked
button, values = form.ReadNonBlocking() button, values = form.ReadNonBlocking()
if button == 'Cancel' or values == None: if button == 'Cancel' or values == None:
break break
# update bar with loop value +1 so that bar eventually reaches the maximum # update bar with loop value +1 so that bar eventually reaches the maximum
progress_bar.UpdateBar(i+1) form.FindElement('progbar').UpdateBar(i + 1)
# done with loop... need to destroy the window as it's still open # done with loop... need to destroy the window as it's still open
form.CloseNonBlockingForm() form.CloseNonBlockingForm()
---- ----
@ -595,7 +572,8 @@ This example uses a Column. There is a Listbox on the left that is 3 rows high.
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. 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) ![cookbook columns](https://user-images.githubusercontent.com/13696193/45309948-f6c52280-b4f2-11e8-8691-a45fa0e06c50.jpg)
import PySimpleGUI as sg import PySimpleGUI as sg
@ -636,13 +614,12 @@ This simple program keep a form open, taking input values until the user termina
form = sg.FlexForm('Math') form = sg.FlexForm('Math')
output = sg.Txt('', size=(8,1))
layout = [ [sg.Txt('Enter values to calculate')], layout = [ [sg.Txt('Enter values to calculate')],
[sg.In(size=(8,1), key='numerator')], [sg.In(size=(8,1), key='numerator')],
[sg.Txt('_' * 10)], [sg.Txt('_' * 10)],
[sg.In(size=(8,1), key='denominator')], [sg.In(size=(8,1), key='denominator')],
[output], [sg.Txt('', size=(8,1), key='output') ],
[sg.ReadFormButton('Calculate', bind_return_key=True)]] [sg.ReadFormButton('Calculate', bind_return_key=True)]]
form.Layout(layout) form.Layout(layout)
@ -658,7 +635,7 @@ This simple program keep a form open, taking input values until the user termina
except: except:
calc = 'Invalid' calc = 'Invalid'
output.Update(calc) form.FindElement('output').Update(calc)
else: else:
break break
@ -676,31 +653,28 @@ The Canvas Element is one of the few tkinter objects that are directly accessibl
![canvas](https://user-images.githubusercontent.com/13696193/44632429-5266ac00-a948-11e8-9ee0-664103c40178.jpg) ![canvas](https://user-images.githubusercontent.com/13696193/44632429-5266ac00-a948-11e8-9ee0-664103c40178.jpg)
import PySimpleGUI as sg
import PySimpleGUI as gui
canvas = gui.Canvas(size=(100,100), background_color='red')
layout = [ layout = [
[canvas], [sg.Canvas(size=(100, 100), background_color='red', key= 'canvas')],
[gui.T('Change circle color to:'), gui.ReadFormButton('Red'), gui.ReadFormButton('Blue')] [sg.T('Change circle color to:'), sg.ReadFormButton('Red'), sg.ReadFormButton('Blue')]
] ]
form = gui.FlexForm('Canvas test') form = sg.FlexForm('Canvas test')
form.Layout(layout) form.Layout(layout)
form.ReadNonBlocking() form.ReadNonBlocking()
canvas = form.FindElement('canvas')
cir = canvas.TKCanvas.create_oval(50, 50, 100, 100) cir = canvas.TKCanvas.create_oval(50, 50, 100, 100)
while True: while True:
button, values = form.Read() button, values = form.Read()
if button is None: if button is None:
break break
if button is 'Blue': if button is 'Blue':
canvas.TKCanvas.itemconfig(cir, fill = "Blue") canvas.TKCanvas.itemconfig(cir, fill="Blue")
elif button is 'Red': elif button is 'Red':
canvas.TKCanvas.itemconfig(cir, fill = "Red") canvas.TKCanvas.itemconfig(cir, fill="Red")
## Input Element Update ## Input Element Update
@ -719,9 +693,7 @@ There are a number of features used in this Recipe including:
import PySimpleGUI as g import PySimpleGUI as sg
# g.SetOptions(button_color=g.COLOR_SYSTEM_DEFAULT) # because some people like gray buttons
# Demonstrates a number of PySimpleGUI features including: # Demonstrates a number of PySimpleGUI features including:
# Default element size # Default element size
@ -731,38 +703,34 @@ There are a number of features used in this Recipe including:
# Update of elements in form (Text, Input) # Update of elements in form (Text, Input)
# do_not_clear of Input elements # do_not_clear of Input elements
# create the 2 Elements we want to control outside the form layout = [[sg.Text('Enter Your Passcode')],
out_elem = g.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red') [sg.Input(size=(10, 1), do_not_clear=True, justification='right', key='input')],
in_elem = g.Input(size=(10, 1), do_not_clear=True, key='input') [sg.ReadFormButton('1'), sg.ReadFormButton('2'), sg.ReadFormButton('3')],
[sg.ReadFormButton('4'), sg.ReadFormButton('5'), sg.ReadFormButton('6')],
layout = [[g.Text('Enter Your Passcode')], [sg.ReadFormButton('7'), sg.ReadFormButton('8'), sg.ReadFormButton('9')],
[in_elem], [sg.ReadFormButton('Submit'), sg.ReadFormButton('0'), sg.ReadFormButton('Clear')],
[g.ReadFormButton('1'), g.ReadFormButton('2'), g.ReadFormButton('3')], [sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')],
[g.ReadFormButton('4'), g.ReadFormButton('5'), g.ReadFormButton('6')],
[g.ReadFormButton('7'), g.ReadFormButton('8'), g.ReadFormButton('9')],
[g.ReadFormButton('Submit'), g.ReadFormButton('0'), g.ReadFormButton('Clear')],
[out_elem],
] ]
form = g.FlexForm('Keypad', default_element_size=(5, 2), auto_size_buttons=False) form = sg.FlexForm('Keypad', default_button_element_size=(5, 2), auto_size_buttons=False, grab_anywhere=False)
form.Layout(layout) form.Layout(layout)
# Loop forever reading the form's values, updating the Input field # Loop forever reading the form's values, updating the Input field
keys_entered = '' keys_entered = ''
while True: while True:
button, values = form.Read() # read the form button, values = form.Read() # read the form
if button is None: # if the X button clicked, just exit if button is None: # if the X button clicked, just exit
break break
if button is 'Clear': # clear keys if clear button if button is 'Clear': # clear keys if clear button
keys_entered = '' keys_entered = ''
elif button in '1234567890': elif button in '1234567890':
keys_entered = values['input'] # get what's been entered so far keys_entered = values['input'] # get what's been entered so far
keys_entered += button # add the new digit keys_entered += button # add the new digit
elif button is 'Submit': elif button is 'Submit':
keys_entered = values['input'] keys_entered = values['input']
out_elem.Update(keys_entered) # output the final string form.FindElement('out').Update(keys_entered) # output the final string
in_elem.Update(keys_entered) # change the form to reflect current key string form.FindElement('input').Update(keys_entered) # change the form to reflect current key string
## Animated Matplotlib Graph ## Animated Matplotlib Graph
@ -788,17 +756,17 @@ Use the Canvas Element to create an animated graph. The code is a bit tricky to
ax.set_ylabel("Y axis") ax.set_ylabel("Y axis")
ax.grid() ax.grid()
canvas_elem = g.Canvas(size=(640, 480)) # get the canvas we'll be drawing on
layout = [[g.Text('Animated Matplotlib', size=(40, 1), justification='center', font='Helvetica 20')], layout = [[g.Text('Animated Matplotlib', size=(40, 1), justification='center', font='Helvetica 20')],
[canvas_elem], [g.Canvas(size=(640, 480), key='canvas')],
[g.ReadFormButton('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]] [g.ReadFormButton('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]]
# create the form and show it without the plot # create the form and show it without the plot
form = g.FlexForm('Demo Application - Embedding Matplotlib In PySimpleGUI') form = g.FlexForm('Demo Application - Embedding Matplotlib In PySimpleGUI')
form.Layout(layout) form.Layout(layout)
form.ReadNonBlocking() form.ReadNonBlocking()
canvas_elem = form.FindElement('canvas')
graph = FigureCanvasTkAgg(fig, master=canvas_elem.TKCanvas) graph = FigureCanvasTkAgg(fig, master=canvas_elem.TKCanvas)
canvas = canvas_elem.TKCanvas canvas = canvas_elem.TKCanvas
@ -825,24 +793,80 @@ Use the Canvas Element to create an animated graph. The code is a bit tricky to
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2) tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
# time.sleep(.1) # time.sleep(.1)
if __name__ == '__main__': if __name__ == '__main__':
main() main()
## Tables ## Tables
While there is no official support for "Tables" (e.g. there is no Table Element), it is possible to display information in a tabular way. This only works for smaller tables because there is no way to scroll a window or a column element. Until scrollable columns are implemented there is little use in creating a Table Element. While there is no official support for "Tables" (e.g. there is no Table Element), it is possible to display information in a tabular way. This only works for smaller tables because there is no way to scroll a window or a column element. Until scrollable columns are implemented there is little use in creating a Table Element.
![simpletable](https://user-images.githubusercontent.com/13696193/44960497-c667fd80-aece-11e8-988c-5fa7dfdb90a6.jpg) This Recipe contains a number of concepts beyond just tables. It has menus, and 'realtime' keyboard input. Working with large numbers of elements takes time to layout. Once laid out it should be OK.
layout = [[sg.T('Table Test')]]
![table 2](https://user-images.githubusercontent.com/13696193/45310830-5de3d680-b4f5-11e8-925a-7c2a7b8b1922.jpg)
import PySimpleGUI as sg
menu_def = [['File', ['Open', 'Save', 'Exit']],
['Edit', ['Paste', ['Special', 'Normal',], 'Undo'],],
['Help', 'About...'],]
sg.SetOptions(element_padding=(0,0))
layout = [ [sg.Menu(menu_def)],
[sg.T('Table Using Combos and Input Elements', font='Any 18')],
[sg.T('Row, Cal to change'),
sg.In(key='inputrow', justification='right', size=(8,1), pad=(1,1), do_not_clear=True),
sg.In(key='inputcol', size=(8,1), pad=(1,1), justification='right', do_not_clear=True),
sg.In(key='value', size=(8,1), pad=(1,1), justification='right', do_not_clear=True)]]
for i in range(20): for i in range(20):
row = [sg.T(f'Row {i} ', size=(10,1))] inputs = [sg.In(size=(18, 1), pad=(1, 1), justification='right', key=(i,j), do_not_clear=True) for j in range(10)]
layout.append([sg.T(f'{i}{j}', size=(4,1), background_color='white', pad=(1,1)) for j in range(10)]) line = [sg.Combo(('Customer ID', 'Customer Name', 'Customer Info'))]
line.append(inputs)
layout.append(inputs)
form = sg.FlexForm('Table', return_keyboard_events=True, grab_anywhere=False)
form.Layout(layout)
while True:
button, values = form.Read()
if button is None:
break
if button == 'Open':
filename = sg.PopupGetFile('filename to open', no_window=True, file_types=(("CSV Files","*.csv"),))
if filename is not None:
with open(filename, "r") as infile:
reader = csv.reader(infile)
# first_row = next(reader, None) # skip the headers
data = list(reader) # read everything else into a list of rows
sg.Print(data)
for i, row in enumerate(data):
for j, item in enumerate(row):
print(i,j, item)
# form.FindElement(key=(i,j)).Update(item)
location = (i,j)
try:
# location = (int(values['inputrow']), int(values['inputcol']))
target_element = form.FindElement(location)
new_value = item
# new_value = values['value']
if target_element is not None and new_value != '':
target_element.Update(new_value)
except:
pass
if button == 'Exit':
break
try:
location = (int(values['inputrow']), int(values['inputcol']))
target_element = form.FindElement(location)
new_value = values['value']
if target_element is not None and new_value != '':
target_element.Update(new_value)
except:
pass
sg.FlexForm('Table').LayoutAndRead(layout)
## Tight Layout with Button States ## Tight Layout with Button States
@ -856,26 +880,26 @@ In other GUI frameworks this program would be most likely "event driven" with ca
import PySimpleGUI as sg import PySimpleGUI as sg
"""
Demonstrates using a "tight" layout with a Dark theme.
Shows how button states can be controlled by a user application. The program manages the disabled/enabled
states for buttons and changes the text color to show greyed-out (disabled) buttons
"""
sg.ChangeLookAndFeel('Dark') sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0, 0)) sg.SetOptions(element_padding=(0,0))
StartButton = sg.ReadFormButton('Start', button_color=('white', 'black')) layout = [[sg.T('User:', pad=((3,0),0)), sg.OptionMenu(values = ('User 1', 'User 2'), size=(20,1)), sg.T('0', size=(8,1))],
StopButton = sg.ReadFormButton('Stop', button_color=('gray34', 'black')) [sg.T('Customer:', pad=((3,0),0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20,1)), sg.T('1', size=(8,1))],
ResetButton = sg.ReadFormButton('Reset', button_color=('gray', 'firebrick3')) [sg.T('Notes:', pad=((3,0),0)), sg.In(size=(44,1), background_color='white', text_color='black')],
SubmitButton = sg.ReadFormButton('Submit', button_color=('gray34', 'springgreen4')) [sg.ReadFormButton('Start', button_color=('white', 'black'), key='start'),
sg.ReadFormButton('Stop', button_color=('gray34', 'black'), key='stop'),
sg.ReadFormButton('Reset', button_color=('gray', 'firebrick3'), key='reset'),
sg.ReadFormButton('Submit', button_color=('gray34', 'springgreen4'), key='submit')]
]
layout = [ form = sg.FlexForm("Time Tracker", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False,
[sg.T('User:', pad=((3, 0), 0)), sg.OptionMenu(values=('User 1', 'User 2'), size=(20, 1)), sg.T('0', size=(8, 1))], default_button_element_size=(12,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) form.Layout(layout)
recording = have_data = False recording = have_data = False
while True: while True:
@ -883,28 +907,28 @@ In other GUI frameworks this program would be most likely "event driven" with ca
if button is None: if button is None:
exit(69) exit(69)
if button is 'Start': if button is 'Start':
StartButton.Update(button_color=('gray34', 'black')) form.FindElement('start').Update(button_color=('gray34','black'))
StopButton.Update(button_color=('white', 'black')) form.FindElement('stop').Update(button_color=('white', 'black'))
ResetButton.Update(button_color=('white', 'firebrick3')) form.FindElement('reset').Update(button_color=('white', 'firebrick3'))
recording = True recording = True
elif button is 'Stop' and recording: elif button is 'Stop' and recording:
StopButton.Update(button_color=('gray34', 'black')) form.FindElement('stop').Update(button_color=('gray34','black'))
StartButton.Update(button_color=('white', 'black')) form.FindElement('start').Update(button_color=('white', 'black'))
SubmitButton.Update(button_color=('white', 'springgreen4')) form.FindElement('submit').Update(button_color=('white', 'springgreen4'))
recording = False recording = False
have_data = True have_data = True
elif button is 'Reset': elif button is 'Reset':
StopButton.Update(button_color=('gray34', 'black')) form.FindElement('stop').Update(button_color=('gray34','black'))
StartButton.Update(button_color=('white', 'black')) form.FindElement('start').Update(button_color=('white', 'black'))
SubmitButton.Update(button_color=('gray34', 'springgreen4')) form.FindElement('submit').Update(button_color=('gray34', 'springgreen4'))
ResetButton.Update(button_color=('gray34', 'firebrick3')) form.FindElement('reset').Update(button_color=('gray34', 'firebrick3'))
recording = False recording = False
have_data = False have_data = False
elif button is 'Submit' and have_data: elif button is 'Submit' and have_data:
StopButton.Update(button_color=('gray34', 'black')) form.FindElement('stop').Update(button_color=('gray34','black'))
StartButton.Update(button_color=('white', 'black')) form.FindElement('start').Update(button_color=('white', 'black'))
SubmitButton.Update(button_color=('gray34', 'springgreen4')) form.FindElement('submit').Update(button_color=('gray34', 'springgreen4'))
ResetButton.Update(button_color=('gray34', 'firebrick3')) form.FindElement('reset').Update(button_color=('gray34', 'firebrick3'))
recording = False recording = False
## Password Protection For Scripts ## Password Protection For Scripts
@ -976,3 +1000,5 @@ Use the upper half to generate your hash code. Then paste it into the code in t
print('Login SUCCESSFUL') print('Login SUCCESSFUL')
else: else:
print('Login FAILED!!') print('Login FAILED!!')