Readme update for version 2.4 features. Button images

This commit is contained in:
MikeTheWatchGuy 2018-07-24 15:52:14 -04:00
parent efa5b5fc6b
commit d9d548747b
12 changed files with 338 additions and 158 deletions

BIN
ButtonGraphics/Exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
ButtonGraphics/Loop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
ButtonGraphics/Next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
ButtonGraphics/Pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
ButtonGraphics/Restart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
ButtonGraphics/Rewind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
ButtonGraphics/Stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -1,11 +0,0 @@
import PySimpleGUI as sg
sg.MsgBox('Title', 'My first message... Is the length the same?')
rc, number = sg.GetTextBox('Title goes here', 'Enter a number')
if not rc:
sg.MsgBoxError('You have cancelled')
exit(0)
msg = '\n'.join([f'{i}' for i in range(0,int(number))])
sg.ScrolledTextBox(msg, height=10)

60
Demo Media Player.py Normal file
View File

@ -0,0 +1,60 @@
import PySimpleGUI as sg
#
# An Async Demonstration of a media player
# Uses button images for a super snazzy look
# See how it looks here:
# https://user-images.githubusercontent.com/13696193/43159403-45c9726e-8f50-11e8-9da0-0d272e20c579.jpg
#
def MediaPlayerGUI():
# Images are located in a subfolder in the Demo Media Player.py folder
image_pause = './ButtonGraphics/Pause.png'
image_restart = './ButtonGraphics/Restart.png'
image_next = './ButtonGraphics/Next.png'
image_exit = './ButtonGraphics/Exit.png'
# A text element that will be changed to display messages in the GUI
TextElem = sg.Text('', size=(20, 3), font=("Helvetica", 14))
# 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),
font=("Helvetica", 25))
# define layout of the rows
layout= [[sg.Text('Media File Player', size=(20, 1), font=("Helvetica", 25))],
[TextElem],
[sg.ReadFormButton('Restart Song', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0,
size=(10, 2), font=("Helvetica", 15)), sg.Text(' ' * 2),
sg.ReadFormButton('Pause', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0,
font=("Helvetica", 15), size=(10, 2)), sg.Text(' ' * 2),
sg.ReadFormButton('Next', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0,
size=(10, 2), font=("Helvetica", 15)), sg.Text(' ' * 2),
sg.Text(' ' * 2), sg.SimpleButton('Exit', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_exit, image_size=(50, 50), image_subsample=2, border_width=0,
size=(10, 2), font=("Helvetica", 15))],
[sg.Text('Treble', font=("Helvetica", 15), size=(6, 1)),
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15)),
sg.Text(' ' * 5),
sg.Text('Volume', font=("Helvetica", 15), size=(7, 1)),
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15))],
]
# Call the same LayoutAndRead but indicate the form is non-blocking
form.LayoutAndRead(layout, non_blocking=True)
# Our event loop
while(True):
# Read the form (this call will not block)
button, values = form.ReadNonBlocking()
if button == 'Exit':
break
# If a button was pressed, display it on the GUI by updating the text element
if button:
TextElem.Update(button)
MediaPlayerGUI()

59
Demo NonBlocking Form.py Normal file
View File

@ -0,0 +1,59 @@
import PySimpleGUI as sg
import time
def main():
StatusOutputExample()
# form that doen't block
def StatusOutputExample_context_manager():
with sg.FlexForm('Running Timer', auto_size_text=True) as form:
output_element = sg.Text('', size=(8, 2), font=('Helvetica', 20))
form_rows = [[sg.Text('Non-blocking GUI with updates')],
[output_element],
[sg.SimpleButton('Quit')]]
form.LayoutAndRead(form_rows, non_blocking=True)
for i in range(1, 1000):
output_element.Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i/100), 60), i%100))
button, values = form.ReadNonBlocking()
if values is None or button == 'Quit':
break
time.sleep(.01)
else:
form.CloseNonBlockingForm()
# form that doen't block
def StatusOutputExample():
# Make a form, but don't use context manager
form = sg.FlexForm('Running Timer', auto_size_text=True)
# Create a text element that will be updated with status information on the GUI itself
output_element = sg.Text('', size=(8, 2), font=('Helvetica', 20))
# Create the rows
form_rows = [[sg.Text('Non-blocking GUI with updates')],
[output_element],
[sg.SimpleButton('Quit')]]
# Layout the rows of the form and perform a read. Indicate the form is non-blocking!
form.LayoutAndRead(form_rows, non_blocking=True)
#
# Some place later in your code...
# You need to perform a ReadNonBlocking on your form every now and then or
# else it won't refresh
#
for i in range(1, 1000):
output_element.Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100))
button, values = form.ReadNonBlocking()
if values is None or button == 'Quit':
break
time.sleep(.01)
else:
form.CloseNonBlockingForm()
if __name__ == '__main__':
main()

Binary file not shown.

364
readme.md
View File

@ -1,16 +1,19 @@
![logo01 2 _2](https://user-images.githubusercontent.com/13696193/43082437-1252511a-8e62-11e8-9150-fc227cc56cfe.png) ![logo01 2 _2](https://user-images.githubusercontent.com/13696193/43082437-1252511a-8e62-11e8-9150-fc227cc56cfe.png)
[![Downloads](http://pepy.tech/badge/pysimplegui)](http://pepy.tech/project/pysimplegui) since Jul 11, 2018 [![Downloads](http://pepy.tech/badge/pysimplegui)](http://pepy.tech/project/pysimplegui) since Jul 11, 2018
# PySimpleGUI # PySimpleGUI
(Ver 2.3) (Ver 2.4)
This really is a simple GUI, but also powerfully customizable. This really is a simple GUI, but also powerfully customizable.
import PySimpleGUI as SG
SG.MsgBox('My Message Box', 'This is the shortest GUI program ever!') import PySimpleGUI as sg
![snap0102](https://user-images.githubusercontent.com/13696193/42781058-1d28d9fa-8913-11e8-847e-5c2afc16ca4c.jpg) sg.MsgBox('Hello From PySimpleGUI!', 'This is the shortest GUI program ever!')
![snap0136](https://user-images.githubusercontent.com/13696193/43162494-33095ece-8f59-11e8-86de-b6d8bcc5a52f.jpg)
Add a Progress Meter to your code with ONE LINE of code Add a Progress Meter to your code with ONE LINE of code
@ -18,9 +21,13 @@ Add a Progress Meter to your code with ONE LINE of code
![progress meter 2](https://user-images.githubusercontent.com/13696193/42695896-a37eff5c-8684-11e8-8fbb-3d756655a44b.jpg) ![progress meter 2](https://user-images.githubusercontent.com/13696193/42695896-a37eff5c-8684-11e8-8fbb-3d756655a44b.jpg)
Or how about a media player GUI with custom buttons... in 30 lines of code.
![media file player](https://user-images.githubusercontent.com/13696193/43161977-9ee7cace-8f57-11e8-8ff8-3ea24b69dab9.jpg)
I was frustrated by having to deal with the dos prompt when I had a powerful Windows machine right in front of me. Why is it SO difficult to do even the simplest of input/output to a window in Python?? I was frustrated by having to deal with the dos prompt when I had a powerful Windows machine right in front of me. Why is it SO difficult to do even the simplest of input/output to a window in Python??
There are a number of 'easy to use' Python GUIs, but they're **very** limiting. PySimpleGUI takes the best of packages like `EasyGUI`(no longer maintained) and `WxSimpleGUI` (a great package, but limited). The primary difference between these and PySimpleGUI is that in addition to getting those simple Message Boxes you also get the ability to make your own forms that are highly customizeable. Don't like the standard Message Box? Then make your own! There are a number of 'easy to use' Python GUIs, but they're **very** limiting. PySimpleGUI takes the best of packages like `EasyGUI`and `WxSimpleGUI` , both really handy but limited. The primary difference between these and PySimpleGUI is that in addition to getting the simple Message Boxes you also get the ability to make your own forms that are highly customizeable. Don't like the standard Message Box? Then make your own!
Every call has optional parameters so that you can change the look and feel. Don't like the button color? It's easy to change by adding a button_color parameter to your widget. Every call has optional parameters so that you can change the look and feel. Don't like the button color? It's easy to change by adding a button_color parameter to your widget.
@ -100,7 +107,7 @@ You will see a number of different styles of buttons, data entry fields, etc, in
Be Pythonic... Python's lists in particular worked out really well: Be Pythonic... Python's lists in particular worked out really well:
- Forms are represented as Python lists. - Forms are represented as Python lists.
- A form is a list of rows - A form is a list of rows
- A row is a list of elements - A row is a list of elements
- Return values are a list - Return values are a list
@ -290,7 +297,7 @@ A meter AND fun statistics to watch while your machine grinds away, all for the
With a little trickery you can provide a way to break out of your loop using the Progress Meter form. The cancel button results in a `False` return value from `EasyProgressMeter`. It normally returns `True`. With a little trickery you can provide a way to break out of your loop using the Progress Meter form. The cancel button results in a `False` return value from `EasyProgressMeter`. It normally returns `True`.
if not SG.EasyProgressMeter('My Meter', i+1, 10000, 'Optional message'): if not SG.EasyProgressMeter('My Meter', i+1, 10000, 'Optional message'):
break break
***Be sure and add one to your loop counter*** so that your counter goes from 1 to the max value. If you do not add one, your counter will never hit the max value. Instead it will go from 0 to max-1. ***Be sure and add one to your loop counter*** so that your counter goes from 1 to the max value. If you do not add one, your counter will never hit the max value. Instead it will go from 0 to max-1.
#### Debug Output #### Debug Output
@ -336,24 +343,27 @@ It's both not enjoyable nor helpful to immediately jump into tweaking each and e
# Copy these design patterns! # Copy these design patterns!
## Pattern 1 - With Context Manager ## Pattern 1 - With Context Manager
with SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True) as form:
form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')], with sg.FlexForm('SHA-1 & 256 Hash', auto_size_text=True) as form:
[SG.InputText(), SG.FileBrowse()], form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')],
[SG.Submit(), SG.Cancel()]] [sg.InputText(), sg.FileBrowse()],
button, (source_filename, ) = form.LayoutAndShow(form_rows) [sg.Submit(), sg.Cancel()]]
button, (source_filename,) = form.LayoutAndRead(form_rows)
## Pattern 2 - No Context Manager ## Pattern 2 - No Context Manager
form = SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True)
form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')], form = sg.FlexForm('SHA-1 & 256 Hash', auto_size_text=True)
[SG.InputText(), SG.FileBrowse()], form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')],
[SG.Submit(), SG.Cancel()]] [sg.InputText(), sg.FileBrowse()],
button, (source_filename,) = form.LayoutAndShow(form_rows) [sg.Submit(), sg.Cancel()]]
button, (source_filename,) = form.LayoutAndRead(form_rows)
These 2 design patters both produce this custom form: These 2 design patters both produce this custom form:
![sha hash](https://user-images.githubusercontent.com/13696193/42603149-a56acf3a-853a-11e8-91de-771efd3a65a8.jpg) ![snap0134](https://user-images.githubusercontent.com/13696193/43162410-e7775466-8f58-11e8-8d6a-da4772c00dd8.jpg)
It's important to use the "with" context manager so that resources are freed as quickly as possible, using the currently executing thread. PySimpleGUI uses `tkinter`. `tkinter` is very picky about who releases objects and when. The `with` takes care of disposing of everything properly for you. It's important to use the "with" context manager so that resources are freed as quickly as possible, using the currently executing thread. PySimpleGUI uses `tkinter`. `tkinter` is very picky about who releases objects and when. The `with` takes care of disposing of everything properly for you.
@ -374,11 +384,11 @@ The next few rows of code lay out the rows of elements in the window to be displ
[SG.InputText(), SG.FileBrowse()], [SG.InputText(), SG.FileBrowse()],
Now we're on the second row of the form. On this row there are 2 elements. The first is an `Input` field. It's a place the user can enter `strings`. The second element is a `File Browse Button`. A file or folder browse button will always fill in the text field to it's left unless otherwise specified. In this example, the File Browse Button will interact with the `InputText` field to its left. Now we're on the second row of the form. On this row there are 2 elements. The first is an `Input` field. It's a place the user can enter `strings`. The second element is a `File Browse Button`. A file or folder browse button will always fill in the text field to it's left unless otherwise specified. In this example, the File Browse Button will interact with the `InputText` field to its left.
[SG.Submit(), SG.Cancel()]] [SG.Submit(), SG.Cancel()]]
The last line of the `form_rows` variable assignment contains a Submit and a Cancel Button. These are buttons that will cause a form to return its value to the caller. The last line of the `form_rows` variable assignment contains a Submit and a Cancel Button. These are buttons that will cause a form to return its value to the caller.
(button, (source_filename, )) = form.LayoutAndShow(form_rows) (button, (source_filename, )) = form.LayoutAndRead(form_rows)
This is the code that **displays** the form, collects the information and returns the data collected. In this example we have a button return code and only 1 input field This is the code that **displays** the form, collects the information and returns the data collected. In this example we have a button return code and only 1 input field
@ -393,21 +403,21 @@ This is the code that **displays** the form, collects the information and return
Each of the Elements that are Input Elements will have a value in the list of return values. You can unpack your GUI directly into the variables you want to use. Each of the Elements that are Input Elements will have a value in the list of return values. You can unpack your GUI directly into the variables you want to use.
button, (filename, folder1, folder2, should_overwrite) = form.LayoutAndShow(form_rows) button, (filename, folder1, folder2, should_overwrite) = form.LayoutAndRead(form_rows)
Or, you can unpack the return results separately. Or, you can unpack the return results separately.
button, values = form.LayoutAndShow(form_rows) button, values = form.LayoutAndRead(form_rows)
filename, folder1, folder2, should_overwrite = values filename, folder1, folder2, should_overwrite = values
If you have a SINGLE value being returned, it is written this way: If you have a SINGLE value being returned, it is written this way:
button, (value1,) = form.LayoutAndShow(form_rows) button, (value1,) = form.LayoutAndRead(form_rows)
Another way of parsing the return values is to store the list of values into a variable representing the list of values. Another way of parsing the return values is to store the list of values into a variable representing the list of values.
button, value_list = form.LayoutAndShow(form_rows) button, value_list = form.LayoutAndRead(form_rows)
value1 = value_list[0] value1 = value_list[0]
value2 = value_list[1] value2 = value_list[1]
... ...
@ -466,7 +476,7 @@ You've already seen a number of examples above that use blocking forms. Anytime
NON-BLOCKING form call: NON-BLOCKING form call:
form.Show(non_blocking=True) form.Show(non_blocking=True)
### 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.
@ -490,17 +500,17 @@ This is the definition of the FlexForm object:
Parameter Descriptions. You will find these same parameters specified for each `Element` and some of them in `Row` specifications. The `Element` specified value will take precedence over the `Row` and `Form` values. Parameter Descriptions. You will find these same parameters specified for each `Element` and some of them in `Row` specifications. The `Element` specified value will take precedence over the `Row` and `Form` values.
default_element_size - Size of elements in form in characters (width, height) default_element_size - Size of elements in form in characters (width, height)
auto_size_text - Bool. True is elements should size themselves according to contents auto_size_text - Bool. True is elements should size themselves according to contents
scale - Set size of element to be a multiple of the Element size scale - Set size of element to be a multiple of the Element size
location - Location to place window in pixels location - Location to place window in pixels
button_color - Default color for buttons (foreground, background). Can be text or hex button_color - Default color for buttons (foreground, background). Can be text or hex
progress_bar_color - Foreground and background colors for progress bars progress_bar_color - Foreground and background colors for progress bars
is_tabbed_form - Bool. If True then form is a tabbed form is_tabbed_form - Bool. If True then form is a tabbed form
border_depth - Amount of 'bezel' to put on input boxes, buttons, etc. border_depth - Amount of 'bezel' to put on input boxes, buttons, etc.
auto_close - Bool. If True form will autoclose auto_close - Bool. If True form will autoclose
auto_close_duration - Duration in seconds before form closes auto_close_duration - Duration in seconds before form closes
icon - .ICO file that will appear on the Task Bar and end of Title Bar icon - .ICO file that will appear on the Task Bar and end of Title Bar
#### Sizes #### Sizes
@ -621,10 +631,10 @@ The shorthand functions for `Text` are `Txt` and `T`
This Element doubles as both an input and output Element. The `DefaultText` optional parameter is used to indicate what to output to the window. This Element doubles as both an input and output Element. The `DefaultText` optional parameter is used to indicate what to output to the window.
Multiline(default_text='', Multiline(default_text='',
enter_submits = False, enter_submits = False,
scale=(None, None), scale=(None, None),
size=(None, None), size=(None, None),
auto_size_text=None) auto_size_text=None)
. .
default_text - Text to display in the text box default_text - Text to display in the text box
@ -697,30 +707,30 @@ The standard listbox like you'll find in most GUIs. Note that the return values
![snap0130](https://user-images.githubusercontent.com/13696193/43115859-2fbf0646-8ed3-11e8-9979-bbee8eaebfab.jpg) ![snap0130](https://user-images.githubusercontent.com/13696193/43115859-2fbf0646-8ed3-11e8-9979-bbee8eaebfab.jpg)
Listbox(values, Listbox(values,
select_mode=None, select_mode=None,
scale=(None, None), scale=(None, None),
size=(None, None), size=(None, None),
auto_size_text=None, auto_size_text=None,
font=None) font=None)
. .
values - Choices to be displayed. List of strings values - Choices to be displayed. List of strings
select_mode - Defines how to list is to operate. select_mode - Defines how to list is to operate.
Choices include constants or strings: Choices include constants or strings:
Constants version: Constants version:
LISTBOX_SELECT_MODE_BROWSE LISTBOX_SELECT_MODE_BROWSE
LISTBOX_SELECT_MODE_EXTENDED LISTBOX_SELECT_MODE_EXTENDED
LISTBOX_SELECT_MODE_MULTIPLE LISTBOX_SELECT_MODE_MULTIPLE
LISTBOX_SELECT_MODE_SINGLE - the default LISTBOX_SELECT_MODE_SINGLE - the default
Strings version: Strings version:
'browse' 'browse'
'extended' 'extended'
'multiple' 'multiple'
'single' 'single'
scale - Amount to scale size by scale - Amount to scale size by
size - (width, height) of element in characters size - (width, height) of element in characters
auto_size_text - Bool. True if size should fit the text length auto_size_text - Bool. True if size should fit the text length
The `select_mode` option can be a string or a constant value defined as a variable. Generally speaking strings are used for these kinds of options. The `select_mode` option can be a string or a constant value defined as a variable. Generally speaking strings are used for these kinds of options.
@ -731,30 +741,30 @@ Sliders have a couple of slider-specific settings as well as appearance settings
![snap0129](https://user-images.githubusercontent.com/13696193/43115741-e1cb52c8-8ed2-11e8-80bb-0e99ae846ec1.jpg) ![snap0129](https://user-images.githubusercontent.com/13696193/43115741-e1cb52c8-8ed2-11e8-80bb-0e99ae846ec1.jpg)
Slider(range=(None,None), Slider(range=(None,None),
default_value=None, default_value=None,
orientation=None, orientation=None,
border_width=None, border_width=None,
relief=None, relief=None,
scale=(None, None), scale=(None, None),
size=(None, None), size=(None, None),
font=None): font=None):
. .
range - (min, max) slider's range range - (min, max) slider's range
default_value - default setting (within range) default_value - default setting (within range)
orientation - 'horizontal' or 'vertical' ('h' or 'v' work) orientation - 'horizontal' or 'vertical' ('h' or 'v' work)
border_width - how deep the widget looks border_width - how deep the widget looks
relief - relief style. Values are same as progress meter relief values. Can be a constant or a string: relief - relief style. Values are same as progress meter relief values. Can be a constant or a string:
RELIEF_RAISED= 'raised' RELIEF_RAISED= 'raised'
RELIEF_SUNKEN= 'sunken' RELIEF_SUNKEN= 'sunken'
RELIEF_FLAT= 'flat' RELIEF_FLAT= 'flat'
RELIEF_RIDGE= 'ridge' RELIEF_RIDGE= 'ridge'
RELIEF_GROOVE= 'groove' RELIEF_GROOVE= 'groove'
RELIEF_SOLID = 'solid' RELIEF_SOLID = 'solid'
scale - Amount to scale size by scale - Amount to scale size by
size - (width, height) of element in characters size - (width, height) of element in characters
auto_size_text - Bool. True if size should fit the text auto_size_text - Bool. True if size should fit the text
#### Radio Button Element #### Radio Button Element
Creates one radio button that is assigned to a group of radio buttons. Only 1 of the buttons in the group can be selected at any one time. Creates one radio button that is assigned to a group of radio buttons. Only 1 of the buttons in the group can be selected at any one time.
@ -897,6 +907,26 @@ layout = [[SG.SimpleButton('My Button')]]
All buttons can have their text changed by changing the `button_text` variable. All buttons can have their text changed by changing the `button_text` variable.
**Button Images**
Now this is an exciting feature not found in many simplified packages.... images on buttons! You can make a pretty spiffy user interface with the help of a few button images.
Your button images need to be in PNG or GIF format. When you make a button with an image, set the button background to the same color as the background. There's a button color TRANSPARENT_BUTTON that you can set your button color to.
This example comes from the `Demo Media Player.py` example program. Because it's a non-blocking button, it's defined as `ReadFormButton`. You also put images on blocking buttons by using `SimpleButton`.
sg.ReadFormButton('Restart Song', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0)
Three parameters are used for button images.
image_filename - Filename. Can be a relative path
image_size - Size of image file in pixels
image_subsample - Amount to divide the size by. 2 means your image will be 1/2 the size. 3 means 1/3
This is one you'll have to experiment with at this point. Not up for an exhaustive explanation.
**File Types** **File Types**
The `FileBrowse` button has an additional setting named `file_types`. This variable is used to filter the files shown in the file dialog box. The default value for this setting is The `FileBrowse` button has an additional setting named `file_types`. This variable is used to filter the files shown in the file dialog box. The default value for this setting is
@ -928,14 +958,14 @@ If you want a bit more customization of your meter, then you can go up 1 level a
You setup the progress meter by calling You setup the progress meter by calling
my_meter = ProgressMeter(title, my_meter = ProgressMeter(title,
max_value, max_value,
*args, *args,
orientantion=None, orientantion=None,
bar_color=DEFAULT_PROGRESS_BAR_COLOR, bar_color=DEFAULT_PROGRESS_BAR_COLOR,
button_color=None, button_color=None,
size=DEFAULT_PROGRESS_BAR_SIZE, size=DEFAULT_PROGRESS_BAR_SIZE,
scale=(None, None), scale=(None, None),
border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH) border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH)
Then to update the bar within your loop Then to update the bar within your loop
return_code = ProgressMeterUpdate(my_meter, return_code = ProgressMeterUpdate(my_meter,
@ -968,10 +998,10 @@ Here's a complete solution for a chat-window using an Async form with an Output
[SG.Output(size=(80, 20))], [SG.Output(size=(80, 20))],
[SG.Multiline(size=(70, 5), enter_submits=True), SG.ReadFormButton('SEND', button_color=(SG.YELLOWS[0], SG.BLUES[0])), SG.SimpleButton('EXIT', button_color=(SG.YELLOWS[0], SG.GREENS[0]))]] [SG.Multiline(size=(70, 5), enter_submits=True), SG.ReadFormButton('SEND', button_color=(SG.YELLOWS[0], SG.BLUES[0])), SG.SimpleButton('EXIT', button_color=(SG.YELLOWS[0], SG.GREENS[0]))]]
# notice this is NOT the usual LayoutAndRead call because you don't yet want to read the form # notice this is NOT the usual LayoutAndRead call because you don't yet want to read the form
# if you call LayoutAndRead from here, then you will miss the first button click # if you call LayoutAndRead from here, then you will miss the first button click
form.Layout(layout) form.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:
button, value = form.Read() button, value = form.Read()
if button == 'SEND': if button == 'SEND':
print(value) print(value)
@ -979,8 +1009,6 @@ Here's a complete solution for a chat-window using an Async form with an Output
break break
## Tabbed Forms ## Tabbed Forms
Tabbed forms are shown using the `ShowTabbedForm` call. The call has the format Tabbed forms are shown using the `ShowTabbedForm` call. The call has the format
@ -1013,22 +1041,22 @@ Let's have some fun customizing! Make PySimpleGUI look the way you want it to l
Explanation of parameters Explanation of parameters
icon - filename of icon used for taskbar and title bar icon - filename of icon used for taskbar and title bar
button_color - button color (foreground, background) button_color - button color (foreground, background)
element_size - element size (width, height) in characters element_size - element size (width, height) in characters
margins - tkinter margins around outsize margins - tkinter margins around outsize
element_padding - tkinter padding around each element element_padding - tkinter padding around each element
auto_size_text - autosize the elements to fit their text auto_size_text - autosize the elements to fit their text
font - font used for elements font - font used for elements
border_width - amount of bezel or border around sunken or raised elements border_width - amount of bezel or border around sunken or raised elements
slider_border_width - changes the way sliders look slider_border_width - changes the way sliders look
slider_relief - changes the way sliders look slider_relief - changes the way sliders look
slider_orientation - changes orientation of slider slider_orientation - changes orientation of slider
autoclose_time - time in seconds for autoclose boxes autoclose_time - time in seconds for autoclose boxes
message_box_line_width - number of characers in a line of text in message boxes message_box_line_width - number of characers in a line of text in message boxes
progress_meter_border_depth - amount of border around raised or lowered progress meters progress_meter_border_depth - amount of border around raised or lowered progress meters
text_justification - justification to use on Text Elements. Values are strings - 'left', 'right', 'center' text_justification - justification to use on Text Elements. Values are strings - 'left', 'right', 'center'
debug_win_size - size of the Print output window debug_win_size - size of the Print output window
These settings apply to all forms `SetOptions`. The Row options and Element options will take precedence over these settings. Settings can be thought of as levels of settings with the Form-level being the highest and the Element-level the lowest. Thus the levels are: These settings apply to all forms `SetOptions`. The Row options and Element options will take precedence over these settings. Settings can be thought of as levels of settings with the Form-level being the highest and the Element-level the lowest. Thus the levels are:
@ -1052,18 +1080,20 @@ Word of warning... version 2.2, the currently released, and upcoming version 2.3
The proper code to check if the user has exited the form will be a polling-loop that looks something like this: The proper code to check if the user has exited the form will be a polling-loop that looks something like this:
while True: while True:
button, values = form.ReadNonBlocking() button, values = form.ReadNonBlocking()
if values is None or button == 'Quit': if values is None or button == 'Quit':
break break
We're going to build an app that does the latter. It's going to update our form with a running clock. We're going to build an app that does the latter. It's going to update our form with a running clock.
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.AddRows(form_rows) form = FlexForm()
form.Show(non_blocking = True) form_rows = .....
form.LayoutAndRead(form_rows, non_blocking=True)
Periodic refresh Periodic refresh
@ -1072,29 +1102,45 @@ If you need to close the form
form.CloseNonBlockingForm() form.CloseNonBlockingForm()
Rather than the usual `form.LayoutAndShow()` call, we're manually adding the rows (doing the layout) and then showing the form. After the form is shown, you simply call `form.ReadNonBlocking()` every now and then. Rather than the usual `form.LayoutAndRead()` call, we're manually adding the rows (doing the layout) and then showing the form. After the form is shown, you simply call `form.ReadNonBlocking()` every now and then.
When you are ready to close the form (assuming the form wasn't closed by the user or a button click) you simply call `form.CloseNonBlockingForm()` When you are ready to close the form (assuming the form wasn't closed by the user or a button click) you simply call `form.CloseNonBlockingForm()`
**Example - Running timer that updates** **Example - Running timer that updates**
We're going to make a form and update one of the elements of that form every .01 seconds. Here's the entire code to do that. See the sample code on the GitHub named Demo Media Player for another example of Async Forms. We're going to make a form and update one of the elements of that form every .01 seconds. Here's the entire code to do that.
import PySimpleGUI as sg
import time
# form that doesn't block
# Make a form, but don't use context manager
form = sg.FlexForm('Running Timer', auto_size_text=True)
# Create a text element that will be updated with status information on the GUI itself
output_element = sg.Text('', size=(8, 2), font=('Helvetica', 20))
# Create the rows
form_rows = [[sg.Text('Non-blocking GUI with updates')],
[output_element],
[sg.SimpleButton('Quit')]]
# Layout the rows of the form and perform a read. Indicate the form is non-blocking!
form.LayoutAndRead(form_rows, non_blocking=True)
#
# Some place later in your code...
# You need to perform a ReadNonBlocking on your form every now and then or
# else it won't refresh
#
for i in range(1, 1000):
output_element.Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100))
button, values = form.ReadNonBlocking()
if values is None or button == 'Quit':
break
time.sleep(.01)
else:
form.CloseNonBlockingForm()
with SG.FlexForm('Running Timer', auto_size_text=True) as form:
output_element = SG.Text('', size=(8, 2), font=('Helvetica', 20))
form_rows = [[SG.Text('Non-blocking GUI with updates')],
[output_element],
[SG.SimpleButton('Quit')]]
form.AddRows(form_rows)
form.Show(non_blocking=True)
for i in range(1, 100):
output_element.Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i/100), 60), i%100))
button, values = form.ReadNonBlocking()
if values is None or button == 'Quit':
break
time.sleep(.01)
else:
form.CloseNonBlockingForm()
What we have here is the same sequence of function calls as in the description. Get a form, add rows to it, show the form, and then refresh it every now and then. What we have here is the same sequence of function calls as in the description. Get a form, add rows to it, show the form, and then refresh it every now and then.
@ -1126,8 +1172,42 @@ To get a random color pair call `PySimpleGUI.GetRandomColorPair`. This returns
that color's compliment. that color's compliment.
sprint sprint
**sprint** **Debug Output**
Call `sprint` with as many parameters as you want and it'll print them all out in a `ScrolledTextBox`. This is simply a function pointing to `PySimpleGUI.ScrolledTextBox`. Be sure and check out the EasyPrint (Print) function described in the high-level API section. Leave your code the way it is, route your stdout and stderror to a scrolling window.
For a fun time, add this line to the top of your script
import PySimpleGUI as sg
print = sg.Print
**Look and Feel**
Dial in the look and feel that you like with the `SetOptions` function. You can change all of the defaults in one function call. One line of code to customize the entire GUI.
**ObjToString**
Ever wanted to easily display an objects contents easily? Use ObjToString to get a nicely formatted recursive walk of your objects.
This statement:
print(sg.ObjToSting(x))
And this was the output
<class '__main__.X'>
abc = abc
attr12 = 12
c = <class '__main__.C'>
b = <class '__main__.B'>
a = <class '__main__.A'>
attr1 = 1
attr2 = 2
attr3 = three
attr10 = 10
attrx = x
You'll quickly wonder how you ever coded without it.
--- ---
# Known Issues # Known Issues
@ -1154,18 +1234,18 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
| 2.1.1 | July 18, 2018 - Global settings exposed, fixes | 2.1.1 | July 18, 2018 - Global settings exposed, fixes
| 2.2.0| July 20, 2018 - Image Elements, Print output | 2.2.0| July 20, 2018 - Image Elements, Print output
| 2.3.0 | July 23, 2018 - Changed form.Read return codes, Slider Elements, Listbox element. Renamed some methods but left legacy calls in place for now. | 2.3.0 | July 23, 2018 - Changed form.Read return codes, Slider Elements, Listbox element. Renamed some methods but left legacy calls in place for now.
| 2.4.0 | July XX, 2018 - Planned release. Button images. | 2.4.0 | July 24, 2018 - Button images. Fixes so can run on Raspberry Pi
### Release Notes ### Release Notes
2.3 - Sliders, Listbox's and Image elements (oh my!) 2.3 - Sliders, Listbox's and Image elements (oh my!)
If using Progress Meters, avoid cancelling them when you have another window open. It could lead to future windows being blank. It's being worked on. If using Progress Meters, avoid cancelling them when you have another window open. It could lead to future windows being blank. It's being worked on.
New debug printing capability. `sg.Print`
### Upcoming ### Upcoming
Make suggestions people! Future release features Make suggestions people! Future release features
Button images. Ability to replace boring rectangular buttons with your own images.
Columns. How multiple columns would be specified in the SDK interface are still being designed. Columns. How multiple columns would be specified in the SDK interface are still being designed.
Progress Meters - Replace custom meter with tkinter meter. Progress Meters - Replace custom meter with tkinter meter.
@ -1216,11 +1296,3 @@ For Python questions, I simply start my query with 'Python'. Let's say you forg
In the hands of a competent programmer, this tool is **amazing**. It's a must-try kind of program that has completely changed my programming process. I'm not afraid of asking for help! You just have to be smart about using what you find. In the hands of a competent programmer, this tool is **amazing**. It's a must-try kind of program that has completely changed my programming process. I'm not afraid of asking for help! You just have to be smart about using what you find.
The PySimpleGUI window that the results are shown in is an 'input' field which means you can copy and paste the results right into your code. The PySimpleGUI window that the results are shown in is an 'input' field which means you can copy and paste the results right into your code.