Merge pull request #321 from MikeTheWatchGuy/Dev-latest

Goodbye FlexForm.... Hello Window
This commit is contained in:
MikeTheWatchGuy 2018-09-23 16:56:57 -04:00 committed by GitHub
commit e509bf0598
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 3257 additions and 3223 deletions

View File

@ -13,7 +13,7 @@ Same GUI screen except the return values are in a list instead of a dictionary a
import PySimpleGUI as sg import PySimpleGUI as sg
# Very basic form. Return values as a list # Very basic form. Return values as a list
form = sg.FlexForm('Simple data entry form') # begin with a blank form form = sg.Window('Simple data entry form') # begin with a blank form
layout = [ layout = [
[sg.Text('Please enter your Name, Address, Phone')], [sg.Text('Please enter your Name, Address, Phone')],
@ -35,7 +35,7 @@ A simple form with default values. Results returned in a dictionary.
import PySimpleGUI as sg import PySimpleGUI as sg
# Very basic form. Return values as a dictionary # Very basic form. Return values as a dictionary
form = sg.FlexForm('Simple data entry form') # begin with a blank form form = sg.Window('Simple data entry form') # begin with a blank form
layout = [ layout = [
[sg.Text('Please enter your Name, Address, Phone')], [sg.Text('Please enter your Name, Address, Phone')],
@ -66,7 +66,7 @@ Browse for a filename that is populated into the input field.
[sg.InputText(), sg.FileBrowse()], [sg.InputText(), sg.FileBrowse()],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
(button, (source_filename,)) = sg.FlexForm('SHA-1 & 256 Hash').LayoutAndRead(form_rows) (button, (source_filename,)) = sg.Window('SHA-1 & 256 Hash').LayoutAndRead(form_rows)
print(button, source_filename) print(button, source_filename)
@ -81,7 +81,7 @@ Quickly add a GUI allowing the user to browse for a filename if a filename is no
import sys import sys
if len(sys.argv) == 1: if len(sys.argv) == 1:
button, (fname,) = sg.FlexForm('My Script').LayoutAndRead([[sg.Text('Document to open')], button, (fname,) = sg.Window('My Script').LayoutAndRead([[sg.Text('Document to open')],
[sg.In(), sg.FileBrowse()], [sg.In(), sg.FileBrowse()],
[sg.Open(), sg.Cancel()]]) [sg.Open(), sg.Cancel()]])
else: else:
@ -110,7 +110,7 @@ Browse to get 2 file names that can be then compared.
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
form = sg.FlexForm('File Compare') form = sg.Window('File Compare')
button, values = form.LayoutAndRead(form_rows) button, values = form.LayoutAndRead(form_rows)
@ -167,7 +167,7 @@ Example of nearly all of the widgets in a single form. Uses a customized color
] ]
form = sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout) form = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout)
button, values = form.Read() button, values = form.Read()
@ -194,7 +194,7 @@ An async form that has a button read loop. A Text Element is updated periodical
[sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center', key='output')], [sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center', key='output')],
[sg.T(' ' * 5), sg.ReadButton('Start/Stop', focus=True), sg.Quit()]] [sg.T(' ' * 5), sg.ReadButton('Start/Stop', focus=True), sg.Quit()]]
form = sg.FlexForm('Running Timer').Layout(form_rows) form = sg.Window('Running Timer').Layout(form_rows)
timer_running = True timer_running = True
i = 0 i = 0
@ -238,7 +238,7 @@ The architecture of some programs works better with button callbacks instead of
[sg.ReadFormButton('1'), sg.ReadFormButton('2'), sg.Quit()]] [sg.ReadFormButton('1'), sg.ReadFormButton('2'), sg.Quit()]]
# Show the form to the user # Show the form to the user
form = sg.FlexForm('Button callback example').Layout(layout) form = sg.Window('Button callback example').Layout(layout)
# Event loop. Read buttons, make callbacks # Event loop. Read buttons, make callbacks
while True: while True:
@ -272,7 +272,7 @@ This recipe implements a remote control interface for a robot. There are 4 dire
[sg.Quit(button_color=('black', 'orange'))] [sg.Quit(button_color=('black', 'orange'))]
] ]
form = sg.FlexForm('Robotics Remote Control', auto_size_text=True).Layout(form_rows) form = sg.Window('Robotics Remote Control', auto_size_text=True).Layout(form_rows)
# #
# Some place later in your code... # Some place later in your code...
@ -321,8 +321,8 @@ Tabbed forms are **easy** to make and use in PySimpleGUI. You simple may your l
[sg.InputText(), sg.Text('Enter some info')], [sg.InputText(), sg.Text('Enter some info')],
[sg.Submit(button_color=('red', 'yellow')), sg.Cancel(button_color=('white', 'blue'))]] [sg.Submit(button_color=('red', 'yellow')), sg.Cancel(button_color=('white', 'blue'))]]
form = sg.FlexForm('') form = sg.Window('')
form2 = sg.FlexForm('') form2 = sg.Window('')
results = sg.ShowTabbedForm('Tabbed form example', (form, layout_tab_1, 'First Tab'), results = sg.ShowTabbedForm('Tabbed form example', (form, layout_tab_1, 'First Tab'),
(form2, layout_tab_2, 'Second Tab')) (form2, layout_tab_2, 'Second Tab'))
@ -377,7 +377,7 @@ Buttons can have PNG of GIF images on them. This Media Player recipe requires 4
sg.Text('Volume', font=("Helvetica", 15), size=(7, 1))] sg.Text('Volume', font=("Helvetica", 15), size=(7, 1))]
] ]
form = sg.FlexForm('Media File Player', auto_size_text=True, default_element_size=(20, 1), form = sg.Window('Media File Player', auto_size_text=True, default_element_size=(20, 1),
font=("Helvetica", 25)).Layout(layout) font=("Helvetica", 25)).Layout(layout)
# Our event loop # Our event loop
while (True): while (True):
@ -419,7 +419,7 @@ This form doesn't close after button clicks. To achieve this the buttons are sp
] ]
form = sg.FlexForm('Script launcher').Layout(layout) form = sg.Window('Script launcher').Layout(layout)
# ---===--- Loop taking in user input and using it to call scripts --- # # ---===--- Loop taking in user input and using it to call scripts --- #
@ -469,7 +469,7 @@ A standard non-blocking GUI with lots of inputs.
[sg.Radio('MSE(L2)', 'loss', size=(12, 1)), sg.Radio('MB(L0)', 'loss', size=(12, 1))], [sg.Radio('MSE(L2)', 'loss', size=(12, 1)), sg.Radio('MB(L0)', 'loss', size=(12, 1))],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
form = sg.FlexForm('Machine Learning Front End', font=("Helvetica", 12)).Layout(layout) form = sg.Window('Machine Learning Front End', font=("Helvetica", 12)).Layout(layout)
button, values = form.Read() button, values = form.Read()
@ -488,7 +488,7 @@ Perhaps you don't want all the statistics that the EasyProgressMeter provides an
[sg.Cancel()]] [sg.Cancel()]]
# create the form # create the form
form = sg.FlexForm('Custom Progress Meter').Layout(layout) form = sg.Window('Custom Progress Meter').Layout(layout)
# 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
@ -505,7 +505,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. 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 `Window` and the call to `LayoutAndRead`. `Window` returns a `Window` object which has the `LayoutAndRead` method.
![simple](https://user-images.githubusercontent.com/13696193/44227935-ecb53b80-a161-11e8-968b-b3f963404dec.jpg) ![simple](https://user-images.githubusercontent.com/13696193/44227935-ecb53b80-a161-11e8-968b-b3f963404dec.jpg)
@ -519,13 +519,13 @@ Instead of
[sg.Input(), sg.FileBrowse()], [sg.Input(), sg.FileBrowse()],
[sg.OK(), sg.Cancel()]] [sg.OK(), sg.Cancel()]]
button, (number,) = sg.FlexForm('Get filename example').LayoutAndRead(layout) button, (number,) = sg.Window('Get filename example').LayoutAndRead(layout)
you can write this line of code for the exact same result (OK, two lines with the import): you can write this line of code for the exact same result (OK, two lines with the import):
import PySimpleGUI as sg import PySimpleGUI as sg
button, (filename,) = sg.FlexForm('Get filename example').LayoutAndRead( button, (filename,) = sg.Window('Get filename example').LayoutAndRead(
[[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()]]) [[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()]])
-------------------- --------------------
@ -560,7 +560,7 @@ To make it easier to see the Column in the window, the Column background has bee
# Display the form and get values # Display the form and get values
button, values = sg.FlexForm('Compact 1-line form with column').LayoutAndRead(layout) button, values = sg.Window('Compact 1-line form with column').LayoutAndRead(layout)
sg.Popup(button, values, line_width=200) sg.Popup(button, values, line_width=200)
@ -582,7 +582,7 @@ This simple program keep a form open, taking input values until the user termina
[sg.Txt('', size=(8,1), key='output') ], [sg.Txt('', size=(8,1), key='output') ],
[sg.ReadButton('Calculate', bind_return_key=True)]] [sg.ReadButton('Calculate', bind_return_key=True)]]
form = sg.FlexForm('Math').Layout(layout) form = sg.Window('Math').Layout(layout)
while True: while True:
button, values = form.Read() button, values = form.Read()
@ -622,7 +622,7 @@ While it's fun to scribble on a Canvas Widget, try Graph Element makes it a down
[sg.T('Change circle color to:'), sg.ReadButton('Red'), sg.ReadButton('Blue')] [sg.T('Change circle color to:'), sg.ReadButton('Red'), sg.ReadButton('Blue')]
] ]
form = sg.FlexForm('Canvas test') form = sg.Window('Canvas test')
form.Layout(layout) form.Layout(layout)
form.Finalize() form.Finalize()
@ -652,7 +652,7 @@ Just like you can draw on a tkinter widget, you can also draw on a Graph Element
[sg.T('Change circle color to:'), sg.ReadFormButton('Red'), sg.ReadFormButton('Blue'), sg.ReadFormButton('Move')] [sg.T('Change circle color to:'), sg.ReadFormButton('Red'), sg.ReadFormButton('Blue'), sg.ReadFormButton('Move')]
] ]
form = sg.FlexForm('Graph test') form = sg.Window('Graph test')
form.Layout(layout) form.Layout(layout)
form.Finalize() form.Finalize()
@ -713,7 +713,7 @@ There are a number of features used in this Recipe including:
[sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')], [sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')],
] ]
form = sg.FlexForm('Keypad', default_button_element_size=(5, 2), auto_size_buttons=False, grab_anywhere=False).Layout(layout) form = sg.Window('Keypad', default_button_element_size=(5, 2), auto_size_buttons=False, grab_anywhere=False).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 = ''
@ -762,7 +762,7 @@ Use the Canvas Element to create an animated graph. The code is a bit tricky to
# 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').Layout(layout) form = g.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout)
form.Finalize() # needed to access the canvas element prior to reading the form form.Finalize() # needed to access the canvas element prior to reading the form
canvas_elem = form.FindElement('canvas') canvas_elem = form.FindElement('canvas')
@ -825,7 +825,7 @@ In other GUI frameworks this program would be most likely "event driven" with ca
sg.ReadFormButton('Submit', button_color=('white', 'springgreen4'), key='Submit')] sg.ReadFormButton('Submit', button_color=('white', 'springgreen4'), key='Submit')]
] ]
form = sg.FlexForm("Time Tracker", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False, form = sg.Window("Time Tracker", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False,
default_button_element_size=(12,1)) default_button_element_size=(12,1))
form.Layout(layout) form.Layout(layout)
form.Finalize() form.Finalize()
@ -893,7 +893,7 @@ Use the upper half to generate your hash code. Then paste it into the code in t
[sg.T('SHA Hash'), sg.In('', size=(40,1), key='hash')], [sg.T('SHA Hash'), sg.In('', size=(40,1), key='hash')],
] ]
form = sg.FlexForm('SHA Generator', auto_size_text=False, default_element_size=(10,1), form = sg.Window('SHA Generator', auto_size_text=False, default_element_size=(10,1),
text_justification='r', return_keyboard_events=True, grab_anywhere=False).Layout(layout) text_justification='r', return_keyboard_events=True, grab_anywhere=False).Layout(layout)
@ -990,7 +990,7 @@ You can easily change colors to match your background by changing a couple of pa
sg.Button('EXIT', button_color=('white','firebrick3'))], sg.Button('EXIT', button_color=('white','firebrick3'))],
[sg.T('', text_color='white', size=(50,1), key='output')]] [sg.T('', text_color='white', size=(50,1), key='output')]]
form = sg.FlexForm('Floating Toolbar', no_titlebar=True, keep_on_top=True).Layout(layout) form = sg.Window('Floating Toolbar', no_titlebar=True, keep_on_top=True).Layout(layout)
# ---===--- Loop taking in user input (buttons) --- # # ---===--- Loop taking in user input (buttons) --- #
while True: while True:
@ -1060,7 +1060,7 @@ Much of the code is handling the button states in a fancy way. It could be much
sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'), sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'),
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]] sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
form = sg.FlexForm('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout) form = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout)
# ---------------- main loop ---------------- # ---------------- main loop ----------------
@ -1124,7 +1124,7 @@ The spinner changes the number of seconds between reads. Note that you will get
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
[sg.Exit(button_color=('white', 'firebrick4'), pad=((15,0), 0)), sg.Spin([x+1 for x in range(10)], 1, key='spin')]] [sg.Exit(button_color=('white', 'firebrick4'), pad=((15,0), 0)), sg.Spin([x+1 for x in range(10)], 1, key='spin')]]
# Layout the rows of the form and perform a read. Indicate the form is non-blocking! # Layout the rows of the form and perform a read. Indicate the form is non-blocking!
form = sg.FlexForm('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(form_rows) form = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(form_rows)
# ---------------- main loop ---------------- # ---------------- main loop ----------------
@ -1178,7 +1178,7 @@ If you double click the dashed line at the top of the list of choices, that menu
[sg.Output(size=(60, 20))] [sg.Output(size=(60, 20))]
] ]
form = sg.FlexForm("Windows-like program", default_element_size=(12, 1), auto_size_text=False, auto_size_buttons=False, form = sg.Window("Windows-like program", default_element_size=(12, 1), auto_size_text=False, auto_size_buttons=False,
default_button_element_size=(12, 1)).Layout(layout) default_button_element_size=(12, 1)).Layout(layout)
# ------ Loop & Process button menu choices ------ # # ------ Loop & Process button menu choices ------ #
@ -1209,7 +1209,7 @@ In this example we're defining our graph to be from -100, -100 to +100,+100. Th
layout = [[sg.Graph(canvas_size=(400, 400), graph_bottom_left=(-100,-100), graph_top_right=(100,100), background_color='white', key='graph')],] layout = [[sg.Graph(canvas_size=(400, 400), graph_bottom_left=(-100,-100), graph_top_right=(100,100), background_color='white', key='graph')],]
form = sg.FlexForm('Graph of Sine Function').Layout(layout) form = sg.Window('Graph of Sine Function').Layout(layout)
form.Finalize() form.Finalize()
graph = form.FindElement('graph') graph = form.FindElement('graph')

View File

@ -7,10 +7,10 @@
# PySimpleGUI # PySimpleGUI
![Python Version](https://img.shields.io/badge/PySimpleGUI_Version-3.5.2-red.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_Version-3.6.0-red.svg?longCache=true&style=for-the-badge)
[Wiki for the latest news](https://github.com/MikeTheWatchGuy/PySimpleGUI/wiki) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
[ReadTheDocs](http://pysimplegui.readthedocs.io/) [ReadTheDocs](http://pysimplegui.readthedocs.io/)
@ -18,6 +18,8 @@
[Brief Tutorial](https://pysimplegui.readthedocs.io/en/latest/tutorial/) [Brief Tutorial](https://pysimplegui.readthedocs.io/en/latest/tutorial/)
[OpenSource Article](https://opensource.com/article/18/8/pysimplegui)
[Latest Demos and Master Branch on GitHub](https://github.com/MikeTheWatchGuy/PySimpleGUI) [Latest Demos and Master Branch on GitHub](https://github.com/MikeTheWatchGuy/PySimpleGUI)
Super-simple GUI to use... Powerfully customizable. Super-simple GUI to use... Powerfully customizable.
@ -26,7 +28,13 @@ Home of the 1-line custom GUI and 1-line progress meter
Note - ***Python3*** is required to run PySimpleGUI. It takes advantage of some Python3 features that do not translate well into Python2. Note - ***Python3*** is required to run PySimpleGUI. It takes advantage of some Python3 features that do not translate well into Python2.
Looking to take your Python code from the world of command lines and into the convenience of a GUI? Have a Raspberry **Pi** with a touchscreen that's going to waste because you don't have the time to learn a GUI SDK? Into Machine Learning and are sick of the command line? How about distributing your Python code to Windows users as a single .EXE file that launches straight into a GUI, much like a WinForms app? Look no further, **you've found your GUI package**. Looking for a GUI package to help with
* Taking your Python code from the world of command lines and into the convenience of a GUI? *
* Have a Raspberry **Pi** with a touchscreen that's going to waste because you don't have the time to learn a GUI SDK?
* Into Machine Learning and are sick of the command line?
* How about distributing your Python code to Windows users as a single .EXE file that launches straight into a GUI, much like a WinForms app?
Look no further, **you've found your GUI package**.
import PySimpleGUI as sg import PySimpleGUI as sg
@ -40,7 +48,7 @@ Or how about a ***custom GUI*** in 1 line of code?
import PySimpleGUI as sg import PySimpleGUI as sg
button, (filename,) = sg.FlexForm('Get filename example'). LayoutAndRead([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ]) button, (filename,) = sg.Window('Get filename example'). LayoutAndRead([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ])
![get filename](https://user-images.githubusercontent.com/13696193/44960039-f1018880-aec5-11e8-8a43-3d7f8ff93b67.jpg) ![get filename](https://user-images.githubusercontent.com/13696193/44960039-f1018880-aec5-11e8-8a43-3d7f8ff93b67.jpg)
@ -119,6 +127,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad
Checkboxes Checkboxes
Radio Buttons Radio Buttons
Listbox Listbox
Option Menu
Slider Slider
Graph Graph
Frame with title Frame with title
@ -126,9 +135,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad
Multi-line Text Input Multi-line Text Input
Scroll-able Output Scroll-able Output
Images Images
Progress Bar Progress Bar Async/Non-Blocking Windows
Calendar chooser
Async/Non-Blocking Windows
Tabbed forms Tabbed forms
Persistent Windows Persistent Windows
Redirect Python Output/Errors to scrolling window Redirect Python Output/Errors to scrolling window
@ -188,7 +195,7 @@ An example of many widgets used on a single form. A little further down you'll
sg.FolderBrowse()], sg.FolderBrowse()],
[sg.Submit(), sg.Cancel(), sg.Button('Customized', button_color=('white', 'green'))]] [sg.Submit(), sg.Cancel(), sg.Button('Customized', button_color=('white', 'green'))]]
button, values = sg.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40, 1)).LayoutAndRead(layout) button, values = sg.Window('Everything bagel', auto_size_text=True, default_element_size=(40, 1)).LayoutAndRead(layout)
@ -582,7 +589,7 @@ Finally we can put it all together into a program that will display our window.
[sg.Input()], [sg.Input()],
[sg.OK()] ] [sg.OK()] ]
button, (number,) = sg.FlexForm('Enter a number example').LayoutAndRead(layout) button, (number,) = sg.Window('Enter a number example').LayoutAndRead(layout)
sg.Popup(button, number) sg.Popup(button, number)
@ -600,7 +607,7 @@ Writing the code for this one is just as straightforward. There is one tricky t
[sg.Input(), sg.FileBrowse()], [sg.Input(), sg.FileBrowse()],
[sg.OK(), sg.Cancel()] ] [sg.OK(), sg.Cancel()] ]
button, (number,) = sg.FlexForm('Get filename example').LayoutAndRead(layout) button, (number,) = sg.Window('Get filename example').LayoutAndRead(layout)
sg.Popup(button, number) sg.Popup(button, number)
@ -622,7 +629,7 @@ This is the most basic design pattern. Use this for forms that are shown to the
[sg.InputText(), sg.FileBrowse()], [sg.InputText(), sg.FileBrowse()],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
form = sg.FlexForm('SHA-1 & 256 Hash') form = sg.Window('SHA-1 & 256 Hash')
button, (source_filename,) = form.LayoutAndRead(form_rows) button, (source_filename,) = form.LayoutAndRead(form_rows)
@ -630,20 +637,20 @@ This is the most basic design pattern. Use this for forms that are shown to the
## Pattern 2 - Single-read form "chained" ## Pattern 2 - Single-read form "chained"
Python has a ***beautiful*** way of compacting code known as "chaining". You take the output from one function and feed it as input to the next. Notice in the first example how a form is first obtained by calling FlexForm and then that form is then read. It's possible to combine the creation of the form with the read. This design pattern does exactly that, chain together the form creation and the form reading. Python has a ***beautiful*** way of compacting code known as "chaining". You take the output from one function and feed it as input to the next. Notice in the first example how a form is first obtained by calling Window and then that form is then read. It's possible to combine the creation of the form with the read. This design pattern does exactly that, chain together the form creation and the form reading.
form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')], form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')],
[sg.InputText(), sg.FileBrowse()], [sg.InputText(), sg.FileBrowse()],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
button, (source_filename,) = sg.FlexForm('SHA-1 & 256 Hash').LayoutAndRead(form_rows) button, (source_filename,) = sg.Window('SHA-1 & 256 Hash').LayoutAndRead(form_rows)
## Pattern 3 - Persistent form (multiple reads) ## Pattern 3 - Persistent form (multiple reads)
Some of the more advanced programs operate with the form remaining visible on the screen. Input values are collected, but rather than closing the form, it is kept visible acting as a way to both output information to the user and gather input data. Some of the more advanced programs operate with the form remaining visible on the screen. Input values are collected, but rather than closing the form, it is kept visible acting as a way to both output information to the user and gather input data.
This is done by splitting the LayoutAndRead call apart into a Layout call and a Read call. Note how chaining is again used. In this case a form is created by calling FlexForm which is then passed on to the Layout method. The Layout method returns the form value so that it can be stored and used later in the program to Read the form. This is done by splitting the LayoutAndRead call apart into a Layout call and a Read call. Note how chaining is again used. In this case a form is created by calling Window which is then passed on to the Layout method. The Layout method returns the form value so that it can be stored and used later in the program to Read the form.
import PySimpleGUI as sg import PySimpleGUI as sg
@ -652,7 +659,7 @@ This is done by splitting the LayoutAndRead call apart into a Layout call and a
[sg.RButton('Turn LED Off')], [sg.RButton('Turn LED Off')],
[sg.Exit()]] [sg.Exit()]]
form = sg.FlexForm('Raspberry Pi GUI').Layout(layout) form = sg.Window('Raspberry Pi GUI').Layout(layout)
while True: while True:
button, values = form.Read() button, values = form.Read()
@ -675,7 +682,7 @@ The key to custom forms in PySimpleGUI is to view forms as ROWS of Elements. E
[sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()], [sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
form = sg.FlexForm('Rename Files or Folders') form = sg.Window('Rename Files or Folders')
button, (folder_path, file_path) = form.LayoutAndRead(layout) button, (folder_path, file_path) = form.LayoutAndRead(layout)
@ -719,7 +726,7 @@ Isn't this what almost every Python programmer looking for a GUI wants?? Someth
By default return values are a list of values, one entry for each input field. By default return values are a list of values, one entry for each input field.
Return information from FlexForm, SG's primary form builder interface, is in this format: Return information from Window, SG's primary form builder interface, is in this format:
button, (value1, value2, ...) button, (value1, value2, ...)
@ -766,7 +773,7 @@ If **any** element in the form has a `key`, then **all** of the return values ar
Let's take a look at your first dictionary-based form. Let's take a look at your first dictionary-based form.
import PySimpleGUI as sg import PySimpleGUI as sg
form = sg.FlexForm('Simple data entry form') form = sg.Window('Simple data entry form')
layout = [ layout = [
[sg.Text('Please enter your Name, Address, Phone')], [sg.Text('Please enter your Name, Address, Phone')],
[sg.Text('Name', size=(15, 1)), sg.InputText('1', key='name')], [sg.Text('Name', size=(15, 1)), sg.InputText('1', key='name')],
@ -823,7 +830,7 @@ This little program has a typical Event Loop
[sg.RButton('Turn LED Off')], [sg.RButton('Turn LED Off')],
[sg.Exit()]] [sg.Exit()]]
form = sg.FlexForm('Raspberry Pi).Layout(layout) form = sg.Window('Raspberry Pi).Layout(layout)
# ---- Event Loop ---- # # ---- Event Loop ---- #
while True: while True:
@ -900,7 +907,7 @@ This code utilizes as many of the elements in one form as possible.
] ]
form = sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout) form = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout)
button, values = form.Read() button, values = form.Read()
@ -941,11 +948,11 @@ NON-BLOCKING form call:
### Beginning a Form ### Beginning a Form
The first step is to create the form object using the desired form customization. The first step is to create the form object using the desired form customization.
with FlexForm('Everything bagel', auto_size_text=True, default_element_size=(30,1)) as form: with Window('Everything bagel', auto_size_text=True, default_element_size=(30,1)) as form:
This is the definition of the FlexForm object: This is the definition of the Window object:
def FlexForm(title, def Window(title,
default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]),
default_button_element_size = (None, None), default_button_element_size = (None, None),
auto_size_text=None, auto_size_text=None,
@ -1037,7 +1044,9 @@ To keep a window on top of all other windows on the screen, set keep_on_top = Tr
Buttons including these types: Buttons including these types:
File Browse File Browse
Folder Browse Folder Browse
Non-closing return Calendar picker
Date Chooser
Read form
Close form Close form
Realtime Realtime
Checkboxes Checkboxes
@ -1047,9 +1056,12 @@ To keep a window on top of all other windows on the screen, set keep_on_top = Tr
Multi-line Text Input Multi-line Text Input
Scroll-able Output Scroll-able Output
Progress Bar Progress Bar
Option Menu
Menu Menu
Frame Frame
Column
Graph Graph
Image
Table Table
Async/Non-Blocking Windows Async/Non-Blocking Windows
Tabbed forms Tabbed forms
@ -1533,7 +1545,7 @@ Targets that are specified using a key will find its target element by using the
If the Target is specified using (row, column) then it utilizes a grid system. The rows in your GUI are numbered starting with 0. The target can be specified as a hard coded grid item or it can be relative to the button. If the Target is specified using (row, column) then it utilizes a grid system. The rows in your GUI are numbered starting with 0. The target can be specified as a hard coded grid item or it can be relative to the button.
The (row, col) targeting can only target elements that are in the same "container". Containers are the FlexForm, Column and Frame Elements. A File Browse button located inside of a Column is unable to target elements outside of that Column. The (row, col) targeting can only target elements that are in the same "container". Containers are the Window, Column and Frame Elements. A File Browse button located inside of a Column is unable to target elements outside of that Column.
The default value for `target` is `(ThisRow, -1)`. `ThisRow` is a special value that tells the GUI to use the same row as the button. The Y-value of -1 means the field one value to the left of the button. For a File or Folder Browse button, the field that it fills are generally to the left of the button is most cases. (ThisRow, -1) means the Element to the left of the button, on the same row. The default value for `target` is `(ThisRow, -1)`. `ThisRow` is a special value that tells the GUI to use the same row as the button. The Y-value of -1 means the field one value to the left of the button. For a File or Folder Browse button, the field that it fills are generally to the left of the button is most cases. (ThisRow, -1) means the Element to the left of the button, on the same row.
@ -1640,7 +1652,7 @@ This form has 2 button types. There's the normal "Simple Button" (Quit) and 4 "
Here is the code to make, show and get results from this form: Here is the code to make, show and get results from this form:
form = sg.FlexForm('Robotics Remote Control', auto_size_text=True) form = sg.Window('Robotics Remote Control', auto_size_text=True)
form_rows = [[sg.Text('Robotics Remote Control')], form_rows = [[sg.Text('Robotics Remote Control')],
[sg.T(' '*10), sg.RealtimeButton('Forward')], [sg.T(' '*10), sg.RealtimeButton('Forward')],
@ -1706,7 +1718,7 @@ Another way of using a Progress Meter with PySimpleGUI is to build a custom form
[sg.Cancel()]] [sg.Cancel()]]
# create the form` # create the form`
form = sg.FlexForm('Custom Progress Meter').Layout(layout) form = sg.Window('Custom Progress Meter').Layout(layout)
progress_bar = form.FindElement('progressbar') progress_bar = form.FindElement('progressbar')
# loop that would normally do something useful # loop that would normally do something useful
for i in range(10000): for i in range(10000):
@ -1737,7 +1749,7 @@ Here's a complete solution for a chat-window using an Async form with an Output
sg.RButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0])), sg.RButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0])),
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]] sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
form = sg.FlexForm('Chat Window', default_element_size=(30, 2)).Layout(layout) form = sg.Window('Chat Window', default_element_size=(30, 2)).Layout(layout)
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- # # ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
while True: while True:
@ -1778,7 +1790,7 @@ This code produced the above window.
# Prior to the Column element, this layout was not possible # 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. # Columns layouts look identical to form layouts, they are a list of lists of elements.
form = sg.FlexForm('Columns') # blank form form = sg.Window('Columns') # blank form
# Column layout # Column layout
col = [[sg.Text('col Row 1')], col = [[sg.Text('col Row 1')],
@ -1796,7 +1808,7 @@ This code produced the above window.
# Display the form and get values # Display the form and get values
# If you're willing to not use the "context manager" design pattern, then it's possible # 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. # 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) button, values = sg.Window('Compact 1-line form with column').LayoutAndRead(layout)
sg.Popup(button, values, line_width=200) sg.Popup(button, values, line_width=200)
@ -1839,7 +1851,7 @@ This code creates a form with a Frame and 2 buttons.
[sg.Submit(), sg.Cancel()] [sg.Submit(), sg.Cancel()]
] ]
form = sg.FlexForm('Frame with buttons', font=("Helvetica", 12)).Layout(layout) form = sg.Window('Frame with buttons', font=("Helvetica", 12)).Layout(layout)
![frame element](https://user-images.githubusercontent.com/13696193/45889173-c2245700-bd8d-11e8-8f73-1e5f1be3ddb1.jpg) ![frame element](https://user-images.githubusercontent.com/13696193/45889173-c2245700-bd8d-11e8-8f73-1e5f1be3ddb1.jpg)
@ -1874,7 +1886,7 @@ The order of operations to obtain a tkinter Canvas Widget is:
[sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]] [sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]]
# create the form and show it without the plot # create the form and show it without the plot
form = sg.FlexForm('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize() form = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize()
# add the plot to the window # add the plot to the window
@ -1964,7 +1976,7 @@ Tabbed forms are shown using the `ShowTabbedForm` call. The call has the format
(form,layout,'Tab 1 label'), (form,layout,'Tab 1 label'),
(form2,layout2, 'Tab 2 label'), ...) (form2,layout2, 'Tab 2 label'), ...)
Each of the tabs of the form is in fact a form. The same steps are taken to create the form as before. A `FlexForm` is created, then rows are filled with Elements, and finally the form is shown. When calling `ShowTabbedForm`, each form is passed in as a tuple. The tuple has the format: `(the form, the rows, a string shown on the tab)` Each of the tabs of the form is in fact a form. The same steps are taken to create the form as before. A `Window` is created, then rows are filled with Elements, and finally the form is shown. When calling `ShowTabbedForm`, each form is passed in as a tuple. The tuple has the format: `(the form, the rows, a string shown on the tab)`
Results are returned as a list of lists. For each form you'll get a list that's in the same format as a normal form. A single tab's values would be: Results are returned as a list of lists. For each form you'll get a list that's in the same format as a normal form. A single tab's values would be:
@ -2150,7 +2162,7 @@ We're going to build an app that does the latter. It's going to update our form
The basic flow and functions you will be calling are: The basic flow and functions you will be calling are:
Setup Setup
form = FlexForm() form = Window()
form_rows = ..... form_rows = .....
form.LayoutAndRead(form_rows, non_blocking=True) form.LayoutAndRead(form_rows, non_blocking=True)
@ -2176,7 +2188,7 @@ See the sample code on the GitHub named Demo Media Player for another example of
# form that doesn't block # form that doesn't block
# Make a form, but don't use context manager # Make a form, but don't use context manager
form = sg.FlexForm('Running Timer', auto_size_text=True) form = sg.Window('Running Timer', auto_size_text=True)
# Create the layout # Create the layout
form_rows = [[sg.Text('Non-blocking GUI with updates')], form_rows = [[sg.Text('Non-blocking GUI with updates')],
@ -2233,7 +2245,7 @@ In some programs these updates happen in response to another Element. This prog
sg.Text("Aa", size=(2, 1), font="Helvetica " + str(fontSize), key='text')]] sg.Text("Aa", size=(2, 1), font="Helvetica " + str(fontSize), key='text')]]
sz = fontSize sz = fontSize
form = sg.FlexForm("Font size selector", grab_anywhere=False).Layout(layout) form = sg.Window("Font size selector", grab_anywhere=False).Layout(layout)
# Event Loop # Event Loop
while True: while True:
button, values= form.Read() button, values= form.Read()
@ -2273,7 +2285,7 @@ The takeaway from this exercise is that keys are key in PySimpleGUI's design. T
## Keyboard & Mouse Capture ## Keyboard & Mouse Capture
Beginning in version 2.10 you can capture keyboard key presses and mouse scroll-wheel events. Keyboard keys can be used, for example, to detect the page-up and page-down keys for a PDF viewer. To use this feature, there's a boolean setting in the FlexForm call `return_keyboard_events` that is set to True in order to get keys returned along with buttons. Beginning in version 2.10 you can capture keyboard key presses and mouse scroll-wheel events. Keyboard keys can be used, for example, to detect the page-up and page-down keys for a PDF viewer. To use this feature, there's a boolean setting in the Window call `return_keyboard_events` that is set to True in order to get keys returned along with buttons.
Keys and scroll-wheel events are returned in exactly the same way as buttons. Keys and scroll-wheel events are returned in exactly the same way as buttons.
@ -2290,7 +2302,7 @@ Key Sym is a string such as 'Control_L'. The Key Code is a numeric representati
# Recipe for getting keys, one at a time as they are released # Recipe for getting keys, one at a time as they are released
# If want to use the space bar, then be sure and disable the "default focus" # If want to use the space bar, then be sure and disable the "default focus"
with sg.FlexForm("Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form: with sg.Window("Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form:
text_elem = sg.Text("", size=(18,1)) text_elem = sg.Text("", size=(18,1))
layout = [[sg.Text("Press a key or scroll mouse")], layout = [[sg.Text("Press a key or scroll mouse")],
[text_elem], [text_elem],
@ -2314,7 +2326,7 @@ Use realtime keyboard capture by calling
import PySimpleGUI as sg import PySimpleGUI as sg
with sg.FlexForm("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form: with sg.Window("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form:
layout = [[sg.Text("Hold down a key")], layout = [[sg.Text("Hold down a key")],
[sg.Button("OK")]] [sg.Button("OK")]]
@ -2577,6 +2589,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
| 03.05.00 | Sept 20, 2018 - See release notes | 03.05.00 | Sept 20, 2018 - See release notes
| 03.05.01 | Sept 22, 2018 - See release notes | 03.05.01 | Sept 22, 2018 - See release notes
| 03.05.02 | Sept 23, 2018 - See release notes | 03.05.02 | Sept 23, 2018 - See release notes
| 03.06.00 | Sept 23, 2018 - Goodbye FlexForm, hello Window
### Release Notes ### Release Notes
2.3 - Sliders, Listbox's and Image elements (oh my!) 2.3 - Sliders, Listbox's and Image elements (oh my!)
@ -2599,9 +2612,9 @@ Listboxes are still without scrollwheels. The mouse can drag to see more items.
3.0 We've come a long way baby! Time for a major revision bump. One reason is that the numbers started to confuse people the latest release was 2.30, but some people read it as 2.3 and thought it went backwards. I kinda messed up the 2.x series of numbers, so why not start with a clean slate. A lot has happened anyway so it's well earned. 3.0 We've come a long way baby! Time for a major revision bump. One reason is that the numbers started to confuse people the latest release was 2.30, but some people read it as 2.3 and thought it went backwards. I kinda messed up the 2.x series of numbers, so why not start with a clean slate. A lot has happened anyway so it's well earned.
One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn this off for windows with sliders. This feature is enabled in the FlexForm call. One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn this off for windows with sliders. This feature is enabled in the Window call.
Related to the Grab Anywhere feature is the no_titlebar option, again found in the call to FlexForm. Your window will be a spiffy, borderless window. It's a really interesting effect. Slight problem is that you do not have an icon on the taskbar with these types of windows, so if you don't supply a button to close the window, there's no way to close it other than task manager. Related to the Grab Anywhere feature is the no_titlebar option, again found in the call to Window. Your window will be a spiffy, borderless window. It's a really interesting effect. Slight problem is that you do not have an icon on the taskbar with these types of windows, so if you don't supply a button to close the window, there's no way to close it other than task manager.
3.0.2 Still making changes to Update methods with many more ahead in the future. Continue to mess with grab anywhere option. Needed to disable in more places such as the PopupGetText function. Any time these is text input on a form, you generally want to turn off the grab anywhere feature. 3.0.2 Still making changes to Update methods with many more ahead in the future. Continue to mess with grab anywhere option. Needed to disable in more places such as the PopupGetText function. Any time these is text input on a form, you generally want to turn off the grab anywhere feature.
@ -2663,6 +2676,10 @@ OneLineProgressMeter function added which gives you not only a one-line solution
* Made `Finalize()` in a way that it can be chained * Made `Finalize()` in a way that it can be chained
* Fixed bug in return values from Frame Element contents * Fixed bug in return values from Frame Element contents
#### 3.6.0
* Renamed FlexForm to Window
* Removed LookAndFeel capability from Mac platform.
### Upcoming ### Upcoming
Make suggestions people! Future release features Make suggestions people! Future release features

View File

@ -7,10 +7,10 @@
# PySimpleGUI # PySimpleGUI
![Python Version](https://img.shields.io/badge/PySimpleGUI_Version-3.5.2-red.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_Version-3.6.0-red.svg?longCache=true&style=for-the-badge)
[Wiki for the latest news](https://github.com/MikeTheWatchGuy/PySimpleGUI/wiki) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
[ReadTheDocs](http://pysimplegui.readthedocs.io/) [ReadTheDocs](http://pysimplegui.readthedocs.io/)
@ -18,6 +18,8 @@
[Brief Tutorial](https://pysimplegui.readthedocs.io/en/latest/tutorial/) [Brief Tutorial](https://pysimplegui.readthedocs.io/en/latest/tutorial/)
[OpenSource Article](https://opensource.com/article/18/8/pysimplegui)
[Latest Demos and Master Branch on GitHub](https://github.com/MikeTheWatchGuy/PySimpleGUI) [Latest Demos and Master Branch on GitHub](https://github.com/MikeTheWatchGuy/PySimpleGUI)
Super-simple GUI to use... Powerfully customizable. Super-simple GUI to use... Powerfully customizable.
@ -26,7 +28,13 @@ Home of the 1-line custom GUI and 1-line progress meter
Note - ***Python3*** is required to run PySimpleGUI. It takes advantage of some Python3 features that do not translate well into Python2. Note - ***Python3*** is required to run PySimpleGUI. It takes advantage of some Python3 features that do not translate well into Python2.
Looking to take your Python code from the world of command lines and into the convenience of a GUI? Have a Raspberry **Pi** with a touchscreen that's going to waste because you don't have the time to learn a GUI SDK? Into Machine Learning and are sick of the command line? How about distributing your Python code to Windows users as a single .EXE file that launches straight into a GUI, much like a WinForms app? Look no further, **you've found your GUI package**. Looking for a GUI package to help with
* Taking your Python code from the world of command lines and into the convenience of a GUI? *
* Have a Raspberry **Pi** with a touchscreen that's going to waste because you don't have the time to learn a GUI SDK?
* Into Machine Learning and are sick of the command line?
* How about distributing your Python code to Windows users as a single .EXE file that launches straight into a GUI, much like a WinForms app?
Look no further, **you've found your GUI package**.
import PySimpleGUI as sg import PySimpleGUI as sg
@ -40,7 +48,7 @@ Or how about a ***custom GUI*** in 1 line of code?
import PySimpleGUI as sg import PySimpleGUI as sg
button, (filename,) = sg.FlexForm('Get filename example'). LayoutAndRead([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ]) button, (filename,) = sg.Window('Get filename example'). LayoutAndRead([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ])
![get filename](https://user-images.githubusercontent.com/13696193/44960039-f1018880-aec5-11e8-8a43-3d7f8ff93b67.jpg) ![get filename](https://user-images.githubusercontent.com/13696193/44960039-f1018880-aec5-11e8-8a43-3d7f8ff93b67.jpg)
@ -119,6 +127,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad
Checkboxes Checkboxes
Radio Buttons Radio Buttons
Listbox Listbox
Option Menu
Slider Slider
Graph Graph
Frame with title Frame with title
@ -126,9 +135,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad
Multi-line Text Input Multi-line Text Input
Scroll-able Output Scroll-able Output
Images Images
Progress Bar Progress Bar Async/Non-Blocking Windows
Calendar chooser
Async/Non-Blocking Windows
Tabbed forms Tabbed forms
Persistent Windows Persistent Windows
Redirect Python Output/Errors to scrolling window Redirect Python Output/Errors to scrolling window
@ -188,7 +195,7 @@ An example of many widgets used on a single form. A little further down you'll
sg.FolderBrowse()], sg.FolderBrowse()],
[sg.Submit(), sg.Cancel(), sg.Button('Customized', button_color=('white', 'green'))]] [sg.Submit(), sg.Cancel(), sg.Button('Customized', button_color=('white', 'green'))]]
button, values = sg.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40, 1)).LayoutAndRead(layout) button, values = sg.Window('Everything bagel', auto_size_text=True, default_element_size=(40, 1)).LayoutAndRead(layout)
@ -582,7 +589,7 @@ Finally we can put it all together into a program that will display our window.
[sg.Input()], [sg.Input()],
[sg.OK()] ] [sg.OK()] ]
button, (number,) = sg.FlexForm('Enter a number example').LayoutAndRead(layout) button, (number,) = sg.Window('Enter a number example').LayoutAndRead(layout)
sg.Popup(button, number) sg.Popup(button, number)
@ -600,7 +607,7 @@ Writing the code for this one is just as straightforward. There is one tricky t
[sg.Input(), sg.FileBrowse()], [sg.Input(), sg.FileBrowse()],
[sg.OK(), sg.Cancel()] ] [sg.OK(), sg.Cancel()] ]
button, (number,) = sg.FlexForm('Get filename example').LayoutAndRead(layout) button, (number,) = sg.Window('Get filename example').LayoutAndRead(layout)
sg.Popup(button, number) sg.Popup(button, number)
@ -622,7 +629,7 @@ This is the most basic design pattern. Use this for forms that are shown to the
[sg.InputText(), sg.FileBrowse()], [sg.InputText(), sg.FileBrowse()],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
form = sg.FlexForm('SHA-1 & 256 Hash') form = sg.Window('SHA-1 & 256 Hash')
button, (source_filename,) = form.LayoutAndRead(form_rows) button, (source_filename,) = form.LayoutAndRead(form_rows)
@ -630,20 +637,20 @@ This is the most basic design pattern. Use this for forms that are shown to the
## Pattern 2 - Single-read form "chained" ## Pattern 2 - Single-read form "chained"
Python has a ***beautiful*** way of compacting code known as "chaining". You take the output from one function and feed it as input to the next. Notice in the first example how a form is first obtained by calling FlexForm and then that form is then read. It's possible to combine the creation of the form with the read. This design pattern does exactly that, chain together the form creation and the form reading. Python has a ***beautiful*** way of compacting code known as "chaining". You take the output from one function and feed it as input to the next. Notice in the first example how a form is first obtained by calling Window and then that form is then read. It's possible to combine the creation of the form with the read. This design pattern does exactly that, chain together the form creation and the form reading.
form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')], form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')],
[sg.InputText(), sg.FileBrowse()], [sg.InputText(), sg.FileBrowse()],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
button, (source_filename,) = sg.FlexForm('SHA-1 & 256 Hash').LayoutAndRead(form_rows) button, (source_filename,) = sg.Window('SHA-1 & 256 Hash').LayoutAndRead(form_rows)
## Pattern 3 - Persistent form (multiple reads) ## Pattern 3 - Persistent form (multiple reads)
Some of the more advanced programs operate with the form remaining visible on the screen. Input values are collected, but rather than closing the form, it is kept visible acting as a way to both output information to the user and gather input data. Some of the more advanced programs operate with the form remaining visible on the screen. Input values are collected, but rather than closing the form, it is kept visible acting as a way to both output information to the user and gather input data.
This is done by splitting the LayoutAndRead call apart into a Layout call and a Read call. Note how chaining is again used. In this case a form is created by calling FlexForm which is then passed on to the Layout method. The Layout method returns the form value so that it can be stored and used later in the program to Read the form. This is done by splitting the LayoutAndRead call apart into a Layout call and a Read call. Note how chaining is again used. In this case a form is created by calling Window which is then passed on to the Layout method. The Layout method returns the form value so that it can be stored and used later in the program to Read the form.
import PySimpleGUI as sg import PySimpleGUI as sg
@ -652,7 +659,7 @@ This is done by splitting the LayoutAndRead call apart into a Layout call and a
[sg.RButton('Turn LED Off')], [sg.RButton('Turn LED Off')],
[sg.Exit()]] [sg.Exit()]]
form = sg.FlexForm('Raspberry Pi GUI').Layout(layout) form = sg.Window('Raspberry Pi GUI').Layout(layout)
while True: while True:
button, values = form.Read() button, values = form.Read()
@ -675,7 +682,7 @@ The key to custom forms in PySimpleGUI is to view forms as ROWS of Elements. E
[sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()], [sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
[sg.Submit(), sg.Cancel()]] [sg.Submit(), sg.Cancel()]]
form = sg.FlexForm('Rename Files or Folders') form = sg.Window('Rename Files or Folders')
button, (folder_path, file_path) = form.LayoutAndRead(layout) button, (folder_path, file_path) = form.LayoutAndRead(layout)
@ -719,7 +726,7 @@ Isn't this what almost every Python programmer looking for a GUI wants?? Someth
By default return values are a list of values, one entry for each input field. By default return values are a list of values, one entry for each input field.
Return information from FlexForm, SG's primary form builder interface, is in this format: Return information from Window, SG's primary form builder interface, is in this format:
button, (value1, value2, ...) button, (value1, value2, ...)
@ -766,7 +773,7 @@ If **any** element in the form has a `key`, then **all** of the return values ar
Let's take a look at your first dictionary-based form. Let's take a look at your first dictionary-based form.
import PySimpleGUI as sg import PySimpleGUI as sg
form = sg.FlexForm('Simple data entry form') form = sg.Window('Simple data entry form')
layout = [ layout = [
[sg.Text('Please enter your Name, Address, Phone')], [sg.Text('Please enter your Name, Address, Phone')],
[sg.Text('Name', size=(15, 1)), sg.InputText('1', key='name')], [sg.Text('Name', size=(15, 1)), sg.InputText('1', key='name')],
@ -823,7 +830,7 @@ This little program has a typical Event Loop
[sg.RButton('Turn LED Off')], [sg.RButton('Turn LED Off')],
[sg.Exit()]] [sg.Exit()]]
form = sg.FlexForm('Raspberry Pi).Layout(layout) form = sg.Window('Raspberry Pi).Layout(layout)
# ---- Event Loop ---- # # ---- Event Loop ---- #
while True: while True:
@ -900,7 +907,7 @@ This code utilizes as many of the elements in one form as possible.
] ]
form = sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout) form = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout)
button, values = form.Read() button, values = form.Read()
@ -941,11 +948,11 @@ NON-BLOCKING form call:
### Beginning a Form ### Beginning a Form
The first step is to create the form object using the desired form customization. The first step is to create the form object using the desired form customization.
with FlexForm('Everything bagel', auto_size_text=True, default_element_size=(30,1)) as form: with Window('Everything bagel', auto_size_text=True, default_element_size=(30,1)) as form:
This is the definition of the FlexForm object: This is the definition of the Window object:
def FlexForm(title, def Window(title,
default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]), default_element_size=(DEFAULT_ELEMENT_SIZE[0], DEFAULT_ELEMENT_SIZE[1]),
default_button_element_size = (None, None), default_button_element_size = (None, None),
auto_size_text=None, auto_size_text=None,
@ -1037,7 +1044,9 @@ To keep a window on top of all other windows on the screen, set keep_on_top = Tr
Buttons including these types: Buttons including these types:
File Browse File Browse
Folder Browse Folder Browse
Non-closing return Calendar picker
Date Chooser
Read form
Close form Close form
Realtime Realtime
Checkboxes Checkboxes
@ -1047,9 +1056,12 @@ To keep a window on top of all other windows on the screen, set keep_on_top = Tr
Multi-line Text Input Multi-line Text Input
Scroll-able Output Scroll-able Output
Progress Bar Progress Bar
Option Menu
Menu Menu
Frame Frame
Column
Graph Graph
Image
Table Table
Async/Non-Blocking Windows Async/Non-Blocking Windows
Tabbed forms Tabbed forms
@ -1533,7 +1545,7 @@ Targets that are specified using a key will find its target element by using the
If the Target is specified using (row, column) then it utilizes a grid system. The rows in your GUI are numbered starting with 0. The target can be specified as a hard coded grid item or it can be relative to the button. If the Target is specified using (row, column) then it utilizes a grid system. The rows in your GUI are numbered starting with 0. The target can be specified as a hard coded grid item or it can be relative to the button.
The (row, col) targeting can only target elements that are in the same "container". Containers are the FlexForm, Column and Frame Elements. A File Browse button located inside of a Column is unable to target elements outside of that Column. The (row, col) targeting can only target elements that are in the same "container". Containers are the Window, Column and Frame Elements. A File Browse button located inside of a Column is unable to target elements outside of that Column.
The default value for `target` is `(ThisRow, -1)`. `ThisRow` is a special value that tells the GUI to use the same row as the button. The Y-value of -1 means the field one value to the left of the button. For a File or Folder Browse button, the field that it fills are generally to the left of the button is most cases. (ThisRow, -1) means the Element to the left of the button, on the same row. The default value for `target` is `(ThisRow, -1)`. `ThisRow` is a special value that tells the GUI to use the same row as the button. The Y-value of -1 means the field one value to the left of the button. For a File or Folder Browse button, the field that it fills are generally to the left of the button is most cases. (ThisRow, -1) means the Element to the left of the button, on the same row.
@ -1640,7 +1652,7 @@ This form has 2 button types. There's the normal "Simple Button" (Quit) and 4 "
Here is the code to make, show and get results from this form: Here is the code to make, show and get results from this form:
form = sg.FlexForm('Robotics Remote Control', auto_size_text=True) form = sg.Window('Robotics Remote Control', auto_size_text=True)
form_rows = [[sg.Text('Robotics Remote Control')], form_rows = [[sg.Text('Robotics Remote Control')],
[sg.T(' '*10), sg.RealtimeButton('Forward')], [sg.T(' '*10), sg.RealtimeButton('Forward')],
@ -1706,7 +1718,7 @@ Another way of using a Progress Meter with PySimpleGUI is to build a custom form
[sg.Cancel()]] [sg.Cancel()]]
# create the form` # create the form`
form = sg.FlexForm('Custom Progress Meter').Layout(layout) form = sg.Window('Custom Progress Meter').Layout(layout)
progress_bar = form.FindElement('progressbar') progress_bar = form.FindElement('progressbar')
# loop that would normally do something useful # loop that would normally do something useful
for i in range(10000): for i in range(10000):
@ -1737,7 +1749,7 @@ Here's a complete solution for a chat-window using an Async form with an Output
sg.RButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0])), sg.RButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0])),
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]] sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
form = sg.FlexForm('Chat Window', default_element_size=(30, 2)).Layout(layout) form = sg.Window('Chat Window', default_element_size=(30, 2)).Layout(layout)
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- # # ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
while True: while True:
@ -1778,7 +1790,7 @@ This code produced the above window.
# Prior to the Column element, this layout was not possible # 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. # Columns layouts look identical to form layouts, they are a list of lists of elements.
form = sg.FlexForm('Columns') # blank form form = sg.Window('Columns') # blank form
# Column layout # Column layout
col = [[sg.Text('col Row 1')], col = [[sg.Text('col Row 1')],
@ -1796,7 +1808,7 @@ This code produced the above window.
# Display the form and get values # Display the form and get values
# If you're willing to not use the "context manager" design pattern, then it's possible # 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. # 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) button, values = sg.Window('Compact 1-line form with column').LayoutAndRead(layout)
sg.Popup(button, values, line_width=200) sg.Popup(button, values, line_width=200)
@ -1839,7 +1851,7 @@ This code creates a form with a Frame and 2 buttons.
[sg.Submit(), sg.Cancel()] [sg.Submit(), sg.Cancel()]
] ]
form = sg.FlexForm('Frame with buttons', font=("Helvetica", 12)).Layout(layout) form = sg.Window('Frame with buttons', font=("Helvetica", 12)).Layout(layout)
![frame element](https://user-images.githubusercontent.com/13696193/45889173-c2245700-bd8d-11e8-8f73-1e5f1be3ddb1.jpg) ![frame element](https://user-images.githubusercontent.com/13696193/45889173-c2245700-bd8d-11e8-8f73-1e5f1be3ddb1.jpg)
@ -1874,7 +1886,7 @@ The order of operations to obtain a tkinter Canvas Widget is:
[sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]] [sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]]
# create the form and show it without the plot # create the form and show it without the plot
form = sg.FlexForm('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize() form = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize()
# add the plot to the window # add the plot to the window
@ -1964,7 +1976,7 @@ Tabbed forms are shown using the `ShowTabbedForm` call. The call has the format
(form,layout,'Tab 1 label'), (form,layout,'Tab 1 label'),
(form2,layout2, 'Tab 2 label'), ...) (form2,layout2, 'Tab 2 label'), ...)
Each of the tabs of the form is in fact a form. The same steps are taken to create the form as before. A `FlexForm` is created, then rows are filled with Elements, and finally the form is shown. When calling `ShowTabbedForm`, each form is passed in as a tuple. The tuple has the format: `(the form, the rows, a string shown on the tab)` Each of the tabs of the form is in fact a form. The same steps are taken to create the form as before. A `Window` is created, then rows are filled with Elements, and finally the form is shown. When calling `ShowTabbedForm`, each form is passed in as a tuple. The tuple has the format: `(the form, the rows, a string shown on the tab)`
Results are returned as a list of lists. For each form you'll get a list that's in the same format as a normal form. A single tab's values would be: Results are returned as a list of lists. For each form you'll get a list that's in the same format as a normal form. A single tab's values would be:
@ -2150,7 +2162,7 @@ We're going to build an app that does the latter. It's going to update our form
The basic flow and functions you will be calling are: The basic flow and functions you will be calling are:
Setup Setup
form = FlexForm() form = Window()
form_rows = ..... form_rows = .....
form.LayoutAndRead(form_rows, non_blocking=True) form.LayoutAndRead(form_rows, non_blocking=True)
@ -2176,7 +2188,7 @@ See the sample code on the GitHub named Demo Media Player for another example of
# form that doesn't block # form that doesn't block
# Make a form, but don't use context manager # Make a form, but don't use context manager
form = sg.FlexForm('Running Timer', auto_size_text=True) form = sg.Window('Running Timer', auto_size_text=True)
# Create the layout # Create the layout
form_rows = [[sg.Text('Non-blocking GUI with updates')], form_rows = [[sg.Text('Non-blocking GUI with updates')],
@ -2233,7 +2245,7 @@ In some programs these updates happen in response to another Element. This prog
sg.Text("Aa", size=(2, 1), font="Helvetica " + str(fontSize), key='text')]] sg.Text("Aa", size=(2, 1), font="Helvetica " + str(fontSize), key='text')]]
sz = fontSize sz = fontSize
form = sg.FlexForm("Font size selector", grab_anywhere=False).Layout(layout) form = sg.Window("Font size selector", grab_anywhere=False).Layout(layout)
# Event Loop # Event Loop
while True: while True:
button, values= form.Read() button, values= form.Read()
@ -2273,7 +2285,7 @@ The takeaway from this exercise is that keys are key in PySimpleGUI's design. T
## Keyboard & Mouse Capture ## Keyboard & Mouse Capture
Beginning in version 2.10 you can capture keyboard key presses and mouse scroll-wheel events. Keyboard keys can be used, for example, to detect the page-up and page-down keys for a PDF viewer. To use this feature, there's a boolean setting in the FlexForm call `return_keyboard_events` that is set to True in order to get keys returned along with buttons. Beginning in version 2.10 you can capture keyboard key presses and mouse scroll-wheel events. Keyboard keys can be used, for example, to detect the page-up and page-down keys for a PDF viewer. To use this feature, there's a boolean setting in the Window call `return_keyboard_events` that is set to True in order to get keys returned along with buttons.
Keys and scroll-wheel events are returned in exactly the same way as buttons. Keys and scroll-wheel events are returned in exactly the same way as buttons.
@ -2290,7 +2302,7 @@ Key Sym is a string such as 'Control_L'. The Key Code is a numeric representati
# Recipe for getting keys, one at a time as they are released # Recipe for getting keys, one at a time as they are released
# If want to use the space bar, then be sure and disable the "default focus" # If want to use the space bar, then be sure and disable the "default focus"
with sg.FlexForm("Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form: with sg.Window("Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form:
text_elem = sg.Text("", size=(18,1)) text_elem = sg.Text("", size=(18,1))
layout = [[sg.Text("Press a key or scroll mouse")], layout = [[sg.Text("Press a key or scroll mouse")],
[text_elem], [text_elem],
@ -2314,7 +2326,7 @@ Use realtime keyboard capture by calling
import PySimpleGUI as sg import PySimpleGUI as sg
with sg.FlexForm("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form: with sg.Window("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False) as form:
layout = [[sg.Text("Hold down a key")], layout = [[sg.Text("Hold down a key")],
[sg.Button("OK")]] [sg.Button("OK")]]
@ -2577,6 +2589,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
| 03.05.00 | Sept 20, 2018 - See release notes | 03.05.00 | Sept 20, 2018 - See release notes
| 03.05.01 | Sept 22, 2018 - See release notes | 03.05.01 | Sept 22, 2018 - See release notes
| 03.05.02 | Sept 23, 2018 - See release notes | 03.05.02 | Sept 23, 2018 - See release notes
| 03.06.00 | Sept 23, 2018 - Goodbye FlexForm, hello Window
### Release Notes ### Release Notes
2.3 - Sliders, Listbox's and Image elements (oh my!) 2.3 - Sliders, Listbox's and Image elements (oh my!)
@ -2599,9 +2612,9 @@ Listboxes are still without scrollwheels. The mouse can drag to see more items.
3.0 We've come a long way baby! Time for a major revision bump. One reason is that the numbers started to confuse people the latest release was 2.30, but some people read it as 2.3 and thought it went backwards. I kinda messed up the 2.x series of numbers, so why not start with a clean slate. A lot has happened anyway so it's well earned. 3.0 We've come a long way baby! Time for a major revision bump. One reason is that the numbers started to confuse people the latest release was 2.30, but some people read it as 2.3 and thought it went backwards. I kinda messed up the 2.x series of numbers, so why not start with a clean slate. A lot has happened anyway so it's well earned.
One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn this off for windows with sliders. This feature is enabled in the FlexForm call. One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn this off for windows with sliders. This feature is enabled in the Window call.
Related to the Grab Anywhere feature is the no_titlebar option, again found in the call to FlexForm. Your window will be a spiffy, borderless window. It's a really interesting effect. Slight problem is that you do not have an icon on the taskbar with these types of windows, so if you don't supply a button to close the window, there's no way to close it other than task manager. Related to the Grab Anywhere feature is the no_titlebar option, again found in the call to Window. Your window will be a spiffy, borderless window. It's a really interesting effect. Slight problem is that you do not have an icon on the taskbar with these types of windows, so if you don't supply a button to close the window, there's no way to close it other than task manager.
3.0.2 Still making changes to Update methods with many more ahead in the future. Continue to mess with grab anywhere option. Needed to disable in more places such as the PopupGetText function. Any time these is text input on a form, you generally want to turn off the grab anywhere feature. 3.0.2 Still making changes to Update methods with many more ahead in the future. Continue to mess with grab anywhere option. Needed to disable in more places such as the PopupGetText function. Any time these is text input on a form, you generally want to turn off the grab anywhere feature.
@ -2663,6 +2676,10 @@ OneLineProgressMeter function added which gives you not only a one-line solution
* Made `Finalize()` in a way that it can be chained * Made `Finalize()` in a way that it can be chained
* Fixed bug in return values from Frame Element contents * Fixed bug in return values from Frame Element contents
#### 3.6.0
* Renamed FlexForm to Window
* Removed LookAndFeel capability from Mac platform.
### Upcoming ### Upcoming
Make suggestions people! Future release features Make suggestions people! Future release features