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.

168
readme.md
View File

@ -1,16 +1,19 @@
![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
# PySimpleGUI
(Ver 2.3)
(Ver 2.4)
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
@ -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)
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??
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.
@ -336,24 +343,27 @@ It's both not enjoyable nor helpful to immediately jump into tweaking each and e
# Copy these design patterns!
## 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')],
[SG.InputText(), SG.FileBrowse()],
[SG.Submit(), SG.Cancel()]]
button, (source_filename, ) = form.LayoutAndShow(form_rows)
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')],
[sg.InputText(), sg.FileBrowse()],
[sg.Submit(), sg.Cancel()]]
button, (source_filename,) = form.LayoutAndRead(form_rows)
## 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')],
[SG.InputText(), SG.FileBrowse()],
[SG.Submit(), SG.Cancel()]]
button, (source_filename,) = form.LayoutAndShow(form_rows)
form = sg.FlexForm('SHA-1 & 256 Hash', auto_size_text=True)
form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')],
[sg.InputText(), sg.FileBrowse()],
[sg.Submit(), sg.Cancel()]]
button, (source_filename,) = form.LayoutAndRead(form_rows)
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.
@ -378,7 +388,7 @@ Now we're on the second row of the form. On this row there are 2 elements. The
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
@ -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.
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.
button, values = form.LayoutAndShow(form_rows)
button, values = form.LayoutAndRead(form_rows)
filename, folder1, folder2, should_overwrite = values
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.
button, value_list = form.LayoutAndShow(form_rows)
button, value_list = form.LayoutAndRead(form_rows)
value1 = value_list[0]
value2 = value_list[1]
...
@ -897,6 +907,26 @@ layout = [[SG.SimpleButton('My Button')]]
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**
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
@ -979,8 +1009,6 @@ Here's a complete solution for a chat-window using an Async form with an Output
break
## Tabbed Forms
Tabbed forms are shown using the `ShowTabbedForm` call. The call has the format
@ -1061,9 +1089,11 @@ 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:
Setup
form = FlexForm()
form.AddRows(form_rows)
form.Show(non_blocking = True)
form_rows = .....
form.LayoutAndRead(form_rows, non_blocking=True)
Periodic refresh
@ -1072,23 +1102,37 @@ If you need to close the form
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()`
**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.
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')],
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')]]
[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)
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))
#
# 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
@ -1096,6 +1140,8 @@ We're going to make a form and update one of the elements of that form every .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.
The new thing in this example is the call use of the Update method for the Text Element. The first thing we do inside the loop is "update" the text element that we made earlier. This changes the value of the text field on the form. The new value will be displayed when `form.ReadNonBlocking()` is called.
@ -1126,8 +1172,42 @@ To get a random color pair call `PySimpleGUI.GetRandomColorPair`. This returns
that color's compliment.
sprint
**sprint**
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`.
**Debug Output**
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
@ -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.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.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
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.
New debug printing capability. `sg.Print`
### Upcoming
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.
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.
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.