Moved all demo programs into subfolder

This commit is contained in:
MikeTheWatchGuy 2018-11-01 02:39:18 -04:00
parent a294d0a347
commit 1881bd7126
107 changed files with 51 additions and 144 deletions

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
sg.ChangeLookAndFeel('GreenTan')
# ------ Menu Definition ------ #
menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']],
['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ],
['&Help', '&About...'], ]
# ------ Column Definition ------ #
column1 = [[sg.Text('Column 1', background_color='lightblue', justification='center', size=(10, 1))],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]]
layout = [
[sg.Menu(menu_def, tearoff=True)],
[sg.Text('(Almost) All widgets in one Window!', size=(30, 1), justification='center', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)],
[sg.Text('Here is some text.... and a place to enter text')],
[sg.InputText('This is my text')],
[sg.Frame(layout=[
[sg.Checkbox('Checkbox', size=(10,1)), sg.Checkbox('My second checkbox!', default=True)],
[sg.Radio('My first Radio! ', "RADIO1", default=True, size=(10,1)), sg.Radio('My second Radio!', "RADIO1")]], title='Options',title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='Use these to set flags')],
[sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)),
sg.Multiline(default_text='A second multi-line', size=(35, 3))],
[sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)),
sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)],
[sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))],
[sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)),
sg.Frame('Labelled Group',[[
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25, tick_interval=25),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10),
sg.Column(column1, background_color='lightblue')]])],
[sg.Text('_' * 80)],
[sg.Text('Choose A Folder', size=(35, 1))],
[sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'),
sg.InputText('Default Folder'), sg.FolderBrowse()],
[sg.Submit(tooltip='Click to submit this form'), sg.Cancel()]]
window = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout)
event, values = window.Read()
sg.Popup('Title',
'The results of the window.',
'The button clicked was "{}"'.format(event),
'The values are', values)

View file

@ -0,0 +1,42 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Turn off padding in order to get a really tight looking layout.
"""
sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0, 0))
layout = [[sg.T('User:', pad=((3, 0), 0)), sg.OptionMenu(values=('User 1', 'User 2'), size=(20, 1)),
sg.T('0', size=(8, 1))],
[sg.T('Customer:', pad=((3, 0), 0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20, 1)),
sg.T('1', size=(8, 1))],
[sg.T('Notes:', pad=((3, 0), 0)), sg.In(size=(44, 1), background_color='white', text_color='black')],
[sg.ReadButton('Start', button_color=('white', 'black')),
sg.ReadButton('Stop', button_color=('gray50', 'black')),
sg.ReadButton('Reset', button_color=('white', '#9B0023')),
sg.ReadButton('Submit', button_color=('gray60', 'springgreen4')),
sg.Button('Exit', button_color=('white', '#00406B'))]]
window = sg.Window("Borderless Window",
default_element_size=(12, 1),
text_justification='r',
auto_size_text=False,
auto_size_buttons=False,
no_titlebar=True,
grab_anywhere=True,
default_button_element_size=(12, 1))
window.Layout(layout)
while True:
event, values = window.Read()
if event is None or event == 'Exit':
break

View file

@ -0,0 +1,33 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
if not sys.platform.startswith('win'):
sg.PopupError('Sorry, you gotta be on Windows')
sys.exit()
import winsound
sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0,0))
layout = [
[sg.ReadButton('Start', button_color=('white', 'black'), key='start'),
sg.ReadButton('Stop', button_color=('white', 'black'), key='stop'),
sg.ReadButton('Reset', button_color=('white', 'firebrick3'), key='reset'),
sg.ReadButton('Submit', button_color=('white', 'springgreen4'), key='submit')]
]
window = sg.Window("Button Click", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False, default_button_element_size=(12,1), use_default_focus=False).Layout(layout).Finalize()
window.FindElement('submit').Update(disabled=True)
recording = have_data = False
while True:
event, values = window.Read()
if event is None:
sys.exit(69)
winsound.PlaySound("ButtonClick.wav", 1)

View file

@ -0,0 +1,51 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Demonstrates using a "tight" layout with a Dark theme.
Shows how button states can be controlled by a user application. The program manages the disabled/enabled
states for buttons and changes the text color to show greyed-out (disabled) buttons
"""
sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0,0))
layout = [[sg.T('User:', pad=((3,0),0)), sg.OptionMenu(values = ('User 1', 'User 2'), size=(20,1)), sg.T('0', size=(8,1))],
[sg.T('Customer:', pad=((3,0),0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20,1)), sg.T('1', size=(8,1))],
[sg.T('Notes:', pad=((3,0),0)), sg.In(size=(44,1), background_color='white', text_color='black')],
[sg.ReadButton('Start', button_color=('white', 'black'), key='_Start_'),
sg.ReadButton('Stop', button_color=('white', 'black'), key='_Stop_'),
sg.ReadButton('Reset', button_color=('white', 'firebrick3'), key='_Reset_'),
sg.ReadButton('Submit', button_color=('white', 'springgreen4'), key='_Submit_')]]
window = 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)).Layout(layout).Finalize()
for key, state in {'_Start_': False, '_Stop_': True, '_Reset_': True, '_Submit_': True}.items():
window.FindElement(key).Update(disabled=state)
recording = have_data = False
while True:
event, values = window.Read()
print(event)
if event is None:
sys.exit(69)
if event == '_Start_':
for key, state in {'_Start_':True, '_Stop_':False, '_Reset_':False, '_Submit_':True}.items():
window.FindElement(key).Update(disabled=state)
recording = True
elif event == '_Stop_' and recording:
[window.FindElement(key).Update(disabled=value) for key,value in {'_Start_':False, '_Stop_':True, '_Reset_':False, '_Submit_':False}.items()]
recording = False
have_data = True
elif event == '_Reset_':
[window.FindElement(key).Update(disabled=value) for key,value in {'_Start_':False, '_Stop_':True, '_Reset_':True, '_Submit_':True}.items()]
recording = False
have_data = False
elif event is '_Submit_' and have_data:
[window.FindElement(key).Update(disabled=value) for key,value in {'_Start_':False, '_Stop_':True, '_Reset_':True, '_Submit_':False}.items()]
recording = False

View file

@ -0,0 +1,50 @@
#!/usr/bin/env python
import sys
import time
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
def show_win():
sg.SetOptions(border_width=0, margins=(0,0), element_padding=(5,3))
frame_layout = [ [sg.Button('', image_data=mac_red, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_exit_'),
sg.Button('', image_data=mac_orange, button_color=('white', sg.COLOR_SYSTEM_DEFAULT)),
sg.Button('', image_data=mac_green, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_minimize_'),
sg.Text(' '*40)],]
layout = [[sg.Frame('',frame_layout)],
[sg.T('')],
[ sg.Text(' My Mac-alike window', size=(25,2)) ],]
window = sg.Window('My new window',
no_titlebar=True,
grab_anywhere=True,
alpha_channel=0,
).Layout(layout).Finalize()
for i in range(100):
window.SetAlpha(i/100)
time.sleep(.01)
while True: # Event Loop
event, values = window.Read()
if event is None or event == '_exit_':
break
if event == '_minimize_':
# window.Minimize() # cannot minimize a window with no titlebar
pass
print(event, values)
mac_red = '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'
mac_green = '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'
mac_orange = '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'
show_win()

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,15 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[sg.T('Calendar Test')],
[sg.In('', size=(20,1), key='input')],
[sg.CalendarButton('Choose Date', target='input', key='date')],
[sg.Ok(key=1)]]
window = sg.Window('Calendar', grab_anywhere=False).Layout(layout)
event,values = window.Read()
sg.Popup(values['input'])

View file

@ -0,0 +1,24 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [
[sg.Canvas(size=(150, 150), background_color='red', key='canvas')],
[sg.T('Change circle color to:'), sg.ReadButton('Red'), sg.ReadButton('Blue')]
]
window = sg.Window('Canvas test').Layout(layout).Finalize()
cir = window.FindElement('canvas').TKCanvas.create_oval(50, 50, 100, 100)
while True:
event, values = window.Read()
if event is None:
break
if event is 'Blue':
window.FindElement('canvas').TKCanvas.itemconfig(cir, fill = "Blue")
elif event is 'Red':
window.FindElement('canvas').TKCanvas.itemconfig(cir, fill = "Red")

View file

@ -0,0 +1,48 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Demonstrates the new change_submits parameter for inputtext elements
It ONLY submits when a button changes the field, not normal user input
Be careful on persistent forms to not clear the input
"""
layout = [[ sg.Text('Test of reading input field') ],
[sg.T('This input is normal'), sg.In()],
[sg.T('This input change submits'), sg.In(change_submits=True)],
[sg.T('This multiline input change submits'), sg.Multiline(change_submits=True, do_not_clear=True)],
[sg.T('This input is normal'), sg.In(), sg.FileBrowse()],
[sg.T('File Browse submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in1_'), sg.FileBrowse()],
[sg.T('Color Chooser submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in2_'), sg.ColorChooserButton('Color...', target=(sg.ThisRow, -1))],
[sg.T('Folder Browse submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in3_'), sg.FolderBrowse()],
[sg.T('Calendar Chooser submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in4_'), sg.CalendarButton('Date...', target=(sg.ThisRow, -1))],
[sg.T('Disabled input submits'), sg.In(change_submits=True,
do_not_clear=True,
disabled=True,
key='_in5'), sg.FileBrowse()],
[sg.T('This input clears after submit'),sg.In(change_submits=True,
key='_in6_'), sg.FileBrowse()],
[ sg.Button('Read')]]
window = sg.Window('Demonstration of InputText with change_submits',
auto_size_text=False,
default_element_size=(22,1),
text_justification='right',
).Layout(layout)
while True: # Event Loop
event, values = window.Read()
print(event, values)
if event is None:
break

32
DemoPrograms/Demo_Chat.py Normal file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
'''
A chat window. Add call to your send-routine, print the response and you're done
'''
sg.ChangeLookAndFeel('GreenTan') # give our window a spiffy set of colors
layout = [[sg.Text('Your output will go here', size=(40, 1))],
[sg.Output(size=(127, 30), font=('Helvetica 10'))],
[sg.Multiline(size=(85, 5), enter_submits=True, key='query'),
sg.ReadButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0]), bind_return_key=True),
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
window = sg.Window('Chat window', default_element_size=(30, 2), font=('Helvetica',' 13'), default_button_element_size=(8,2)).Layout(layout)
# ---===--- Loop taking in user input and using it --- #
while True:
(event, value) = window.Read()
if event is 'SEND':
query = value['query'].rstrip()
# EXECUTE YOUR COMMAND HERE
print('The command you entered was {}'.format(query))
elif event is None or event == 'EXIT': # quit if exit button or X
break
sys.exit(69)

View file

@ -0,0 +1,60 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
'''
A chatbot with history
Scroll up and down through prior commands using the arrow keys
Special keyboard keys:
Up arrow - scroll up in commands
Down arrow - scroll down in commands
Escape - clear current command
Control C - exit form
'''
def ChatBotWithHistory():
# ------- Make a new Window ------- #
sg.ChangeLookAndFeel('GreenTan') # give our form a spiffy set of colors
layout = [[sg.Text('Your output will go here', size=(40, 1))],
[sg.Output(size=(127, 30), font=('Helvetica 10'))],
[sg.T('Command History'), sg.T('', size=(20,3), key='history')],
[sg.Multiline(size=(85, 5), enter_submits=True, key='query', do_not_clear=False),
sg.ReadButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0]), bind_return_key=True),
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
window = sg.Window('Chat window with history', default_element_size=(30, 2), font=('Helvetica',' 13'), default_button_element_size=(8,2), return_keyboard_events=True).Layout(layout)
# ---===--- Loop taking in user input and using it --- #
command_history = []
history_offset = 0
while True:
(event, value) = window.Read()
if event is 'SEND':
query = value['query'].rstrip()
# EXECUTE YOUR COMMAND HERE
print('The command you entered was {}'.format(query))
command_history.append(query)
history_offset = len(command_history)-1
window.FindElement('query').Update('') # manually clear input because keyboard events blocks clear
window.FindElement('history').Update('\n'.join(command_history[-3:]))
elif event is None or event is 'EXIT': # quit if exit event or X
break
elif 'Up' in event and len(command_history):
command = command_history[history_offset]
history_offset -= 1 * (history_offset > 0) # decrement is not zero
window.FindElement('query').Update(command)
elif 'Down' in event and len(command_history):
history_offset += 1 * (history_offset < len(command_history)-1) # increment up to end of list
command = command_history[history_offset]
window.FindElement('query').Update(command)
elif 'Escape' in event:
window.FindElement('query').Update('')
sys.exit(69)
ChatBotWithHistory()

View file

@ -0,0 +1,76 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from chatterbot import ChatBot
import chatterbot.utils
'''
Demo_Chatterbot.py
A GUI wrapped arouind the Chatterbot package.
The GUI is used to show progress bars during the training process and
to collect user input that is sent to the chatbot. The reply is displayed in the GUI window
'''
# Create the 'Trainer GUI'
# The Trainer GUI consists of a lot of progress bars stacked on top of each other
sg.ChangeLookAndFeel('GreenTan')
# sg.DebugWin()
MAX_PROG_BARS = 20 # number of training sessions
bars = []
texts = []
training_layout = [[sg.T('TRAINING PROGRESS', size=(20, 1), font=('Helvetica', 17))], ]
for i in range(MAX_PROG_BARS):
bars.append(sg.ProgressBar(100, size=(30, 4)))
texts.append(sg.T(' ' * 20, size=(20, 1), justification='right'))
training_layout += [[texts[i], bars[i]],] # add a single row
training_window = sg.Window('Training').Layout(training_layout)
current_bar = 0
# callback function for training runs
def print_progress_bar(description, iteration_counter, total_items, progress_bar_length=20):
global current_bar
global bars
global texts
global training_window
# update the window and the bars
button, values = training_window.Read(timeout=0)
if button is None: # if user closed the window on us, exit
sys.exit(69)
if bars[current_bar].UpdateBar(iteration_counter, max=total_items) is False:
sys.exit(69)
texts[current_bar].Update(description) # show the training dataset name
if iteration_counter == total_items:
current_bar += 1
# redefine the chatbot text based progress bar with a graphical one
chatterbot.utils.print_progress_bar = print_progress_bar
chatbot = ChatBot('Ron Obvious', trainer='chatterbot.trainers.ChatterBotCorpusTrainer')
# Train based on the english corpus
chatbot.train("chatterbot.corpus.english")
################# GUI #################
layout = [[sg.Output(size=(80, 20))],
[sg.Multiline(size=(70, 5), enter_submits=True),
sg.Button('SEND', bind_return_key=True), sg.Button('EXIT')]]
window = sg.Window('Chat Window', auto_size_text=True, default_element_size=(30, 2)).Layout(layout)
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
while True:
event, (value,) = window.Read()
if event is not 'SEND':
break
string = value.rstrip()
print(' '+string)
# send the user input to chatbot to get a response
response = chatbot.get_response(value.rstrip())
print(response)

1728
DemoPrograms/Demo_Color.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,703 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Shows a big chart of colors... give it a few seconds to create it
Once large window is shown, you can click on any color and another window will popup
showing both white and black text on that color
Uses TOOLTIPS to show the hex values for the colors. Hover over a color and a tooltip will show you the RGB
You will find the list of tkinter colors here:
http://www.tcl.tk/man/tcl8.5/TkCmd/colors.htm
"""
color_map = {
'alice blue': '#F0F8FF',
'AliceBlue': '#F0F8FF',
'antique white': '#FAEBD7',
'AntiqueWhite': '#FAEBD7',
'AntiqueWhite1': '#FFEFDB',
'AntiqueWhite2': '#EEDFCC',
'AntiqueWhite3': '#CDC0B0',
'AntiqueWhite4': '#8B8378',
'aquamarine': '#7FFFD4',
'aquamarine1': '#7FFFD4',
'aquamarine2': '#76EEC6',
'aquamarine3': '#66CDAA',
'aquamarine4': '#458B74',
'azure': '#F0FFFF',
'azure1': '#F0FFFF',
'azure2': '#E0EEEE',
'azure3': '#C1CDCD',
'azure4': '#838B8B',
'beige': '#F5F5DC',
'bisque': '#FFE4C4',
'bisque1': '#FFE4C4',
'bisque2': '#EED5B7',
'bisque3': '#CDB79E',
'bisque4': '#8B7D6B',
'black': '#000000',
'blanched almond': '#FFEBCD',
'BlanchedAlmond': '#FFEBCD',
'blue': '#0000FF',
'blue violet': '#8A2BE2',
'blue1': '#0000FF',
'blue2': '#0000EE',
'blue3': '#0000CD',
'blue4': '#00008B',
'BlueViolet': '#8A2BE2',
'brown': '#A52A2A',
'brown1': '#FF4040',
'brown2': '#EE3B3B',
'brown3': '#CD3333',
'brown4': '#8B2323',
'burlywood': '#DEB887',
'burlywood1': '#FFD39B',
'burlywood2': '#EEC591',
'burlywood3': '#CDAA7D',
'burlywood4': '#8B7355',
'cadet blue': '#5F9EA0',
'CadetBlue': '#5F9EA0',
'CadetBlue1': '#98F5FF',
'CadetBlue2': '#8EE5EE',
'CadetBlue3': '#7AC5CD',
'CadetBlue4': '#53868B',
'chartreuse': '#7FFF00',
'chartreuse1': '#7FFF00',
'chartreuse2': '#76EE00',
'chartreuse3': '#66CD00',
'chartreuse4': '#458B00',
'chocolate': '#D2691E',
'chocolate1': '#FF7F24',
'chocolate2': '#EE7621',
'chocolate3': '#CD661D',
'chocolate4': '#8B4513',
'coral': '#FF7F50',
'coral1': '#FF7256',
'coral2': '#EE6A50',
'coral3': '#CD5B45',
'coral4': '#8B3E2F',
'cornflower blue': '#6495ED',
'CornflowerBlue': '#6495ED',
'cornsilk': '#FFF8DC',
'cornsilk1': '#FFF8DC',
'cornsilk2': '#EEE8CD',
'cornsilk3': '#CDC8B1',
'cornsilk4': '#8B8878',
'cyan': '#00FFFF',
'cyan1': '#00FFFF',
'cyan2': '#00EEEE',
'cyan3': '#00CDCD',
'cyan4': '#008B8B',
'dark blue': '#00008B',
'dark cyan': '#008B8B',
'dark goldenrod': '#B8860B',
'dark gray': '#A9A9A9',
'dark green': '#006400',
'dark grey': '#A9A9A9',
'dark khaki': '#BDB76B',
'dark magenta': '#8B008B',
'dark olive green': '#556B2F',
'dark orange': '#FF8C00',
'dark orchid': '#9932CC',
'dark red': '#8B0000',
'dark salmon': '#E9967A',
'dark sea green': '#8FBC8F',
'dark slate blue': '#483D8B',
'dark slate gray': '#2F4F4F',
'dark slate grey': '#2F4F4F',
'dark turquoise': '#00CED1',
'dark violet': '#9400D3',
'DarkBlue': '#00008B',
'DarkCyan': '#008B8B',
'DarkGoldenrod': '#B8860B',
'DarkGoldenrod1': '#FFB90F',
'DarkGoldenrod2': '#EEAD0E',
'DarkGoldenrod3': '#CD950C',
'DarkGoldenrod4': '#8B6508',
'DarkGray': '#A9A9A9',
'DarkGreen': '#006400',
'DarkGrey': '#A9A9A9',
'DarkKhaki': '#BDB76B',
'DarkMagenta': '#8B008B',
'DarkOliveGreen': '#556B2F',
'DarkOliveGreen1': '#CAFF70',
'DarkOliveGreen2': '#BCEE68',
'DarkOliveGreen3': '#A2CD5A',
'DarkOliveGreen4': '#6E8B3D',
'DarkOrange': '#FF8C00',
'DarkOrange1': '#FF7F00',
'DarkOrange2': '#EE7600',
'DarkOrange3': '#CD6600',
'DarkOrange4': '#8B4500',
'DarkOrchid': '#9932CC',
'DarkOrchid1': '#BF3EFF',
'DarkOrchid2': '#B23AEE',
'DarkOrchid3': '#9A32CD',
'DarkOrchid4': '#68228B',
'DarkRed': '#8B0000',
'DarkSalmon': '#E9967A',
'DarkSeaGreen': '#8FBC8F',
'DarkSeaGreen1': '#C1FFC1',
'DarkSeaGreen2': '#B4EEB4',
'DarkSeaGreen3': '#9BCD9B',
'DarkSeaGreen4': '#698B69',
'DarkSlateBlue': '#483D8B',
'DarkSlateGray': '#2F4F4F',
'DarkSlateGray1': '#97FFFF',
'DarkSlateGray2': '#8DEEEE',
'DarkSlateGray3': '#79CDCD',
'DarkSlateGray4': '#528B8B',
'DarkSlateGrey': '#2F4F4F',
'DarkTurquoise': '#00CED1',
'DarkViolet': '#9400D3',
'deep pink': '#FF1493',
'deep sky blue': '#00BFFF',
'DeepPink': '#FF1493',
'DeepPink1': '#FF1493',
'DeepPink2': '#EE1289',
'DeepPink3': '#CD1076',
'DeepPink4': '#8B0A50',
'DeepSkyBlue': '#00BFFF',
'DeepSkyBlue1': '#00BFFF',
'DeepSkyBlue2': '#00B2EE',
'DeepSkyBlue3': '#009ACD',
'DeepSkyBlue4': '#00688B',
'dim gray': '#696969',
'dim grey': '#696969',
'DimGray': '#696969',
'DimGrey': '#696969',
'dodger blue': '#1E90FF',
'DodgerBlue': '#1E90FF',
'DodgerBlue1': '#1E90FF',
'DodgerBlue2': '#1C86EE',
'DodgerBlue3': '#1874CD',
'DodgerBlue4': '#104E8B',
'firebrick': '#B22222',
'firebrick1': '#FF3030',
'firebrick2': '#EE2C2C',
'firebrick3': '#CD2626',
'firebrick4': '#8B1A1A',
'floral white': '#FFFAF0',
'FloralWhite': '#FFFAF0',
'forest green': '#228B22',
'ForestGreen': '#228B22',
'gainsboro': '#DCDCDC',
'ghost white': '#F8F8FF',
'GhostWhite': '#F8F8FF',
'gold': '#FFD700',
'gold1': '#FFD700',
'gold2': '#EEC900',
'gold3': '#CDAD00',
'gold4': '#8B7500',
'goldenrod': '#DAA520',
'goldenrod1': '#FFC125',
'goldenrod2': '#EEB422',
'goldenrod3': '#CD9B1D',
'goldenrod4': '#8B6914',
'green': '#00FF00',
'green yellow': '#ADFF2F',
'green1': '#00FF00',
'green2': '#00EE00',
'green3': '#00CD00',
'green4': '#008B00',
'GreenYellow': '#ADFF2F',
'grey': '#BEBEBE',
'grey0': '#000000',
'grey1': '#030303',
'grey2': '#050505',
'grey3': '#080808',
'grey4': '#0A0A0A',
'grey5': '#0D0D0D',
'grey6': '#0F0F0F',
'grey7': '#121212',
'grey8': '#141414',
'grey9': '#171717',
'grey10': '#1A1A1A',
'grey11': '#1C1C1C',
'grey12': '#1F1F1F',
'grey13': '#212121',
'grey14': '#242424',
'grey15': '#262626',
'grey16': '#292929',
'grey17': '#2B2B2B',
'grey18': '#2E2E2E',
'grey19': '#303030',
'grey20': '#333333',
'grey21': '#363636',
'grey22': '#383838',
'grey23': '#3B3B3B',
'grey24': '#3D3D3D',
'grey25': '#404040',
'grey26': '#424242',
'grey27': '#454545',
'grey28': '#474747',
'grey29': '#4A4A4A',
'grey30': '#4D4D4D',
'grey31': '#4F4F4F',
'grey32': '#525252',
'grey33': '#545454',
'grey34': '#575757',
'grey35': '#595959',
'grey36': '#5C5C5C',
'grey37': '#5E5E5E',
'grey38': '#616161',
'grey39': '#636363',
'grey40': '#666666',
'grey41': '#696969',
'grey42': '#6B6B6B',
'grey43': '#6E6E6E',
'grey44': '#707070',
'grey45': '#737373',
'grey46': '#757575',
'grey47': '#787878',
'grey48': '#7A7A7A',
'grey49': '#7D7D7D',
'grey50': '#7F7F7F',
'grey51': '#828282',
'grey52': '#858585',
'grey53': '#878787',
'grey54': '#8A8A8A',
'grey55': '#8C8C8C',
'grey56': '#8F8F8F',
'grey57': '#919191',
'grey58': '#949494',
'grey59': '#969696',
'grey60': '#999999',
'grey61': '#9C9C9C',
'grey62': '#9E9E9E',
'grey63': '#A1A1A1',
'grey64': '#A3A3A3',
'grey65': '#A6A6A6',
'grey66': '#A8A8A8',
'grey67': '#ABABAB',
'grey68': '#ADADAD',
'grey69': '#B0B0B0',
'grey70': '#B3B3B3',
'grey71': '#B5B5B5',
'grey72': '#B8B8B8',
'grey73': '#BABABA',
'grey74': '#BDBDBD',
'grey75': '#BFBFBF',
'grey76': '#C2C2C2',
'grey77': '#C4C4C4',
'grey78': '#C7C7C7',
'grey79': '#C9C9C9',
'grey80': '#CCCCCC',
'grey81': '#CFCFCF',
'grey82': '#D1D1D1',
'grey83': '#D4D4D4',
'grey84': '#D6D6D6',
'grey85': '#D9D9D9',
'grey86': '#DBDBDB',
'grey87': '#DEDEDE',
'grey88': '#E0E0E0',
'grey89': '#E3E3E3',
'grey90': '#E5E5E5',
'grey91': '#E8E8E8',
'grey92': '#EBEBEB',
'grey93': '#EDEDED',
'grey94': '#F0F0F0',
'grey95': '#F2F2F2',
'grey96': '#F5F5F5',
'grey97': '#F7F7F7',
'grey98': '#FAFAFA',
'grey99': '#FCFCFC',
'grey100': '#FFFFFF',
'honeydew': '#F0FFF0',
'honeydew1': '#F0FFF0',
'honeydew2': '#E0EEE0',
'honeydew3': '#C1CDC1',
'honeydew4': '#838B83',
'hot pink': '#FF69B4',
'HotPink': '#FF69B4',
'HotPink1': '#FF6EB4',
'HotPink2': '#EE6AA7',
'HotPink3': '#CD6090',
'HotPink4': '#8B3A62',
'indian red': '#CD5C5C',
'IndianRed': '#CD5C5C',
'IndianRed1': '#FF6A6A',
'IndianRed2': '#EE6363',
'IndianRed3': '#CD5555',
'IndianRed4': '#8B3A3A',
'ivory': '#FFFFF0',
'ivory1': '#FFFFF0',
'ivory2': '#EEEEE0',
'ivory3': '#CDCDC1',
'ivory4': '#8B8B83',
'khaki': '#F0E68C',
'khaki1': '#FFF68F',
'khaki2': '#EEE685',
'khaki3': '#CDC673',
'khaki4': '#8B864E',
'lavender': '#E6E6FA',
'lavender blush': '#FFF0F5',
'LavenderBlush': '#FFF0F5',
'LavenderBlush1': '#FFF0F5',
'LavenderBlush2': '#EEE0E5',
'LavenderBlush3': '#CDC1C5',
'LavenderBlush4': '#8B8386',
'lawn green': '#7CFC00',
'LawnGreen': '#7CFC00',
'lemon chiffon': '#FFFACD',
'LemonChiffon': '#FFFACD',
'LemonChiffon1': '#FFFACD',
'LemonChiffon2': '#EEE9BF',
'LemonChiffon3': '#CDC9A5',
'LemonChiffon4': '#8B8970',
'light blue': '#ADD8E6',
'light coral': '#F08080',
'light cyan': '#E0FFFF',
'light goldenrod': '#EEDD82',
'light goldenrod yellow': '#FAFAD2',
'light gray': '#D3D3D3',
'light green': '#90EE90',
'light grey': '#D3D3D3',
'light pink': '#FFB6C1',
'light salmon': '#FFA07A',
'light sea green': '#20B2AA',
'light sky blue': '#87CEFA',
'light slate blue': '#8470FF',
'light slate gray': '#778899',
'light slate grey': '#778899',
'light steel blue': '#B0C4DE',
'light yellow': '#FFFFE0',
'LightBlue': '#ADD8E6',
'LightBlue1': '#BFEFFF',
'LightBlue2': '#B2DFEE',
'LightBlue3': '#9AC0CD',
'LightBlue4': '#68838B',
'LightCoral': '#F08080',
'LightCyan': '#E0FFFF',
'LightCyan1': '#E0FFFF',
'LightCyan2': '#D1EEEE',
'LightCyan3': '#B4CDCD',
'LightCyan4': '#7A8B8B',
'LightGoldenrod': '#EEDD82',
'LightGoldenrod1': '#FFEC8B',
'LightGoldenrod2': '#EEDC82',
'LightGoldenrod3': '#CDBE70',
'LightGoldenrod4': '#8B814C',
'LightGoldenrodYellow': '#FAFAD2',
'LightGray': '#D3D3D3',
'LightGreen': '#90EE90',
'LightGrey': '#D3D3D3',
'LightPink': '#FFB6C1',
'LightPink1': '#FFAEB9',
'LightPink2': '#EEA2AD',
'LightPink3': '#CD8C95',
'LightPink4': '#8B5F65',
'LightSalmon': '#FFA07A',
'LightSalmon1': '#FFA07A',
'LightSalmon2': '#EE9572',
'LightSalmon3': '#CD8162',
'LightSalmon4': '#8B5742',
'LightSeaGreen': '#20B2AA',
'LightSkyBlue': '#87CEFA',
'LightSkyBlue1': '#B0E2FF',
'LightSkyBlue2': '#A4D3EE',
'LightSkyBlue3': '#8DB6CD',
'LightSkyBlue4': '#607B8B',
'LightSlateBlue': '#8470FF',
'LightSlateGray': '#778899',
'LightSlateGrey': '#778899',
'LightSteelBlue': '#B0C4DE',
'LightSteelBlue1': '#CAE1FF',
'LightSteelBlue2': '#BCD2EE',
'LightSteelBlue3': '#A2B5CD',
'LightSteelBlue4': '#6E7B8B',
'LightYellow': '#FFFFE0',
'LightYellow1': '#FFFFE0',
'LightYellow2': '#EEEED1',
'LightYellow3': '#CDCDB4',
'LightYellow4': '#8B8B7A',
'lime green': '#32CD32',
'LimeGreen': '#32CD32',
'linen': '#FAF0E6',
'magenta': '#FF00FF',
'magenta1': '#FF00FF',
'magenta2': '#EE00EE',
'magenta3': '#CD00CD',
'magenta4': '#8B008B',
'maroon': '#B03060',
'maroon1': '#FF34B3',
'maroon2': '#EE30A7',
'maroon3': '#CD2990',
'maroon4': '#8B1C62',
'medium aquamarine': '#66CDAA',
'medium blue': '#0000CD',
'medium orchid': '#BA55D3',
'medium purple': '#9370DB',
'medium sea green': '#3CB371',
'medium slate blue': '#7B68EE',
'medium spring green': '#00FA9A',
'medium turquoise': '#48D1CC',
'medium violet red': '#C71585',
'MediumAquamarine': '#66CDAA',
'MediumBlue': '#0000CD',
'MediumOrchid': '#BA55D3',
'MediumOrchid1': '#E066FF',
'MediumOrchid2': '#D15FEE',
'MediumOrchid3': '#B452CD',
'MediumOrchid4': '#7A378B',
'MediumPurple': '#9370DB',
'MediumPurple1': '#AB82FF',
'MediumPurple2': '#9F79EE',
'MediumPurple3': '#8968CD',
'MediumPurple4': '#5D478B',
'MediumSeaGreen': '#3CB371',
'MediumSlateBlue': '#7B68EE',
'MediumSpringGreen': '#00FA9A',
'MediumTurquoise': '#48D1CC',
'MediumVioletRed': '#C71585',
'midnight blue': '#191970',
'MidnightBlue': '#191970',
'mint cream': '#F5FFFA',
'MintCream': '#F5FFFA',
'misty rose': '#FFE4E1',
'MistyRose': '#FFE4E1',
'MistyRose1': '#FFE4E1',
'MistyRose2': '#EED5D2',
'MistyRose3': '#CDB7B5',
'MistyRose4': '#8B7D7B',
'moccasin': '#FFE4B5',
'navajo white': '#FFDEAD',
'NavajoWhite': '#FFDEAD',
'NavajoWhite1': '#FFDEAD',
'NavajoWhite2': '#EECFA1',
'NavajoWhite3': '#CDB38B',
'NavajoWhite4': '#8B795E',
'navy': '#000080',
'navy blue': '#000080',
'NavyBlue': '#000080',
'old lace': '#FDF5E6',
'OldLace': '#FDF5E6',
'olive drab': '#6B8E23',
'OliveDrab': '#6B8E23',
'OliveDrab1': '#C0FF3E',
'OliveDrab2': '#B3EE3A',
'OliveDrab3': '#9ACD32',
'OliveDrab4': '#698B22',
'orange': '#FFA500',
'orange red': '#FF4500',
'orange1': '#FFA500',
'orange2': '#EE9A00',
'orange3': '#CD8500',
'orange4': '#8B5A00',
'OrangeRed': '#FF4500',
'OrangeRed1': '#FF4500',
'OrangeRed2': '#EE4000',
'OrangeRed3': '#CD3700',
'OrangeRed4': '#8B2500',
'orchid': '#DA70D6',
'orchid1': '#FF83FA',
'orchid2': '#EE7AE9',
'orchid3': '#CD69C9',
'orchid4': '#8B4789',
'pale goldenrod': '#EEE8AA',
'pale green': '#98FB98',
'pale turquoise': '#AFEEEE',
'pale violet red': '#DB7093',
'PaleGoldenrod': '#EEE8AA',
'PaleGreen': '#98FB98',
'PaleGreen1': '#9AFF9A',
'PaleGreen2': '#90EE90',
'PaleGreen3': '#7CCD7C',
'PaleGreen4': '#548B54',
'PaleTurquoise': '#AFEEEE',
'PaleTurquoise1': '#BBFFFF',
'PaleTurquoise2': '#AEEEEE',
'PaleTurquoise3': '#96CDCD',
'PaleTurquoise4': '#668B8B',
'PaleVioletRed': '#DB7093',
'PaleVioletRed1': '#FF82AB',
'PaleVioletRed2': '#EE799F',
'PaleVioletRed3': '#CD687F',
'PaleVioletRed4': '#8B475D',
'papaya whip': '#FFEFD5',
'PapayaWhip': '#FFEFD5',
'peach puff': '#FFDAB9',
'PeachPuff': '#FFDAB9',
'PeachPuff1': '#FFDAB9',
'PeachPuff2': '#EECBAD',
'PeachPuff3': '#CDAF95',
'PeachPuff4': '#8B7765',
'peru': '#CD853F',
'pink': '#FFC0CB',
'pink1': '#FFB5C5',
'pink2': '#EEA9B8',
'pink3': '#CD919E',
'pink4': '#8B636C',
'plum': '#DDA0DD',
'plum1': '#FFBBFF',
'plum2': '#EEAEEE',
'plum3': '#CD96CD',
'plum4': '#8B668B',
'powder blue': '#B0E0E6',
'PowderBlue': '#B0E0E6',
'purple': '#A020F0',
'purple1': '#9B30FF',
'purple2': '#912CEE',
'purple3': '#7D26CD',
'purple4': '#551A8B',
'red': '#FF0000',
'red1': '#FF0000',
'red2': '#EE0000',
'red3': '#CD0000',
'red4': '#8B0000',
'rosy brown': '#BC8F8F',
'RosyBrown': '#BC8F8F',
'RosyBrown1': '#FFC1C1',
'RosyBrown2': '#EEB4B4',
'RosyBrown3': '#CD9B9B',
'RosyBrown4': '#8B6969',
'royal blue': '#4169E1',
'RoyalBlue': '#4169E1',
'RoyalBlue1': '#4876FF',
'RoyalBlue2': '#436EEE',
'RoyalBlue3': '#3A5FCD',
'RoyalBlue4': '#27408B',
'saddle brown': '#8B4513',
'SaddleBrown': '#8B4513',
'salmon': '#FA8072',
'salmon1': '#FF8C69',
'salmon2': '#EE8262',
'salmon3': '#CD7054',
'salmon4': '#8B4C39',
'sandy brown': '#F4A460',
'SandyBrown': '#F4A460',
'sea green': '#2E8B57',
'SeaGreen': '#2E8B57',
'SeaGreen1': '#54FF9F',
'SeaGreen2': '#4EEE94',
'SeaGreen3': '#43CD80',
'SeaGreen4': '#2E8B57',
'seashell': '#FFF5EE',
'seashell1': '#FFF5EE',
'seashell2': '#EEE5DE',
'seashell3': '#CDC5BF',
'seashell4': '#8B8682',
'sienna': '#A0522D',
'sienna1': '#FF8247',
'sienna2': '#EE7942',
'sienna3': '#CD6839',
'sienna4': '#8B4726',
'sky blue': '#87CEEB',
'SkyBlue': '#87CEEB',
'SkyBlue1': '#87CEFF',
'SkyBlue2': '#7EC0EE',
'SkyBlue3': '#6CA6CD',
'SkyBlue4': '#4A708B',
'slate blue': '#6A5ACD',
'slate gray': '#708090',
'slate grey': '#708090',
'SlateBlue': '#6A5ACD',
'SlateBlue1': '#836FFF',
'SlateBlue2': '#7A67EE',
'SlateBlue3': '#6959CD',
'SlateBlue4': '#473C8B',
'SlateGray': '#708090',
'SlateGray1': '#C6E2FF',
'SlateGray2': '#B9D3EE',
'SlateGray3': '#9FB6CD',
'SlateGray4': '#6C7B8B',
'SlateGrey': '#708090',
'snow': '#FFFAFA',
'snow1': '#FFFAFA',
'snow2': '#EEE9E9',
'snow3': '#CDC9C9',
'snow4': '#8B8989',
'spring green': '#00FF7F',
'SpringGreen': '#00FF7F',
'SpringGreen1': '#00FF7F',
'SpringGreen2': '#00EE76',
'SpringGreen3': '#00CD66',
'SpringGreen4': '#008B45',
'steel blue': '#4682B4',
'SteelBlue': '#4682B4',
'SteelBlue1': '#63B8FF',
'SteelBlue2': '#5CACEE',
'SteelBlue3': '#4F94CD',
'SteelBlue4': '#36648B',
'tan': '#D2B48C',
'tan1': '#FFA54F',
'tan2': '#EE9A49',
'tan3': '#CD853F',
'tan4': '#8B5A2B',
'thistle': '#D8BFD8',
'thistle1': '#FFE1FF',
'thistle2': '#EED2EE',
'thistle3': '#CDB5CD',
'thistle4': '#8B7B8B',
'tomato': '#FF6347',
'tomato1': '#FF6347',
'tomato2': '#EE5C42',
'tomato3': '#CD4F39',
'tomato4': '#8B3626',
'turquoise': '#40E0D0',
'turquoise1': '#00F5FF',
'turquoise2': '#00E5EE',
'turquoise3': '#00C5CD',
'turquoise4': '#00868B',
'violet': '#EE82EE',
'violet red': '#D02090',
'VioletRed': '#D02090',
'VioletRed1': '#FF3E96',
'VioletRed2': '#EE3A8C',
'VioletRed3': '#CD3278',
'VioletRed4': '#8B2252',
'wheat': '#F5DEB3',
'wheat1': '#FFE7BA',
'wheat2': '#EED8AE',
'wheat3': '#CDBA96',
'wheat4': '#8B7E66',
'white': '#FFFFFF',
'white smoke': '#F5F5F5',
'WhiteSmoke': '#F5F5F5',
'yellow': '#FFFF00',
'yellow green': '#9ACD32',
'yellow1': '#FFFF00',
'yellow2': '#EEEE00',
'yellow3': '#CDCD00',
'yellow4': '#8B8B00',
'YellowGreen': '#9ACD32',
}
sg.SetOptions(button_element_size=(12,1), element_padding=(0,0), auto_size_buttons=False, border_width=1, tooltip_time=100)
#start layout with the tittle
layout = [[sg.Text('Hover mouse to see RGB value, click for white & black text',
text_color='blue',
font='Any 15',
relief=sg.RELIEF_SUNKEN,
justification='center',
size=(100,1),
background_color='light green',
pad=(0,(0,20))),]]
# -- Create primary color viewer window by building rows and appending to layout --
row = []
for i, color in enumerate(color_map):
row.append(sg.Button(color, button_color=('black', color), key=color, tooltip=color_map[color]))
if (i+1) % 15 == 0: # every 15 buttons make a new row
layout.append(row)
row = []
window = sg.Window('Color Viewer', grab_anywhere=False, font=('any 9')).Layout(layout)
# -- Event loop --
while True:
event, values = window.Read()
if event is None:
break
# -- Create a secondary window that shows white and black text on chosen color
layout2 =[[sg.DummyButton(event, button_color=('white', event), tooltip=color_map[event]), sg.DummyButton(event, button_color=('black', event), tooltip=color_map[event])] ]
sg.Window('Buttons with white and black text', keep_on_top=True).Layout(layout2).Read(timeout=0)

View file

@ -0,0 +1,113 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Color names courtesy of Big Daddy's Wiki-Python
http://www.wikipython.com/tkinter-ttk-tix/summary-information/colors/
Shows a big chart of colors... give it a few seconds to create it
Once large window is shown, you can click on any color and another window will popup
showing both white and black text on that color
"""
COLORS = ['snow', 'ghost white', 'white smoke', 'gainsboro', 'floral white', 'old lace',
'linen', 'antique white', 'papaya whip', 'blanched almond', 'bisque', 'peach puff',
'navajo white', 'lemon chiffon', 'mint cream', 'azure', 'alice blue', 'lavender',
'lavender blush', 'misty rose', 'dark slate gray', 'dim gray', 'slate gray',
'light slate gray', 'gray', 'light gray', 'midnight blue', 'navy', 'cornflower blue', 'dark slate blue',
'slate blue', 'medium slate blue', 'light slate blue', 'medium blue', 'royal blue', 'blue',
'dodger blue', 'deep sky blue', 'sky blue', 'light sky blue', 'steel blue', 'light steel blue',
'light blue', 'powder blue', 'pale turquoise', 'dark turquoise', 'medium turquoise', 'turquoise',
'cyan', 'light cyan', 'cadet blue', 'medium aquamarine', 'aquamarine', 'dark green', 'dark olive green',
'dark sea green', 'sea green', 'medium sea green', 'light sea green', 'pale green', 'spring green',
'lawn green', 'medium spring green', 'green yellow', 'lime green', 'yellow green',
'forest green', 'olive drab', 'dark khaki', 'khaki', 'pale goldenrod', 'light goldenrod yellow',
'light yellow', 'yellow', 'gold', 'light goldenrod', 'goldenrod', 'dark goldenrod', 'rosy brown',
'indian red', 'saddle brown', 'sandy brown',
'dark salmon', 'salmon', 'light salmon', 'orange', 'dark orange',
'coral', 'light coral', 'tomato', 'orange red', 'red', 'hot pink', 'deep pink', 'pink', 'light pink',
'pale violet red', 'maroon', 'medium violet red', 'violet red',
'medium orchid', 'dark orchid', 'dark violet', 'blue violet', 'purple', 'medium purple',
'thistle', 'snow2', 'snow3',
'snow4', 'seashell2', 'seashell3', 'seashell4', 'AntiqueWhite1', 'AntiqueWhite2',
'AntiqueWhite3', 'AntiqueWhite4', 'bisque2', 'bisque3', 'bisque4', 'PeachPuff2',
'PeachPuff3', 'PeachPuff4', 'NavajoWhite2', 'NavajoWhite3', 'NavajoWhite4',
'LemonChiffon2', 'LemonChiffon3', 'LemonChiffon4', 'cornsilk2', 'cornsilk3',
'cornsilk4', 'ivory2', 'ivory3', 'ivory4', 'honeydew2', 'honeydew3', 'honeydew4',
'LavenderBlush2', 'LavenderBlush3', 'LavenderBlush4', 'MistyRose2', 'MistyRose3',
'MistyRose4', 'azure2', 'azure3', 'azure4', 'SlateBlue1', 'SlateBlue2', 'SlateBlue3',
'SlateBlue4', 'RoyalBlue1', 'RoyalBlue2', 'RoyalBlue3', 'RoyalBlue4', 'blue2', 'blue4',
'DodgerBlue2', 'DodgerBlue3', 'DodgerBlue4', 'SteelBlue1', 'SteelBlue2',
'SteelBlue3', 'SteelBlue4', 'DeepSkyBlue2', 'DeepSkyBlue3', 'DeepSkyBlue4',
'SkyBlue1', 'SkyBlue2', 'SkyBlue3', 'SkyBlue4', 'LightSkyBlue1', 'LightSkyBlue2',
'LightSkyBlue3', 'LightSkyBlue4', 'Slategray1', 'Slategray2', 'Slategray3',
'Slategray4', 'LightSteelBlue1', 'LightSteelBlue2', 'LightSteelBlue3',
'LightSteelBlue4', 'LightBlue1', 'LightBlue2', 'LightBlue3', 'LightBlue4',
'LightCyan2', 'LightCyan3', 'LightCyan4', 'PaleTurquoise1', 'PaleTurquoise2',
'PaleTurquoise3', 'PaleTurquoise4', 'CadetBlue1', 'CadetBlue2', 'CadetBlue3',
'CadetBlue4', 'turquoise1', 'turquoise2', 'turquoise3', 'turquoise4', 'cyan2', 'cyan3',
'cyan4', 'DarkSlategray1', 'DarkSlategray2', 'DarkSlategray3', 'DarkSlategray4',
'aquamarine2', 'aquamarine4', 'DarkSeaGreen1', 'DarkSeaGreen2', 'DarkSeaGreen3',
'DarkSeaGreen4', 'SeaGreen1', 'SeaGreen2', 'SeaGreen3', 'PaleGreen1', 'PaleGreen2',
'PaleGreen3', 'PaleGreen4', 'SpringGreen2', 'SpringGreen3', 'SpringGreen4',
'green2', 'green3', 'green4', 'chartreuse2', 'chartreuse3', 'chartreuse4',
'OliveDrab1', 'OliveDrab2', 'OliveDrab4', 'DarkOliveGreen1', 'DarkOliveGreen2',
'DarkOliveGreen3', 'DarkOliveGreen4', 'khaki1', 'khaki2', 'khaki3', 'khaki4',
'LightGoldenrod1', 'LightGoldenrod2', 'LightGoldenrod3', 'LightGoldenrod4',
'LightYellow2', 'LightYellow3', 'LightYellow4', 'yellow2', 'yellow3', 'yellow4',
'gold2', 'gold3', 'gold4', 'goldenrod1', 'goldenrod2', 'goldenrod3', 'goldenrod4',
'DarkGoldenrod1', 'DarkGoldenrod2', 'DarkGoldenrod3', 'DarkGoldenrod4',
'RosyBrown1', 'RosyBrown2', 'RosyBrown3', 'RosyBrown4', 'IndianRed1', 'IndianRed2',
'IndianRed3', 'IndianRed4', 'sienna1', 'sienna2', 'sienna3', 'sienna4', 'burlywood1',
'burlywood2', 'burlywood3', 'burlywood4', 'wheat1', 'wheat2', 'wheat3', 'wheat4', 'tan1',
'tan2', 'tan4', 'chocolate1', 'chocolate2', 'chocolate3', 'firebrick1', 'firebrick2',
'firebrick3', 'firebrick4', 'brown1', 'brown2', 'brown3', 'brown4', 'salmon1', 'salmon2',
'salmon3', 'salmon4', 'LightSalmon2', 'LightSalmon3', 'LightSalmon4', 'orange2',
'orange3', 'orange4', 'DarkOrange1', 'DarkOrange2', 'DarkOrange3', 'DarkOrange4',
'coral1', 'coral2', 'coral3', 'coral4', 'tomato2', 'tomato3', 'tomato4', 'OrangeRed2',
'OrangeRed3', 'OrangeRed4', 'red2', 'red3', 'red4', 'DeepPink2', 'DeepPink3', 'DeepPink4',
'HotPink1', 'HotPink2', 'HotPink3', 'HotPink4', 'pink1', 'pink2', 'pink3', 'pink4',
'LightPink1', 'LightPink2', 'LightPink3', 'LightPink4', 'PaleVioletRed1',
'PaleVioletRed2', 'PaleVioletRed3', 'PaleVioletRed4', 'maroon1', 'maroon2',
'maroon3', 'maroon4', 'VioletRed1', 'VioletRed2', 'VioletRed3', 'VioletRed4',
'magenta2', 'magenta3', 'magenta4', 'orchid1', 'orchid2', 'orchid3', 'orchid4', 'plum1',
'plum2', 'plum3', 'plum4', 'MediumOrchid1', 'MediumOrchid2', 'MediumOrchid3',
'MediumOrchid4', 'DarkOrchid1', 'DarkOrchid2', 'DarkOrchid3', 'DarkOrchid4',
'purple1', 'purple2', 'purple3', 'purple4', 'MediumPurple1', 'MediumPurple2',
'MediumPurple3', 'MediumPurple4', 'thistle1', 'thistle2', 'thistle3', 'thistle4',
'grey1', 'grey2', 'grey3', 'grey4', 'grey5', 'grey6', 'grey7', 'grey8', 'grey9', 'grey10',
'grey11', 'grey12', 'grey13', 'grey14', 'grey15', 'grey16', 'grey17', 'grey18', 'grey19',
'grey20', 'grey21', 'grey22', 'grey23', 'grey24', 'grey25', 'grey26', 'grey27', 'grey28',
'grey29', 'grey30', 'grey31', 'grey32', 'grey33', 'grey34', 'grey35', 'grey36', 'grey37',
'grey38', 'grey39', 'grey40', 'grey42', 'grey43', 'grey44', 'grey45', 'grey46', 'grey47',
'grey48', 'grey49', 'grey50', 'grey51', 'grey52', 'grey53', 'grey54', 'grey55', 'grey56',
'grey57', 'grey58', 'grey59', 'grey60', 'grey61', 'grey62', 'grey63', 'grey64', 'grey65',
'grey66', 'grey67', 'grey68', 'grey69', 'grey70', 'grey71', 'grey72', 'grey73', 'grey74',
'grey75', 'grey76', 'grey77', 'grey78', 'grey79', 'grey80', 'grey81', 'grey82', 'grey83',
'grey84', 'grey85', 'grey86', 'grey87', 'grey88', 'grey89', 'grey90', 'grey91', 'grey92',
'grey93', 'grey94', 'grey95', 'grey97', 'grey98', 'grey99']
sg.SetOptions(button_element_size=(12,1), element_padding=(0,0), auto_size_buttons=False, border_width=0)
layout = [[sg.Text('Click on a color square to see both white and black text on that color', text_color='blue', font='Any 15')]]
row = []
# -- Create primary color viewer window --
for i, color in enumerate(COLORS):
row.append(sg.Button(color, button_color=('black', color), key=color))
if (i+1) % 12 == 0:
layout.append(row)
row = []
window = sg.Window('Color Viewer', grab_anywhere=False, font=('any 9')).Layout(layout)
# -- Event loop --
while True:
event, values = window.Read()
if event is None:
break
# -- Create a secondary window that shows white and black text on chosen color
layout2 =[[sg.DummyButton(event, button_color=('white', event)), sg.DummyButton(event, button_color=('black', event))]]
sg.Window('Buttons with white and black text', keep_on_top=True).Layout(layout2).Read(timeout=0)

View file

@ -0,0 +1,25 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
sg.ChangeLookAndFeel('BlueMono')
# Column layout
col = [[sg.Text('col Row 1', text_color='white', background_color='blue')],
[sg.Text('col Row 2', text_color='white', background_color='blue'), sg.Input('col input 1')],
[sg.Text('col Row 3', text_color='white', background_color='blue'), sg.Input('col input 2')]]
# Window layout
layout = [[sg.Listbox(values=('Listbox Item 1', 'Listbox Item 2', 'Listbox Item 3'),
select_mode=sg.LISTBOX_SELECT_MODE_MULTIPLE, size=(20, 3)),
sg.Column(col, background_color='blue')],
[sg.Input('Last input')],
[sg.OK()]]
# Display the window and get values
event, values = sg.Window('Compact 1-line form with column').Layout(layout).Read()
sg.Popup(event, values, line_width=200)

View file

@ -0,0 +1,45 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
# sg.SetOptions(button_color=sg.COLOR_SYSTEM_DEFAULT)
def GetFilesToCompare():
form_rows = [[sg.Text('Enter 2 files to comare')],
[sg.Text('File 1', size=(15, 1)), sg.InputText(key='file1'), sg.FileBrowse()],
[sg.Text('File 2', size=(15, 1)), sg.InputText(key='file2'), sg.FileBrowse(target='file2')],
[sg.Submit(), sg.Cancel()]]
window = sg.Window('File Compare')
event, values = window.Layout(form_rows).Read()
return event, values
def main():
button, values = GetFilesToCompare()
f1 = values['file1']
f2 = values['file2']
if any((button != 'Submit', f1 =='', f2 == '')):
sg.PopupError('Operation cancelled')
sys.exit(69)
# --- This portion of the code is not GUI related ---
with open(f1, 'rb') as file1:
with open(f2, 'rb') as file2:
a = file1.read()
b = file2.read()
for i, x in enumerate(a):
if x != b[i]:
sg.Popup('Compare results for files', f1, f2, '**** Mismatch at offset {} ****'.format(i))
break
else:
if len(a) == len(b):
sg.Popup('**** The files are IDENTICAL ****')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,242 @@
"""
@created: 2018-08-19 18:00:00
@author: (c) 2018 Jorj X. McKie
Display a PyMuPDF Document using Tkinter
-------------------------------------------------------------------------------
Dependencies:
-------------
PyMuPDF, PySimpleGUI (requires Python 3), Tkinter, PIL
License:
--------
GNU GPL V3+
Description
------------
Get filename and start displaying page 1. Please note that all file types
of MuPDF are supported (including EPUB e-books and HTML files for example).
Pages can be directly jumped to, or buttons can be used for paging.
This version contains enhancements:
* Use of PIL improves response times by a factor 3 or more.
* Zooming is now flexible: only one button serves as a toggle. Arrow keys can
be used for moving the window when zooming.
We also interpret keyboard events (PageDown / PageUp) and mouse wheel actions
to support paging as if a button was clicked. Similarly, we do not include
a 'Quit' button. Instead, the ESCAPE key can be used, or cancelling the window.
To improve paging performance, we are not directly creating pixmaps from
pages, but instead from the fitz.DisplayList of the page. A display list
will be stored in a list and looked up by page number. This way, zooming
pixmaps and page re-visits will re-use a once-created display list.
"""
import sys
import fitz
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import tkinter as tk
from PIL import Image, ImageTk
import time
if len(sys.argv) == 1:
fname = sg.PopupGetFile('Document Browser', 'Document file to open', no_window=True,
file_types = (
("PDF Files", "*.pdf"),
("XPS Files", "*.*xps"),
("Epub Files", "*.epub"),
("Fiction Books", "*.fb2"),
("Comic Books", "*.cbz"),
("HTML", "*.htm*")
# add more document types here
)
)
else:
fname = sys.argv[1]
if not fname:
sg.Popup("Cancelling:", "No filename supplied")
raise SystemExit("Cancelled: no filename supplied")
doc = fitz.open(fname)
page_count = len(doc)
# used for response time statistics only
fitz_img_time = 0.0
tk_img_time = 0.0
img_count = 1
# allocate storage for page display lists
dlist_tab = [None] * page_count
title = "PyMuPDF display of '%s', pages: %i" % (fname, page_count)
def get_page(pno, zoom = False, max_size = None, first = False):
"""Return a PNG image for a document page number.
"""
dlist = dlist_tab[pno] # get display list of page number
if not dlist: # create if not yet there
dlist_tab[pno] = doc[pno].getDisplayList()
dlist = dlist_tab[pno]
r = dlist.rect # the page rectangle
clip = r
# ensure image fits screen:
# exploit, but do not exceed width or height
zoom_0 = 1
if max_size:
zoom_0 = min(1, max_size[0] / r.width, max_size[1] / r.height)
if zoom_0 == 1:
zoom_0 = min(max_size[0] / r.width, max_size[1] / r.height)
mat_0 = fitz.Matrix(zoom_0, zoom_0)
if not zoom: # show total page
pix = dlist.getPixmap(matrix = mat_0, alpha=False)
else:
mp = r.tl + (r.br - r.tl) * 0.5 # page rect center
w2 = r.width / 2
h2 = r.height / 2
clip = r * 0.5
tl = zoom[0] # old top-left
tl.x += zoom[1] * (w2 / 2)
tl.x = max(0, tl.x)
tl.x = min(w2, tl.x)
tl.y += zoom[2] * (h2 / 2)
tl.y = max(0, tl.y)
tl.y = min(h2, tl.y)
clip = fitz.Rect(tl, tl.x + w2, tl.y + h2)
mat = mat_0 * fitz.Matrix(2, 2) # zoom matrix
pix = dlist.getPixmap(alpha=False, matrix=mat, clip=clip)
if first: # first call: tkinter still inactive
img = pix.getPNGData() # so use fitz png output
else: # else take tk photo image
pilimg = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
img = ImageTk.PhotoImage(pilimg)
return img, clip.tl # return image, clip position
root = tk.Tk()
max_width = root.winfo_screenwidth() - 20
max_height = root.winfo_screenheight() - 135
max_size = (max_width, max_height)
root.destroy()
del root
window = sg.Window(title, return_keyboard_events = True,
location = (0,0), use_default_focus = False, no_titlebar=False)
cur_page = 0
data, clip_pos = get_page(cur_page,
zoom = False,
max_size = max_size,
first = True)
image_elem = sg.Image(data = data)
goto = sg.InputText(str(cur_page + 1), size=(5, 1), do_not_clear=True,
key = "PageNumber")
layout = [
[
sg.ReadButton('Next'),
sg.ReadButton('Prev'),
sg.Text('Page:'),
goto,
sg.Text('(%i)' % page_count),
sg.ReadButton('Zoom'),
sg.Text('(toggle on/off, use arrows to navigate while zooming)'),
],
[image_elem],
]
window.Layout(layout)
# now define the buttons / events we want to handle
enter_buttons = [chr(13), "Return:13"]
quit_buttons = ["Escape:27", chr(27)]
next_buttons = ["Next", "Next:34", "MouseWheel:Down"]
prev_buttons = ["Prev", "Prior:33", "MouseWheel:Up"]
Up = "Up:38"
Left = "Left:37"
Right = "Right:39"
Down = "Down:40"
zoom_buttons = ["Zoom", Up, Down, Left, Right]
# all the buttons we will handle
my_keys = enter_buttons + next_buttons + prev_buttons + zoom_buttons
# old page store and zoom toggle
old_page = 0
old_zoom = False
while True:
event, value = window.Read()
if event is None and (value is None or value['PageNumber'] is None):
break
if event in quit_buttons:
break
zoom_pressed = False
zoom = False
if event in enter_buttons:
try:
cur_page = int(value['PageNumber']) - 1 # check if valid
while cur_page < 0:
cur_page += page_count
except:
cur_page = 0 # this guy's trying to fool me
elif event in next_buttons:
cur_page += 1
elif event in prev_buttons:
cur_page -= 1
elif event == Up:
zoom = (clip_pos, 0, -1)
elif event == Down:
zoom = (clip_pos, 0, 1)
elif event == Left:
zoom = (clip_pos, -1, 0)
elif event == Right:
zoom = (clip_pos, 1, 0)
elif event == "Zoom":
zoom_pressed = True
zoom = (clip_pos, 0, 0)
# sanitize page number
if cur_page >= page_count: # wrap around
cur_page = 0
while cur_page < 0: # pages > 0 look nicer
cur_page += page_count
if zoom_pressed and old_zoom:
zoom = zoom_pressed = old_zoom = False
t0 = time.perf_counter()
data, clip_pos = get_page(cur_page, zoom = zoom, max_size = max_size,
first = False)
t1 = time.perf_counter()
image_elem.Update(data = data)
t2 = time.perf_counter()
fitz_img_time += t1 - t0
tk_img_time += t2 - t1
img_count += 1
old_page = cur_page
old_zoom = zoom_pressed or zoom
# update page number field
if event in my_keys:
goto.Update(str(cur_page + 1))
# print some response time statistics
if img_count > 0:
print("response times for '%s'" % doc.name)
print("%.4f" % (fitz_img_time/img_count), "sec fitz avg. image time")
print("%.4f" % (tk_img_time/img_count), "sec tk avg. image time")
print("%.4f" % ((fitz_img_time + tk_img_time)/img_count), "sec avg. total time")
print(img_count, "images read")
print(page_count, "pages")

View file

@ -0,0 +1,25 @@
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [
[sg.Text('Your typed chars appear here:'), sg.Text('', key='_OUTPUT_')],
[sg.Input(do_not_clear=True, key='_IN_')],
[sg.Button('Show'), sg.Button('Exit')]
]
window = sg.Window('Window Title').Layout(layout)
while True: # Event Loop
event, values = window.Read()
print(event, values)
if event is None or event == 'Exit':
break
if event == 'Show':
# change the "output" element to be the value of "input" element
window.FindElement('_OUTPUT_').Update(values['_IN_'])
window.Close()

View file

@ -0,0 +1,60 @@
"""
When creating a new PySimpleGUI program from scratch, start here.
These are the accepted design patterns that cover the two primary use cases
1. A window that closes when a "submit" type button is clicked
2. A persistent window that stays open after button clicks (uses an event loop)
3. A persistent window that needs access to the elements' interface variables
"""
# ---------------------------------#
# DESIGN PATTERN 1 - Simple Window #
# ---------------------------------#
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[ sg.Text('My layout') ]]
window = sg.Window('My window').Layout(layout)
event, values = window.Read()
# -------------------------------------#
# DESIGN PATTERN 2 - Persistent Window #
# -------------------------------------#
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[ sg.Text('My layout') ]]
window = sg.Window('My new window').Layout(layout)
while True: # Event Loop
event, values = window.Read()
if event is None:
break
# ------------------------------------------------------------------#
# DESIGN PATTERN 3 - Persistent Window with "early update" required #
# ------------------------------------------------------------------#
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[ sg.Text('My layout') ]]
window = sg.Window('My new window').Layout(layout).Finalize()
# if you have operations on elements that must take place before the event loop, do them here
while True: # Event Loop
event, values = window.Read()
if event is None:
break

View file

@ -0,0 +1,90 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import subprocess
import os
"""
Demo_Toolbar - A floating toolbar with quick launcher
One cool PySimpleGUI demo. Shows borderless windows, grab_anywhere, tight button layout
You can setup a specific program to launch when a button is clicked, or use the
Combobox to select a .py file found in the root folder, and run that file.
"""
ROOT_PATH = './'
def Launcher():
# def print(line):
# window.FindElement('output').Update(line)
sg.ChangeLookAndFeel('Dark')
namesonly = [f for f in os.listdir(ROOT_PATH) if f.endswith('.py') ]
if len(namesonly) == 0:
namesonly = ['test 1', 'test 2', 'test 3']
sg.SetOptions(element_padding=(0,0), button_element_size=(12,1), auto_size_buttons=False)
layout = [[sg.Combo(values=namesonly, size=(35,30), key='demofile'),
sg.ReadButton('Run', button_color=('white', '#00168B')),
sg.ReadButton('Program 1'),
sg.ReadButton('Program 2'),
sg.ReadButton('Program 3', button_color=('white', '#35008B')),
sg.Button('EXIT', button_color=('white','firebrick3'))],
[sg.T('', text_color='white', size=(50,1), key='output')]]
window = sg.Window('Floating Toolbar', no_titlebar=True, grab_anywhere=True, keep_on_top=True).Layout(layout)
# ---===--- Loop taking in user input and executing appropriate program --- #
while True:
(event, values) = window.Read()
if event == 'EXIT' or event is None:
break # exit button clicked
if event == 'Program 1':
print('Run your program 1 here!')
elif event == 'Program 2':
print('Run your program 2 here!')
elif event == 'Run':
file = values['demofile']
print('Launching %s'%file)
ExecuteCommandSubprocess('python', os.path.join(ROOT_PATH, file))
else:
print(event)
def ExecuteCommandSubprocess(command, *args, wait=False):
try:
if sys.platform == 'linux':
arg_string = ''
arg_string = ' '.join([str(arg) for arg in args])
# for arg in args:
# arg_string += ' ' + str(arg)
print('python3 ' + arg_string)
sp = subprocess.Popen(['python3 ', arg_string ], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
else:
arg_string = ' '.join([str(arg) for arg in args])
sp = subprocess.Popen([command, arg_string], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# sp = subprocess.Popen([command, list(args)], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if wait:
out, err = sp.communicate()
if out:
print(out.decode("utf-8"))
if err:
print(err.decode("utf-8"))
except: pass
if __name__ == '__main__':
Launcher()

View file

@ -0,0 +1,110 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import psutil
"""
Desktop floating widget - CPU Cores
Uses psutil to display:
CPU usage on each individual core
Information is updated once a second and is shown as an area graph that scrolls
"""
GRAPH_WIDTH = 120 # each individual graph size in pixels
GRAPH_HEIGHT = 40
TRANSPARENCY = .8 # how transparent the window looks. 0 = invisible, 1 = normal window
NUM_COLS = 4
POLL_FREQUENCY = 500 # how often to update graphs in milliseconds
colors = ('#23a0a0', '#56d856', '#be45be', '#5681d8', '#d34545', '#BE7C29')
# DashGraph does the drawing of each graph
class DashGraph(object):
def __init__(self, graph_elem, text_elem, starting_count, color):
self.graph_current_item = 0
self.graph_elem = graph_elem
self.text_elem = text_elem
self.prev_value = starting_count
self.max_sent = 1
self.color = color
def graph_percentage_abs(self, value):
self.graph_elem.DrawLine((self.graph_current_item, 0), (self.graph_current_item, value), color=self.color)
if self.graph_current_item >= GRAPH_WIDTH:
self.graph_elem.Move(-1,0)
else:
self.graph_current_item += 1
def text_display(self, text):
self.text_elem.Update(text)
def main():
# A couple of "Uber Elements" that combine several elements and enable bulk edits
def Txt(text, **kwargs):
return(sg.Text(text, font=('Helvetica 8'), **kwargs))
def GraphColumn(name, key):
col = sg.Column([[Txt(name, key=key+'_TXT_'), ],
[sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black',
key=key+'_GRAPH_')]], pad=(2, 2))
return col
num_cores = len(psutil.cpu_percent(percpu=True)) # get the number of cores in the CPU
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0,0), margins=(1,1), border_width=0)
# ---------------- Create Layout ----------------
layout = [[ sg.Button('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window'),
sg.Text(' CPU Core Usage')] ]
# add on the graphs
for rows in range(num_cores//NUM_COLS+1):
row = []
for cols in range(min(num_cores-rows*NUM_COLS, NUM_COLS)):
row.append(GraphColumn('CPU '+str(rows*NUM_COLS+cols), '_CPU_'+str(rows*NUM_COLS+cols)))
layout.append(row)
# ---------------- Create Window ----------------
window = sg.Window('PSG System Dashboard',
keep_on_top=True,
auto_size_buttons=False,
grab_anywhere=True,
no_titlebar=True,
default_button_element_size=(12, 1),
return_keyboard_events=True,
alpha_channel=TRANSPARENCY,
use_default_focus=False,
).Layout(layout).Finalize()
# setup graphs & initial values
graphs = []
for i in range(num_cores):
graphs.append(DashGraph(window.FindElement('_CPU_'+str(i)+'_GRAPH_'),
window.FindElement('_CPU_'+str(i) + '_TXT_'),
0, colors[i%6]))
# ---------------- main loop ----------------
while (True):
# --------- Read and update window once every Polling Frequency --------
event, values = window.Read(timeout=POLL_FREQUENCY)
if event in (None, 'Exit'): # Be nice and give an exit
break
# read CPU for each core
stats = psutil.cpu_percent(percpu=True)
# Update each graph
for i in range(num_cores):
graphs[i].graph_percentage_abs(stats[i])
graphs[i].text_display('{} CPU {:2.0f}'.format(i, stats[i]))
window.Close()
if __name__ == "__main__":
# the clever Red X graphic
red_x = "R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=="
main()
sys.exit(69)

View file

@ -0,0 +1,79 @@
#!/usr/bin/env python
import sys
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import time
import random
import psutil
from threading import Thread
STEP_SIZE=3
SAMPLES = 300
SAMPLE_MAX = 500
CANVAS_SIZE = (300,200)
g_interval = .25
g_cpu_percent = 0
g_procs = None
g_exit = False
def CPU_thread(args):
global g_interval, g_cpu_percent, g_procs, g_exit
while not g_exit:
try:
g_cpu_percent = psutil.cpu_percent(interval=g_interval)
g_procs = psutil.process_iter()
except:
pass
def main():
global g_exit, g_response_time
# start ping measurement thread
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0,0))
layout = [ [sg.Quit( button_color=('white','black')), sg.T('', pad=((100,0),0), font='Any 15', key='output')],
[sg.Graph(CANVAS_SIZE, (0,0), (SAMPLES,SAMPLE_MAX),background_color='black', key='graph')],]
window = sg.Window('CPU Graph', grab_anywhere=True, keep_on_top=True, background_color='black', no_titlebar=True, use_default_focus=False).Layout(layout)
graph = window.FindElement('graph')
output = window.FindElement('output')
# start cpu measurement thread
thread = Thread(target=CPU_thread,args=(None,))
thread.start()
last_cpu = i = 0
prev_x, prev_y = 0, 0
while True: # the Event Loop
time.sleep(.5)
event, values = window.Read(timeout=0)
if event == 'Quit' or event is None: # always give ths user a way out
break
# do CPU measurement and graph it
current_cpu = int(g_cpu_percent*10)
if current_cpu == last_cpu:
continue
output.Update(current_cpu/10) # show current cpu usage at top
if current_cpu > SAMPLE_MAX:
current_cpu = SAMPLE_MAX
new_x, new_y = i, current_cpu
if i >= SAMPLES:
graph.Move(-STEP_SIZE,0) # shift graph over if full of data
prev_x = prev_x - STEP_SIZE
graph.DrawLine((prev_x, prev_y), (new_x, new_y), color='white')
prev_x, prev_y = new_x, new_y
i += STEP_SIZE if i < SAMPLES else 0
last_cpu = current_cpu
g_exit = True
window.Close()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,99 @@
#!/usr/bin/env python
import sys
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import psutil
import time
from threading import Thread
import operator
"""
PSUTIL Desktop Widget
Creates a floating CPU utilization window that is always on top of other windows
You move it by grabbing anywhere on the window
Good example of how to do a non-blocking, polling program using PySimpleGUI
Use the spinner to adjust the number of seconds between readings of the CPU utilizaiton
NOTE - you will get a warning message printed when you exit using exit button.
It will look something like:
invalid command name "1616802625480StopMove"
"""
# globale used to communicate with thread.. yea yea... it's working fine
g_interval = 1
g_cpu_percent = 0
g_procs = None
g_exit = False
def CPU_thread(args):
global g_interval, g_cpu_percent, g_procs, g_exit
while not g_exit:
try:
g_cpu_percent = psutil.cpu_percent(interval=g_interval)
g_procs = psutil.process_iter()
except:
pass
def main():
global g_interval, g_procs, g_exit
# ---------------- Create Form ----------------
sg.ChangeLookAndFeel('Black')
layout = [[sg.Text('', size=(8,1), font=('Helvetica', 20),text_color=sg.YELLOWS[0],
justification='center', key='text')],
[sg.Text('', size=(30, 8), font=('Courier New', 12),text_color='white', justification='left', key='processes')],
[sg.Exit(button_color=('white', 'firebrick4'), pad=((15,0), 0), size=(9,1)),
sg.Spin([x+1 for x in range(10)], 3, key='spin')],]
window = sg.Window('CPU Utilization',
no_titlebar=True,
keep_on_top=True,
alpha_channel=.8,
grab_anywhere=True).Layout(layout)
# start cpu measurement thread
thread = Thread(target=CPU_thread,args=(None,))
thread.start()
timeout_value = 1 # make first read really quick
g_interval = 1
# ---------------- main loop ----------------
while (True):
# --------- Read and update window --------
event, values = window.Read(timeout=timeout_value, timeout_key='Timeout')
# --------- Do Button Operations --------
if event is None or event == 'Exit':
break
timeout_value = int(values['spin']) * 1000
cpu_percent = g_cpu_percent
display_string = ''
if g_procs:
# --------- Create list of top % CPU porocesses --------
try:
top = {proc.name() : proc.cpu_percent() for proc in g_procs}
except: pass
top_sorted = sorted(top.items(), key=operator.itemgetter(1), reverse=True)
if top_sorted:
top_sorted.pop(0)
display_string = ''
for proc, cpu in top_sorted:
display_string += '{:2.2f} {}\n'.format(cpu/10, proc)
# --------- Display timer and proceses in window --------
window.FindElement('text').Update('CPU {}'.format(cpu_percent))
window.FindElement('processes').Update(display_string)
g_exit = True
thread.join()
if __name__ == "__main__":
main()

View file

@ -0,0 +1,45 @@
#!/usr/bin/env python
import sys
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import psutil
# ---------------- Create Form ----------------
sg.ChangeLookAndFeel('Black')
layout = [[sg.Text('CPU Utilization')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='_text_')],
[sg.Exit(button_color=('white', 'firebrick4'), pad=((15, 0), 0), size=(9,1)),
sg.Spin([x + 1 for x in range(10)], 3, key='_spin_')]]
# Layout the rows of the Window
window = sg.Window('CPU Meter',
no_titlebar=True,
keep_on_top=True,
grab_anywhere=True).Layout(layout).Finalize()
# ---------------- main loop ----------------
interval = 10 # For the first one, make it quick
while (True):
# --------- Read and update window --------
event, values = window.Read(timeout=interval)
# --------- Do Button Operations --------
if event is None or event == 'Exit':
break
interval = int(values['_spin_'])*1000
cpu_percent = psutil.cpu_percent(interval=1)
# --------- Display timer in window --------
window.FindElement('_text_').Update(f'CPU {cpu_percent:02.0f}%')
# Broke out of main loop. Close the window.
window.CloseNonBlocking()

View file

@ -0,0 +1,108 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
sg.PopupError('Sorry, at the moment this program only runs on Python 3')
sys.exit()
import email
import imaplib
from datetime import datetime
import calendar
IMAP_SERVER_GMAIL = 'imap.gmail.com' # gmail server address
IMAP_SERVER_HOTMAIL = 'imap-mail.outlook.com' # hotmail server address
################# Change these to match your email setup ################
LOGIN_EMAIL = 'you@mail.com'
LOGIN_PASSWORD = 'your email password'
IMAP_SERVER = IMAP_SERVER_GMAIL # change to match your email service
MAX_EMAILS = 10
def gui():
sg.ChangeLookAndFeel('Topanga')
sg.SetOptions(border_width=0, margins=(0, 0), element_padding=(4, 0))
layout = [[sg.T('Email New Mail Notification' + 48 * ' '),
sg.Button('', image_data=refresh, button_color=('#282923', '#282923'), key='_refresh_',
tooltip='Refreshes Email'),
sg.Button('', image_data=red_x, button_color=('#282923', '#282923'), key='_quit_',
tooltip='Closes window')],
[sg.T('', key='_status_', size=(25, 1))], ]
for i in range(MAX_EMAILS):
layout.append([sg.T('', size=(20, 1), key='{}date'.format(i), font='Sans 8'),
sg.T('', size=(45, 1), font='Sans 8', key='{}from'.format(i))])
window = sg.Window('',
no_titlebar=True,
grab_anywhere=True,
keep_on_top=True,
alpha_channel=0,
).Layout(layout).Finalize()
# move the window to the upper right corner of the screen
w, h = window.GetScreenDimensions()
window.Move(w - 410, 0)
window.SetAlpha(.9)
window.Refresh()
status_elem = window.FindElement('_status_')
# The Event Loop
while True:
status_elem.Update('Reading...')
window.Refresh()
read_mail(window)
status_elem.Update('')
event, values = window.Read(timeout=30 * 1000) # return every 30 seconds
if event == '_quit_':
break
def read_mail(window):
"""
Reads late emails from IMAP server and displays them in the Window
:param window: window to display emails in
:return:
"""
mail = imaplib.IMAP4_SSL(IMAP_SERVER)
(retcode, capabilities) = mail.login(LOGIN_EMAIL, LOGIN_PASSWORD)
mail.list()
typ, data = mail.select('Inbox')
n = 0
now = datetime.now()
# get messages from today
search_string = '(SENTON {}-{}-{})'.format(now.day, calendar.month_abbr[now.month], now.year)
(retcode, messages) = mail.search(None, search_string)
if retcode == 'OK':
msg_list = messages[0].split() # message numbers are separated by spaces, turn into list
msg_list.sort(reverse=True) # sort messages descending
for n, message in enumerate(msg_list):
if n >= MAX_EMAILS:
break
from_elem = window.FindElement('{}from'.format(n))
date_elem = window.FindElement('{}date'.format(n))
from_elem.Update('') # erase them so you know they're changing
date_elem.Update('')
window.Refresh()
typ, data = mail.fetch(message, '(RFC822)')
for response_part in data:
if isinstance(response_part, tuple):
original = email.message_from_bytes(response_part[1])
date_str = original['Date'][:22]
from_elem.Update(original['From'])
date_elem.Update(date_str)
window.Refresh() # make the window changes show up right away
red_x = "R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=="
refresh = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACb0lEQVR4XpVTXUiTbxT/vePdFuF0BTFW9ufVvMlu+iACka6CQY1gQVdtEmTMpSKzzJT/RTdCRHhT4F0Us8LGVqlo1lZaFslWQWBkN+tDkpSpbfNz797T8zy6DbUbf/Dbec7vfOycMwa0DBJjM7Ko72mBtz+KplCS6Ronf3NNxNZBt2qv4dJzL0uKwGRqU/6zHDqyd1dBk32/xMnfXOMxkVPXXYlVSLjykk4fKIb/4zgUSxEO7zRBKd4Bjm/jU9ys8f2fJoCFhRiWl6pw6+Qw0BymhlfT5Lg/xmycHA++ktL+nsRqrUOrdpBpH6hhKC7yhObti0CgKUTu0KTgcd8X4j4aB2bYvj7UPqkQrO/1cU25ESV3eJJO8LzLIQ11/CYXn5Grf4KqGF19E3Ts9iixe2QPm0dtt5PtP6NcHxF5ZVfDhIbeqMQ6E0hcI4ec327jah513T4YDM5TR/dh8vc0hkfHUxI2gwuPKyDLb2wV5cIdePuZZGwWmQxSSyqICFBVyKgJJkFaQW4Hna4THQ4X/gUiD2+QXEwjNZsASJvTgWgMqoY95WWw7raAJdjheeTEeniCTqgZu2IxswnSmGI3gEZjMiQpAMocTC2nJcm4hU9gRjp9E+6Ajb07wKFpHqRVOzKqedFUhOX4HyRnEwSjMQCB8/4IqnxU2DYiaGnsIe7n2UlK61MWe0dbW18Ijdfk/wuy7IXeEEvEvmM+kcRM4XYYSkohW62ChtIS/NKbWGwO8z9+Anp9TNSsQU2wEtVdEZy5o7Gfi7Z5ewj/vxbkPs51kYhVP4zAw3I3IN+ohSVFcfZeEs67Gid/c03E1uEv5QpTFzvZK5EAAAAASUVORK5CYII='
gui()

View file

@ -0,0 +1,71 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import time
"""
Timer Desktop Widget Creates a floating timer that is always on top of other windows You move it by grabbing anywhere on the window Good example of how to do a non-blocking, polling program using SimpleGUI Can be used to poll hardware when running on a Pi
While the timer ticks are being generated by PySimpleGUI's "timeout" mechanism, the actual value
of the timer that is displayed comes from the system timer, time.time(). This guarantees an
accurate time value is displayed regardless of the accuracy of the PySimpleGUI timer tick. If
this design were not used, then the time value displayed would slowly drift by the amount of time
it takes to execute the PySimpleGUI read and update calls (not good!)
NOTE - you will get a warning message printed when you exit using exit button.
It will look something like: invalid command name \"1616802625480StopMove\"
"""
# ---------------- Create Form ----------------
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0, 0))
layout = [[sg.Text('')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
[sg.Button('Pause', key='button', button_color=('white', '#001480')),
sg.Button('Reset', button_color=('white', '#007339'), key='Reset'),
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout)
# ---------------- main loop ----------------
current_time = 0
paused = False
start_time = int(round(time.time() * 100))
while (True):
# --------- Read and update window --------
if not paused:
event, values = window.Read(timeout=10)
current_time = int(round(time.time() * 100)) - start_time
else:
event, values = window.Read()
if event == 'button':
event = window.FindElement(event).GetText()
# --------- Do Button Operations --------
if event is None or event == 'Exit': # ALWAYS give a way out of program
break
if event is 'Reset':
start_time = int(round(time.time() * 100))
current_time = 0
paused_time = start_time
elif event == 'Pause':
paused = True
paused_time = int(round(time.time() * 100))
element = window.FindElement('button')
element.Update(text='Run')
elif event == 'Run':
paused = False
start_time = start_time + int(round(time.time() * 100)) - paused_time
element = window.FindElement('button')
element.Update(text='Pause')
# --------- Display timer in window --------
window.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
(current_time // 100) % 60,
current_time % 100))
# --------- After loop --------

View file

@ -0,0 +1,138 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import psutil
"""
Desktop floating widget - System status dashboard
Uses psutil to display:
Network I/O
Disk I/O
CPU Used
Mem Used
Information is updated once a second and is shown as an area graph that scrolls
"""
GRAPH_WIDTH = 120 # each individual graph size in pixels
GRAPH_HEIGHT = 40
ALPHA = .7
class DashGraph(object):
def __init__(self, graph_elem, starting_count, color):
self.graph_current_item = 0
self.graph_elem = graph_elem
self.prev_value = starting_count
self.max_sent = 1
self.color = color
def graph_value(self, current_value):
delta = current_value - self.prev_value
self.prev_value = current_value
self.max_sent = max(self.max_sent, delta)
percent_sent = 100 * delta / self.max_sent
self.graph_elem.DrawLine((self.graph_current_item, 0), (self.graph_current_item, percent_sent), color=self.color)
if self.graph_current_item >= GRAPH_WIDTH:
self.graph_elem.Move(-1,0)
else:
self.graph_current_item += 1
return delta
def graph_percentage_abs(self, value):
self.graph_elem.DrawLine((self.graph_current_item, 0), (self.graph_current_item, value), color=self.color)
if self.graph_current_item >= GRAPH_WIDTH:
self.graph_elem.Move(-1,0)
else:
self.graph_current_item += 1
def human_size(bytes, units=[' bytes','KB','MB','GB','TB', 'PB', 'EB']):
""" Returns a human readable string reprentation of bytes"""
return str(bytes) + units[0] if bytes < 1024 else human_size(bytes>>10, units[1:])
def main():
# Make the layout less cluttered and allow bulk-changes to text formatting
def Txt(text, **kwargs):
return(sg.Text(text, font=('Helvetica 8'), **kwargs))
# Update a Text Element
def Txt_Update(window, key, value):
window.FindElement(key).Update(value)
# ---------------- Create Window ----------------
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0,0), margins=(1,1), border_width=0)
def GraphColumn(name, key):
col = sg.Column([[Txt(name, key=key+'TXT_'), ],
[sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black',
key=key+'GRAPH_')]], pad=(2, 2))
return col
layout = [[sg.Text('System Status Dashboard'+' '*18), sg.Button('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window')],
[GraphColumn('Net Out', '_NET_OUT_'),
GraphColumn('Net In', '_NET_IN_')],
[GraphColumn('Disk Read', '_DISK_READ_'),
GraphColumn('Disk Write', '_DISK_WRITE_')],
[GraphColumn('CPU Usage', '_CPU_'),
GraphColumn('Memory Usage', '_MEM_')],]
window = sg.Window('PSG System Dashboard',
keep_on_top=True,
auto_size_buttons=False,
grab_anywhere=True,
no_titlebar=True,
default_button_element_size=(12, 1),
return_keyboard_events=True,
alpha_channel=ALPHA,
use_default_focus=False,
).Layout(layout).Finalize()
# setup graphs & initial values
netio = psutil.net_io_counters()
net_graph_in = DashGraph(window.FindElement('_NET_IN_GRAPH_'), netio.bytes_recv, '#23a0a0')
net_graph_out = DashGraph(window.FindElement('_NET_OUT_GRAPH_'), netio.bytes_sent, '#56d856')
diskio = psutil.disk_io_counters()
disk_graph_write = DashGraph(window.FindElement('_DISK_WRITE_GRAPH_'), diskio.write_bytes, '#be45be')
disk_graph_read = DashGraph(window.FindElement('_DISK_READ_GRAPH_'), diskio.read_bytes, '#5681d8')
cpu_usage_graph = DashGraph(window.FindElement('_CPU_GRAPH_'), 0, '#d34545')
mem_usage_graph = DashGraph(window.FindElement('_MEM_GRAPH_'), 0, '#BE7C29')
print(psutil.cpu_percent(percpu=True))
# ---------------- main loop ----------------
while (True):
# --------- Read and update window once a second--------
event, values = window.Read(timeout=1000)
if event in (None, 'Exit'): # Be nice and give an exit, expecially since there is no titlebar
break
# ----- Network Graphs -----
netio = psutil.net_io_counters()
write_bytes = net_graph_out.graph_value(netio.bytes_sent)
read_bytes = net_graph_in.graph_value(netio.bytes_recv)
Txt_Update(window, '_NET_OUT_TXT_', 'Net out {}'.format(human_size(write_bytes)))
Txt_Update(window, '_NET_IN_TXT_', 'Net In {}'.format(human_size(read_bytes)))
# ----- Disk Graphs -----
diskio = psutil.disk_io_counters()
write_bytes = disk_graph_write.graph_value(diskio.write_bytes)
read_bytes = disk_graph_read.graph_value(diskio.read_bytes)
Txt_Update(window, '_DISK_WRITE_TXT_', 'Disk Write {}'.format(human_size(write_bytes)))
Txt_Update(window, '_DISK_READ_TXT_', 'Disk Read {}'.format(human_size(read_bytes)))
# ----- CPU Graph -----
cpu = psutil.cpu_percent(0)
cpu_usage_graph.graph_percentage_abs(cpu)
Txt_Update(window, '_CPU_TXT_', '{0:2.0f}% CPU Used'.format(cpu))
# ----- Memory Graph -----
mem_used = psutil.virtual_memory().percent
mem_usage_graph.graph_percentage_abs(mem_used)
Txt_Update(window, '_MEM_TXT_', '{}% Memory Used'.format(mem_used))
if __name__ == "__main__":
# the clever Red X graphic
red_x = "R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=="
main()
sys.exit(69)

View file

@ -0,0 +1,45 @@
#!/usr/bin/env python
import sys
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0, 0))
layout = [
[sg.T('Notes:', pad=((3, 0), 0)), sg.In(size=(44, 1), background_color='white', text_color='black', key='notes')],
[sg.T('Output:', pad=((3, 0), 0)), sg.T('', size=(44, 1), text_color='white', key='output')],
[sg.CBox('Checkbox:', default=True, pad=((3, 0), 0), disabled=True, key='cbox'), sg.Listbox((1,2,3,4),size=(8,3),disabled=True, key='listbox'),
sg.Radio('Radio 1', default=True, group_id='1', disabled=True, key='radio1'), sg.Radio('Radio 2', default=False, group_id='1', disabled=True, key='radio2')],
[sg.Spin((1,2,3,4),1,disabled=True, key='spin'), sg.OptionMenu((1,2,3,4),disabled=True, key='option'), sg.Combo(values=(1,2,3,4),disabled=True,key='combo')],
[sg.Multiline('Multiline', size=(20,3),disabled=True, key='multi')],
[sg.Slider((1,10), size=(20,20), orientation='h', disabled=True, key='slider')],
[sg.ReadButton('Enable', button_color=('white', 'black')),
sg.ReadButton('Disable', button_color=('white', 'black')),
sg.ReadButton('Reset', button_color=('white', '#9B0023'), key='reset'),
sg.ReadButton('Values', button_color=('white', 'springgreen4')),
sg.Button('Exit', disabled=True, button_color=('white', '#00406B'), key='exit')]]
window = sg.Window("Disable Elements Demo", default_element_size=(12, 1), text_justification='r', auto_size_text=False,
auto_size_buttons=False, keep_on_top=True, grab_anywhere=False,
default_button_element_size=(12, 1)).Layout(layout).Finalize()
key_list = 'cbox', 'listbox', 'radio1', 'radio2', 'spin', 'option', 'combo', 'reset', 'notes', 'multi', 'slider', 'exit'
for key in key_list: window.FindElement(key).Update(disabled=True) # don't do this kind of for-loop
while True:
event, values = window.Read()
if event in (None, 'exit'):
break
elif event == 'Disable':
for key in key_list: window.FindElement(key).Update(disabled=True)
elif event == 'Enable':
for key in key_list: window.FindElement(key).Update(disabled=False)
elif event == 'Values':
sg.Popup(values, keep_on_top=True)
sys.exit(0)

View file

@ -0,0 +1,62 @@
#!/usr/bin/env python
import sys
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import hashlib
import os
# ====____====____==== FUNCTION DeDuplicate_folder(path) ====____====____==== #
# Function to de-duplicate the folder passed in #
# --------------------------------------------------------------------------- #
def FindDuplicatesFilesInFolder(path):
shatab = []
total = 0
small_count, dup_count, error_count = 0,0,0
pngdir = path
if not os.path.exists(path):
sg.Popup('Duplicate Finder', '** Folder doesn\'t exist***', path)
return
pngfiles = os.listdir(pngdir)
total_files = len(pngfiles)
for idx, f in enumerate(pngfiles):
if not sg.OneLineProgressMeter('Counting Duplicates', idx + 1, total_files, 'Counting Duplicate Files'):
break
total += 1
fname = os.path.join(pngdir, f)
if os.path.isdir(fname):
continue
x = open(fname, "rb").read()
m = hashlib.sha256()
m.update(x)
f_sha = m.digest()
if f_sha in shatab:
# uncomment next line to remove duplicate files
# os.remove(fname)
dup_count += 1
# sg.Print(f'Duplicate file - {f}') # cannot current use sg.Print with Progress Meter
continue
shatab.append(f_sha)
msg = '{} Files processed\n {} Duplicates found'.format(total_files, dup_count)
sg.Popup('Duplicate Finder Ended', msg)
# ====____====____==== Pseudo-MAIN program ====____====____==== #
# This is our main-alike piece of code #
# + Starts up the GUI #
# + Gets values from GUI #
# + Runs DeDupe_folder based on GUI inputs #
# ------------------------------------------------------------- #
if __name__ == '__main__':
source_folder = None
source_folder = sg.PopupGetFolder('Duplicate Finder - Count number of duplicate files', 'Enter path to folder you wish to find duplicates in')
if source_folder is not None:
FindDuplicatesFilesInFolder(source_folder)
else:
sg.PopupCancel('Cancelling', '*** Cancelling ***')
exit(0)

View file

@ -0,0 +1,75 @@
import PySimpleGUI as sg
import subprocess
from shutil import copyfile
import shutil
import os
def Launcher():
sg.ChangeLookAndFeel('LightGreen')
layout = [[sg.T('PyInstaller EXE Creator', font='Any 15')],
[sg.T('Source Python File'), sg.In(key='_sourcefile_', size=(45,1)), sg.FileBrowse(file_types=(("Python Files", "*.py"),))],
[sg.T('Icon File'), sg.In(key='_iconfile_', size=(45,1)), sg.FileBrowse(file_types=(("Icon Files", "*.ico"),))],
[sg.Frame('Output', font='Any 15',layout= [[sg.Output(size=(65, 15), font='Courier 10')]])],
[sg.ReadFormButton('Make EXE',bind_return_key=True),
sg.SimpleButton('Quit', button_color=('white','firebrick3')),]]
window = sg.Window('PySimpleGUI EXE Maker',
auto_size_text=False,
auto_size_buttons=False,
default_element_size=(20,1,),
text_justification='right')
window.Layout(layout)
# ---===--- Loop taking in user input --- #
while True:
(button, values) = window.Read()
if button is 'Quit' or button is None:
break # exit button clicked
source_file = values['_sourcefile_']
icon_file = values['_iconfile_']
icon_option = '-i "{}"'.format(icon_file) if icon_file else ''
source_path, source_filename = os.path.split(source_file)
workpath_option = '--workpath "{}"'.format(source_path)
dispath_option = '--distpath "{}"'.format(source_path)
specpath_option = '--specpath "{}"'.format(source_path)
folder_to_remove = os.path.join(source_path,source_filename[:-3])
file_to_remove = os.path.join(source_path, source_filename[:-3]+'.spec')
command_line = 'pyinstaller -wF "{}" {} {} {} {}'.format(source_file, icon_option, workpath_option, dispath_option, specpath_option)
if button is 'Make EXE':
try:
print(command_line)
print('Making EXE... this will take a while.. the program has NOT locked up...')
window.Refresh()
# print('Running command {}'.format(command_line))
runCommand(command_line)
shutil.rmtree(folder_to_remove)
os.remove(file_to_remove)
print('**** DONE ****')
except:
sg.PopupError('Something went wrong')
def runCommand(cmd, timeout=None):
""" run shell command
@param cmd: command to execute
@param timeout: timeout for command execution
@return: (return code from command, command output)
"""
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = ''
out, err = p.communicate()
p.wait(timeout)
return (out, err)
if __name__ == '__main__':
Launcher()

View file

@ -0,0 +1,66 @@
#!/usr/bin/env python
import sys
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
def Everything():
sg.ChangeLookAndFeel('TanBlue')
column1 = [
[sg.Text('Column 1', background_color=sg.DEFAULT_BACKGROUND_COLOR, justification='center', size=(10, 1))],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1', key='spin1')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2', key='spin2')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3', key='spin3')]]
layout = [
[sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))],
[sg.Text('Here is some text.... and a place to enter text')],
[sg.InputText('This is my text', key='in1', do_not_clear=True)],
[sg.Checkbox('Checkbox', key='cb1'), sg.Checkbox('My second checkbox!', key='cb2', default=True)],
[sg.Radio('My first Radio! ', "RADIO1", key='rad1', default=True),
sg.Radio('My second Radio!', "RADIO1", key='rad2')],
[sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3),
key='multi1', do_not_clear=True),
sg.Multiline(default_text='A second multi-line', size=(35, 3), key='multi2', do_not_clear=True)],
[sg.InputCombo(('Combobox 1', 'Combobox 2'), key='combo', size=(20, 1)),
sg.Slider(range=(1, 100), orientation='h', size=(34, 20), key='slide1', default_value=85)],
[sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'), key='optionmenu')],
[sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3), key='listbox'),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25, key='slide2', ),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75, key='slide3', ),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10, key='slide4'),
sg.Column(column1, background_color='gray34')],
[sg.Text('_' * 80)],
[sg.Text('Choose A Folder', size=(35, 1))],
[sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'),
sg.InputText('Default Folder', key='folder', do_not_clear=True), sg.FolderBrowse()],
[sg.ReadButton('Exit'),
sg.Text(' ' * 40), sg.ReadButton('SaveSettings'), sg.ReadButton('LoadSettings')]
]
window = sg.Window('Form Fill Demonstration', default_element_size=(40, 1), grab_anywhere=False)
# button, values = window.LayoutAndRead(layout, non_blocking=True)
window.Layout(layout)
while True:
event, values = window.Read()
if event is 'SaveSettings':
filename = sg.PopupGetFile('Save Settings', save_as=True, no_window=True)
window.SaveToDisk(filename)
# save(values)
elif event is 'LoadSettings':
filename = sg.PopupGetFile('Load Settings', no_window=True)
window.LoadFromDisk(filename)
# load(form)
elif event in ['Exit', None]:
break
# window.CloseNonBlocking()
if __name__ == '__main__':
Everything()

View file

@ -0,0 +1,77 @@
import PySimpleGUI as sg
import os
import io
from PIL import Image, ImageDraw, ImageTk, ImageFont
import base64
import subprocess
import sys
button_names = ('close', 'cookbook', 'cpu', 'github', 'pysimplegui', 'run', 'storage', 'timer')
ROOT_PATH = './' if sys.platform == 'linux' else 'C:\\Python\\PycharmProjects\\GooeyGUI\\'
house64='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'
cpu64='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'
timer64='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'
close64 = 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAEQ0lEQVR42r2XW2wbRRSG/1177TgkdkyoS4shaaWogVIKRAXUVn4BgRBEIRBSkSK1lAakPhTxABJSK6BEtAoXCUHEWwWi4oEXUAVvRUASSBuJliAh5QJp6hrspoGQi69r73LO7Npu6kvsBGek0ezOrvf79szsmbG0D2iwAN8DaMQaFA0YHQFaLwCX6TQuHQAuNtjR2PawD05LZeFzKeC7b/txPoLxU8Aj1BVkAf1wqw/uejeU9RsASaqYQGp+Dv8EAvjgdD9OAg9S14gQOPKED1XNWyv7+lT0VArxiVH0fCUEOqjr3JoKcImN/pYW2EOnQyUJTESBJkdpgGkV8Cj/owDDdx59A8Mf92FT+GpR+KSlBrt6ehE6+hL0pLp6AYbvfusE5FontFgUZ989UVAiDU+X0OsvQ0/EVy4g4MeOQ3a6Mn38wKHet3MkrofzZJMsFlzpeRVaeLF8ASPsb8Javy7nDXRVxdA7x7FpIZQXnrlP0yDJMoKvHVpZBKq23Qv3M8/nzQt6PIah93qhRxaLwvPNhbLmgGP7Drg694mHlVqKwcsWEBItD8DVvleM6WrhRQXUwBSsnpthvclDY++BZLdnflS9YxecrZ2QFGVZePDIYcq5yWuGK47k39NIzlCdDkHxNuYXiJzrz/xIrr4BFpdbfAFyTS1CSi1uf7IDrqeeheyoLihxubsD2sI8UuEFaItUKfen5mahRcLZl7nft7xAvjIQs+GFP2cLCmjRCL5p3oDN6nzR56xIYDl4ORJlCwyqDnT7Z5aFL5G4w4vN8dnVCwymatA9daVkeCkSJQv8qDtxcDKYF86AwKEuSDYbvB+doq/DlnMPJ6uvmzfmSJQk0E9D+OLVcEG4f38bwgNnxLmz9Wl4+z6HZLXm3JuYHMfE7i0ri8Ck3Y3Hx4L0lvYl8Et7H0Xk7NJ7Xe1d8H74GX2/2YyZmv8XY3euo4SUXJkAFyvtEbdc+CsDn2r3Ifrrz3nHvW7Pftzy/kmxdhSCly2Qlmj66Xf88dB2qP6LRme+jauuo67rIDyvHMN4i1esmvlK6QIUTrEISbKxDnDlPkk2BK6VIDhXXaddP6Vk0H6A9wSUn0WKFn2lCgiYbDEmFVXJYjWOuU1LcHudgAASSLS0FnD4dV4TksYxNEOqsMDwgAAxELToSFZFfGaiVWzGNV6MWM4Uyc5OE8wQCr2AqwmxIuoJowX3k5CjZSd6vvxhqcBj921Fc2g8C2Mwzf5sax7zNZZjSdkcCg6/EEgacAYzlLZvRk1kW7rm39iELwZHsgLPATN311rqb7trG+65dT2FXTEg4o1NoDinZKOYQ8ICFo4ADwMJpEwBDrnKIU+YMqZQ0pAbC4QwODwCf0Rd/BQ4IATagM46oI+CeiNPPVS40EDF6M/pJ78Ap+n0PL8Cp7sGs9asgQSFDLxBmKJ6STKBVSbcZsa10gKcJHi/Hv0PWqbBbaFH/AEAAAAASUVORK5CYII='
def ExecuteCommandSubprocess(command, *args, wait=False):
# try:
if sys.platform == 'linux':
arg_string = ''
for arg in args:
arg_string += ' ' + str(arg)
sp = subprocess.Popen(['python3' + arg_string, ], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
else:
expanded_args = []
for a in args:
expanded_args += a
sp = subprocess.Popen([command, expanded_args], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if wait:
out, err = sp.communicate()
if out:
print(out.decode("utf-8"))
if err:
print(err.decode("utf-8"))
# except: pass
def get_image_bytes(image64):
image_file = io.BytesIO(base64.b64decode(image64))
img = Image.open(image_file)
bio = io.BytesIO()
img.save(bio, format='PNG')
imgbytes = bio.getvalue()
return imgbytes
def ShowMeTheButtons():
sg.SetOptions(auto_size_buttons=True, margins=(0,0), button_color=sg.COLOR_SYSTEM_DEFAULT)
toolbar_buttons = [[sg.Button('', image_data=get_image_bytes(close64),button_color=('white', 'black'), pad=(0,0), key='_close_'),
sg.Button('', image_data=get_image_bytes(timer64), button_color=('white', 'black'), pad=(0, 0), key='_timer_'),
sg.Button('', image_data=get_image_bytes(house64), button_color=('white', 'black'), pad=(0, 0), key='_house_'),
sg.Button('', image_data=get_image_bytes(cpu64), button_color=('white', 'black'), pad=(0,0), key='_cpu_'),]]
# layout = toolbar_buttons
layout = [[sg.Frame('Launcher', toolbar_buttons,title_color='white', background_color='black')]]
window = sg.Window('Toolbar', no_titlebar=True, grab_anywhere=True, background_color='black').Layout(layout)
# ---===--- Loop taking in user input --- #
while True:
button, value = window.Read()
print(button)
if button == '_close_' or button is None:
break # exit button clicked
elif button == '_timer_':
pass # add your call to launch a timer program
elif button == '_cpu_':
pass # add your call to launch a CPU measuring utility
if __name__ == '__main__':
ShowMeTheButtons()

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from tkinter import font
import tkinter
root = tkinter.Tk()
fonts = list(font.families())
fonts.sort()
root.destroy()
sg.ChangeLookAndFeel('Black')
layout = [[ sg.Text('My Text Element',
size=(20,1),
auto_size_text=False,
click_submits=True,
relief=sg.RELIEF_GROOVE,
font = 'Courier` 25',
text_color='#FF0000',
background_color='white',
justification='center',
pad=(5,3),
key='_text_',
tooltip='This is a text element',
) ],
[sg.Listbox(fonts, size=(30,20), change_submits=True, key='_list_')],
[sg.Input(key='_in_')],
[ sg.Button('Read', bind_return_key=True), sg.Exit()]]
window = sg.Window('My new window',
# grab_anywhere=True,
# force_toplevel=True,
).Layout(layout)
while True: # Event Loop
event, values = window.Read()
if event is None or event == 'Exit':
break
text_elem = window.FindElement('_text_')
print(event, values)
if values['_in_'] != '':
text_elem.Update(font=values['_in_'])
else:
text_elem.Update(font=(values['_list_'][0], 25))

View file

@ -0,0 +1,30 @@
# Testing async form, see if can have a slider
# that adjusts the size of text displayed
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
fontSize = 12
layout = [[sg.Spin([sz for sz in range(6, 172)], font=('Helvetica 20'), initial_value=fontSize, change_submits=True, key='spin'),
sg.Slider(range=(6,172), orientation='h', size=(10,20), change_submits=True, key='slider', font=('Helvetica 20')), sg.Text("Aa", size=(2, 1), font="Helvetica " + str(fontSize), key='text')]]
sz = fontSize
window = sg.Window("Font size selector", grab_anywhere=False)
window.Layout(layout)
while True:
event, values= window.Read()
if event is None or event == 'Quit':
break
sz_spin = int(values['spin'])
sz_slider = int(values['slider'])
sz = sz_spin if sz_spin != fontSize else sz_slider
if sz != fontSize:
fontSize = sz
font = "Helvetica " + str(fontSize)
window.FindElement('text').Update(font=font)
window.FindElement('slider').Update(sz)
window.FindElement('spin').Update(sz)
print("Done.")

View file

@ -0,0 +1,34 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[sg.Text('This is my sample text',size=(20,1), key='_text_') ],
[sg.CB('Bold', key='_bold_', change_submits=True),
sg.CB('Italics', key='_italics_', change_submits=True),
sg.CB('Underline', key='_underline_', change_submits=True)],
[sg.Slider((6,50), default_value=12, size=(14,20), orientation='h', key='_slider_', change_submits=True),
sg.Text('Font size')],
[sg.Text('Font string = '), sg.Text('', size=(25,1), key='_fontstring_')],
[ sg.Button('Exit')]]
window = sg.Window('Font string builder').Layout(layout)
text_elem = window.FindElement('_text_')
while True: # Event Loop
event, values = window.Read()
if event in (None, 'Exit'):
break
font_string = 'Helvitica '
font_string += str(values['_slider_'])
if values['_bold_']:
font_string += ' bold'
if values['_italics_']:
font_string += ' italic'
if values['_underline_']:
font_string += ' underline'
text_elem.Update(font=font_string)
window.FindElement('_fontstring_').Update('"'+font_string+'"')
print(event, values)

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
def main():
# ------- Make a new Window ------- #
window = sg.Window('GoodColors', auto_size_text=True, default_element_size=(30,2))
window.AddRow(sg.Text('Having trouble picking good colors? Try one of the colors defined by PySimpleGUI'))
window.AddRow(sg.Text('Here come the good colors as defined by PySimpleGUI'))
#===== Show some nice BLUE colors with yellow text ===== ===== ===== ===== ===== ===== =====#
text_color = sg.YELLOWS[0]
buttons = (sg.Button('BLUES[{}]\n{}'.format(j, c), button_color=(text_color, c), size=(10,2)) for j, c in enumerate(sg.BLUES))
window.AddRow(sg.T('Button Colors Using PySimpleGUI.BLUES'))
window.AddRow(*buttons)
window.AddRow(sg.Text('_' * 100, size=(65, 1)))
#===== Show some nice PURPLE colors with yellow text ===== ===== ===== ===== ===== ===== =====#
buttons = (sg.Button('PURPLES[{}]\n{}'.format(j, c), button_color=(text_color, c), size=(10,2)) for j, c in enumerate(sg.PURPLES))
window.AddRow(sg.T('Button Colors Using PySimpleGUI.PURPLES'))
window.AddRow(*buttons)
window.AddRow(sg.Text('_' * 100, size=(65, 1)))
#===== Show some nice GREEN colors with yellow text ===== ===== ===== ===== ===== ===== =====#
buttons = (sg.Button('GREENS[{}]\n{}'.format(j, c), button_color=(text_color, c), size=(10,2)) for j, c in enumerate(sg.GREENS))
window.AddRow(sg.T('Button Colors Using PySimpleGUI.GREENS'))
window.AddRow(*buttons)
window.AddRow(sg.Text('_' * 100, size=(65, 1)))
#===== Show some nice TAN colors with yellow text ===== ===== ===== ===== ===== ===== =====#
text_color = sg.GREENS[0] # let's use GREEN text on the tan
buttons = (sg.Button('TANS[{}]\n{}'.format(j, c), button_color=(text_color, c), size=(10,2)) for j, c in enumerate(sg.TANS))
window.AddRow(sg.T('Button Colors Using PySimpleGUI.TANS'))
window.AddRow(*buttons)
window.AddRow(sg.Text('_' * 100, size=(65, 1)))
#===== Show some nice YELLOWS colors with black text ===== ===== ===== ===== ===== ===== =====#
text_color = 'black' # let's use black text on the tan
buttons = (sg.Button('YELLOWS[{}]\n{}'.format(j, c), button_color=(text_color, c), size=(10,2)) for j, c in enumerate(sg.YELLOWS))
window.AddRow(sg.T('Button Colors Using PySimpleGUI.YELLOWS'))
window.AddRow(*buttons)
window.AddRow(sg.Text('_' * 100, size=(65, 1)))
#===== Add a click me button for fun and SHOW the window ===== ===== ===== ===== ===== ===== =====#
window.AddRow(sg.Button('Click ME!'))
event, values = window.Read() # show it!
if __name__ == '__main__':
main()

View file

@ -0,0 +1,53 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from gtts import gTTS
from pygame import mixer
import time
import os
'''
Simple demonstration of using Google Text to Speech
Get a multi-line string
Convert to speech
Play back the speech
Note that there are 2 temp files created. The program tries to delete them but will fail on one of them
'''
layout = [[sg.Text('What would you like me to say?')],
[sg.Multiline(size=(60,10), enter_submits=True)],
[sg.Button('Speak', bind_return_key=True), sg.Exit()]]
window = sg.Window('Google Text to Speech').Layout(layout)
i = 0
mixer.init()
while True:
event, values = window.Read()
if event is None or event == 'Exit':
break
# Get the text and convert to mp3 file
tts = gTTS(text=values[0], lang='en',slow=False)
tts.save('speech{}.mp3'.format(i%2))
# playback the speech
mixer.music.load('speech{}.mp3'.format(i%2))
mixer.music.play()
# wait for playback to end
while mixer.music.get_busy():
time.sleep(.1)
mixer.stop()
i += 1
# try to remove the temp files. You'll likely be left with 1 to clean up
try:
os.remove('speech0.mp3')
except:
pass
try:
os.remove('speech1.mp3')
except:
pass

View file

@ -0,0 +1,33 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[sg.Graph(canvas_size=(400, 400), graph_bottom_left=(0,0), graph_top_right=(400, 400), background_color='red', key='graph')],
[sg.T('Change circle color to:'), sg.ReadButton('Red'), sg.ReadButton('Blue'), sg.ReadButton('Move')]]
window = sg.Window('Graph test').Layout(layout).Finalize()
graph = window.FindElement('graph')
circle =graph .DrawCircle((75,75), 25, fill_color='black',line_color='white')
point = graph.DrawPoint((75,75), 10, color='green')
oval = graph.DrawOval((25,300), (100,280), fill_color='purple', line_color='purple' )
rectangle = graph.DrawRectangle((25,300), (100,280), line_color='purple' )
line = graph.DrawLine((0,0), (100,100))
arc = graph.DrawArc((0,0), (400,400), 160, 10, style='arc' ,arc_color='blue')
while True:
event, values = window.Read()
if event is None:
break
if event is 'Blue':
graph.TKCanvas.itemconfig(circle, fill = "Blue")
elif event is 'Red':
graph.TKCanvas.itemconfig(circle, fill = "Red")
elif event is 'Move':
graph.MoveFigure(point, 10,10)
graph.MoveFigure(circle, 10,10)
graph.MoveFigure(oval, 10,10)
graph.MoveFigure(rectangle, 10,10)
graph.MoveFigure(arc, 10,10)

View file

@ -0,0 +1,67 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import ping
from threading import Thread
import time
STEP_SIZE=1
SAMPLES = 1000
CANVAS_SIZE = (1000,500)
# globale used to communicate with thread.. yea yea... it's working fine
g_exit = False
g_response_time = None
def ping_thread(args):
global g_exit, g_response_time
while not g_exit:
g_response_time = ping.quiet_ping('google.com', timeout=1000)
def main():
global g_exit, g_response_time
# start ping measurement thread
thread = Thread(target=ping_thread, args=(None,))
thread.start()
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0,0))
layout = [ [sg.T('Ping times to Google.com', font='Any 12'), sg.Quit(pad=((100,0), 0), button_color=('white', 'black'))],
[sg.Graph(CANVAS_SIZE, (0,0), (SAMPLES,500),background_color='black', key='graph')],]
window = sg.Window('Canvas test', grab_anywhere=True, background_color='black', no_titlebar=False, use_default_focus=False).Layout(layout)
graph = window.FindElement('graph')
prev_response_time = None
i=0
prev_x, prev_y = 0, 0
while True:
event, values = window.Read(timeout=200)
if event == 'Quit' or event is None:
break
if g_response_time is None or prev_response_time == g_response_time:
continue
new_x, new_y = i, g_response_time[0]
prev_response_time = g_response_time
if i >= SAMPLES:
graph.Move(-STEP_SIZE,0)
prev_x = prev_x - STEP_SIZE
graph.DrawLine((prev_x, prev_y), (new_x, new_y), color='white')
# window.FindElement('graph').DrawPoint((new_x, new_y), color='red')
prev_x, prev_y = new_x, new_y
i += STEP_SIZE if i < SAMPLES else 0
# tell thread we're done. wait for thread to exit
g_exit = True
thread.join()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,40 @@
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import math
layout = [[sg.T('Example of Using Math with a Graph', justification='center',
size=(50,1), relief=sg.RELIEF_SUNKEN)],
[sg.Graph(canvas_size=(400, 400),
graph_bottom_left=(-105,-105),
graph_top_right=(105,105),
background_color='white',
key='graph')],]
window = sg.Window('Graph of Sine Function', grab_anywhere=True).Layout(layout).Finalize()
graph = window.FindElement('graph')
# Draw axis
graph.DrawLine((-100,0), (100,0))
graph.DrawLine((0,-100), (0,100))
for x in range(-100, 101, 20):
graph.DrawLine((x,-3), (x,3))
if x != 0:
graph.DrawText( x, (x,-10), color='green')
for y in range(-100, 101, 20):
graph.DrawLine((-3,y), (3,y))
if y != 0:
graph.DrawText( y, (-10,y), color='blue')
# Draw Graph
for x in range(-100,100):
y = math.sin(x/30)*50
graph.DrawCircle((x,y), 1, line_color='red', fill_color='red')
event, values = window.Read()

View file

@ -0,0 +1,70 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import random
import sys
STEP_SIZE=1
SAMPLES = 300
SAMPLE_MAX = 300
CANVAS_SIZE = (300,300)
def main():
global g_exit, g_response_time
layout = [[sg.T('Enter width, height of graph')],
[sg.In(size=(6, 1)), sg.In(size=(6, 1))],
[sg.Ok(), sg.Cancel()]]
window = sg.Window('Enter graph size').Layout(layout)
b,v = window.Read()
if b is None or b == 'Cancel':
sys.exit(69)
w, h = int(v[0]), int(v[1])
CANVAS_SIZE = (w,h)
# start ping measurement thread
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0,0))
layout = [ [sg.Button('Quit', button_color=('white','black'))],
[sg.Graph(CANVAS_SIZE, (0,0), (SAMPLES,SAMPLE_MAX),background_color='black', key='graph')],]
window = sg.Window('Canvas test', grab_anywhere=True, background_color='black', no_titlebar=False, use_default_focus=False).Layout(layout).Finalize()
graph = window.FindElement('graph')
prev_response_time = None
i=0
prev_x, prev_y = 0, 0
graph_value = 250
while True:
# time.sleep(.2)
event, values = window.Read(timeout=0)
if event == 'Quit' or event is None:
break
graph_offset = random.randint(-10, 10)
graph_value = graph_value + graph_offset
if graph_value > SAMPLE_MAX:
graph_value = SAMPLE_MAX
if graph_value < 0:
graph_value = 0
new_x, new_y = i, graph_value
prev_value = graph_value
if i >= SAMPLES:
graph.Move(-STEP_SIZE,0)
prev_x = prev_x - STEP_SIZE
graph.DrawLine((prev_x, prev_y), (new_x, new_y), color='white')
# window.FindElement('graph').DrawPoint((new_x, new_y), color='red')
prev_x, prev_y = new_x, new_y
i += STEP_SIZE if i < SAMPLES else 0
if __name__ == '__main__':
main()

View file

@ -0,0 +1,66 @@
import ping
from threading import Thread
import time
import PySimpleGUI as sg
STEP_SIZE=1
SAMPLES = 6000
CANVAS_SIZE = (6000,500)
# globale used to communicate with thread.. yea yea... it's working fine
g_exit = False
g_response_time = None
def ping_thread(args):
global g_exit, g_response_time
while not g_exit:
g_response_time = ping.quiet_ping('google.com', timeout=1000)
def main():
global g_exit, g_response_time
# start ping measurement thread
thread = Thread(target=ping_thread, args=(None,))
thread.start()
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0,0))
layout = [ [sg.T('Ping times to Google.com', font='Any 12'), sg.Quit(pad=((100,0), 0), button_color=('white', 'black'))],
[sg.Graph(CANVAS_SIZE, (0,0), (SAMPLES,500),background_color='black', key='graph')],]
form = sg.FlexForm('Canvas test', grab_anywhere=True, background_color='black', no_titlebar=False, use_default_focus=False)
form.Layout(layout)
form.Finalize()
graph = form.FindElement('graph')
prev_response_time = None
i=0
prev_x, prev_y = 0, 0
while True:
time.sleep(.2)
button, values = form.ReadNonBlocking()
if button == 'Quit' or values is None:
break
if g_response_time is None or prev_response_time == g_response_time:
continue
new_x, new_y = i, g_response_time[0]
prev_response_time = g_response_time
if i >= SAMPLES:
graph.Move(-STEP_SIZE,0)
prev_x = prev_x - STEP_SIZE
graph.DrawLine((prev_x, prev_y), (new_x, new_y), color='white')
# form.FindElement('graph').DrawPoint((new_x, new_y), color='red')
prev_x, prev_y = new_x, new_y
i += STEP_SIZE if i < SAMPLES else 0
# tell thread we're done. wait for thread to exit
g_exit = True
thread.join()
if __name__ == '__main__':
main()
exit(69)

View file

@ -0,0 +1,85 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import subprocess
# Test this command in a dos window if you are having trouble.
HOW_DO_I_COMMAND = 'python -m howdoi.howdoi'
# if you want an icon on your taskbar for this gui, then change this line of code to point to the ICO file
DEFAULT_ICON = 'E:\\TheRealMyDocs\\Icons\\QuestionMark.ico'
def HowDoI():
'''
Make and show a window (PySimpleGUI form) that takes user input and sends to the HowDoI web oracle
Excellent example of 2 GUI concepts
1. Output Element that will show text in a scrolled window
2. Non-Window-Closing Buttons - These buttons will cause the form to return with the form's values, but doesn't close the form
:return: never returns
'''
# ------- Make a new Window ------- #
sg.ChangeLookAndFeel('GreenTan') # give our form a spiffy set of colors
layout = [
[sg.Text('Ask and your answer will appear here....', size=(40, 1))],
[sg.Output(size=(127, 30), font=('Helvetica 10'))],
[ sg.Spin(values=(1, 2, 3, 4), initial_value=1, size=(2, 1), key='Num Answers', font='Helvetica 15'),
sg.Text('Num Answers',font='Helvetica 15'), sg.Checkbox('Display Full Text', key='full text', font='Helvetica 15'),
sg.T('Command History', font='Helvetica 15'), sg.T('', size=(40,3), text_color=sg.BLUES[0], key='history')],
[sg.Multiline(size=(85, 5), enter_submits=True, key='query', do_not_clear=False),
sg.ReadButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0]), bind_return_key=True),
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]
]
window = sg.Window('How Do I ??', default_element_size=(30, 2), icon=DEFAULT_ICON, font=('Helvetica',' 13'), default_button_element_size=(8,2), return_keyboard_events=True, no_titlebar=True, grab_anywhere=True)
window.Layout(layout)
# ---===--- Loop taking in user input and using it to query HowDoI --- #
command_history = []
history_offset = 0
while True:
event, values = window.Read()
if event == 'SEND':
query = values['query'].rstrip()
# print(query)
QueryHowDoI(query, values['Num Answers'], values['full text']) # send the string to HowDoI
command_history.append(query)
history_offset = len(command_history)-1
window.FindElement('query').Update('') # manually clear input because keyboard events blocks clear
window.FindElement('history').Update('\n'.join(command_history[-3:]))
elif event == None or event == 'EXIT': # if exit button or closed using X
break
elif 'Up' in event and len(command_history): # scroll back in history
command = command_history[history_offset]
history_offset -= 1 * (history_offset > 0) # decrement is not zero
window.FindElement('query').Update(command)
elif 'Down' in event and len(command_history): # scroll forward in history
history_offset += 1 * (history_offset < len(command_history)-1) # increment up to end of list
command = command_history[history_offset]
window.FindElement('query').Update(command)
elif 'Escape' in event: # clear currently line
window.FindElement('query').Update('')
def QueryHowDoI(Query, num_answers, full_text):
'''
Kicks off a subprocess to send the 'Query' to HowDoI
Prints the result, which in this program will route to a gooeyGUI window
:param Query: text english question to ask the HowDoI web engine
:return: nothing
'''
howdoi_command = HOW_DO_I_COMMAND
full_text_option = ' -a' if full_text else ''
t = subprocess.Popen(howdoi_command + ' \"'+ Query + '\" -n ' + str(num_answers)+full_text_option, stdout=subprocess.PIPE)
(output, err) = t.communicate()
print('{:^88}'.format(Query.rstrip()))
print('_'*60)
print(output.decode("utf-8") )
exit_code = t.wait()
if __name__ == '__main__':
HowDoI()

View file

@ -0,0 +1,120 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import os
from PIL import Image, ImageTk
import io
"""
Simple Image Browser based on PySimpleGUI
--------------------------------------------
There are some improvements compared to the PNG browser of the repository:
1. Paging is cyclic, i.e. automatically wraps around if file index is outside
2. Supports all file types that are valid PIL images
3. Limits the maximum form size to the physical screen
4. When selecting an image from the listbox, subsequent paging uses its index
5. Paging performance improved significantly because of using PIL
Dependecies
------------
Python v3
PIL
"""
# Get the folder containin:g the images from the user
folder = sg.PopupGetFolder('Image folder to open', default_path='')
if not folder:
sg.PopupCancel('Cancelling')
raise SystemExit()
# PIL supported image types
img_types = (".png", ".jpg", "jpeg", ".tiff", ".bmp")
# get list of files in folder
flist0 = os.listdir(folder)
# create sub list of image files (no sub folders, no wrong file types)
fnames = [f for f in flist0 if os.path.isfile(os.path.join(folder,f)) and f.lower().endswith(img_types)]
num_files = len(fnames) # number of iamges found
if num_files == 0:
sg.Popup('No files in folder')
raise SystemExit()
del flist0 # no longer needed
#------------------------------------------------------------------------------
# use PIL to read data of one image
#------------------------------------------------------------------------------
def get_img_data(f, maxsize = (1200, 850), first = False):
"""Generate image data using PIL
"""
img = Image.open(f)
img.thumbnail(maxsize)
if first: # tkinter is inactive the first time
bio = io.BytesIO()
img.save(bio, format = "PNG")
del img
return bio.getvalue()
return ImageTk.PhotoImage(img)
#------------------------------------------------------------------------------
# create the form that also returns keyboard events
window = sg.Window('Image Browser', return_keyboard_events=True,
location=(0, 0), use_default_focus=False)
# make these 2 elements outside the layout as we want to "update" them later
# initialize to the first file in the list
filename = os.path.join(folder, fnames[0]) # name of first file in list
image_elem = sg.Image(data = get_img_data(filename, first = True))
filename_display_elem = sg.Text(filename, size=(80, 3))
file_num_display_elem = sg.Text('File 1 of {}'.format(num_files), size=(15,1))
# define layout, show and read the form
col = [[filename_display_elem],
[image_elem]]
col_files = [[sg.Listbox(values = fnames, change_submits=True, size=(60, 30), key='listbox')],
[sg.ReadButton('Next', size=(8,2)), sg.ReadButton('Prev',
size=(8,2)), file_num_display_elem]]
layout = [[sg.Column(col_files), sg.Column(col)]]
window.Layout(layout) # Shows form on screen
# loop reading the user input and displaying image, filename
i=0
while True:
# read the form
event, values = window.Read()
print(event, values)
# perform button and keyboard operations
if event is None:
break
elif event in ('Next', 'MouseWheel:Down', 'Down:40', 'Next:34'):
i += 1
if i >= num_files:
i -= num_files
filename = os.path.join(folder, fnames[i])
elif event in ('Prev', 'MouseWheel:Up', 'Up:38', 'Prior:33'):
i -= 1
if i < 0:
i = num_files + i
filename = os.path.join(folder, fnames[i])
elif event == 'listbox': # something from the listbox
f = values["listbox"][0] # selected filename
filename = os.path.join(folder, f) # read this file
i = fnames.index(f) # update running index
else:
filename = os.path.join(folder, fnames[i])
# update window with new image
image_elem.Update(data=get_img_data(filename))
# update window with filename
filename_display_elem.Update(filename)
# update page display
file_num_display_elem.Update('File {} of {}'.format(i+1, num_files))

View file

@ -0,0 +1,29 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
# 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"
layout = [[sg.Text("Press a key or scroll mouse")],
[sg.Text("", size=(18,1), key='text')],
[sg.Button("OK", key='OK')]]
window = sg.Window("Keyboard Test", return_keyboard_events=True, use_default_focus=False).Layout(layout)
# ---===--- Loop taking in user input --- #
while True:
event, values = window.Read()
text_elem = window.FindElement('text')
if event in ("OK", None):
print(event, "exiting")
break
if len(event) == 1:
text_elem.Update(value='%s - %s' % (event, ord(event)))
if event is not None:
text_elem.Update(event)

View file

@ -0,0 +1,25 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[sg.Text("Hold down a key")],
[sg.Button("OK")]]
window = sg.Window("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False).Layout(layout)
while True:
event, values = window.Read(timeout=0)
if event == "OK":
print(event, values, "exiting")
break
if event is not sg.TIMEOUT_KEY:
if len(event) == 1:
print('%s - %s' % (event, ord(event)))
else:
print(event)
elif event is None:
break

View file

@ -0,0 +1,44 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
# Demonstrates a number of PySimpleGUI features including:
# Default element size
# auto_size_buttons
# Button
# Dictionary return values
# Update of elements in form (Text, Input)
# do_not_clear of Input elements
layout = [[sg.Text('Enter Your Passcode')],
[sg.Input(size=(10, 1), do_not_clear=True, key='input')],
[sg.Button('1'), sg.Button('2'), sg.Button('3')],
[sg.Button('4'), sg.Button('5'), sg.Button('6')],
[sg.Button('7'), sg.Button('8'), sg.Button('9')],
[sg.Button('Submit'), sg.Button('0'), sg.Button('Clear')],
[sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')],
]
window = 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
keys_entered = ''
while True:
event, values = window.Read() # read the form
if event is None: # if the X button clicked, just exit
break
if event == 'Clear': # clear keys if clear button
keys_entered = ''
elif event in '1234567890':
keys_entered = values['input'] # get what's been entered so far
keys_entered += event # add the new digit
elif event == 'Submit':
keys_entered = values['input']
window.FindElement('out').Update(keys_entered) # output the final string
window.FindElement('input').Update(keys_entered) # change the form to reflect current key string

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,51 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import time
import random
"""
Demo program showing how to create your own "LED Indicators"
The LEDIndicator function acts like a new Element that is directly placed in a window's layout
After the Window is created, use the SetLED function to access the LED and set the color
"""
def LEDIndicator(key=None, radius=30):
return sg.Graph(canvas_size=(radius, radius),
graph_bottom_left=(-radius, -radius),
graph_top_right=(radius, radius),
pad=(0, 0), key=key)
def SetLED(window, key, color):
graph = window.FindElement(key)
graph.Erase()
graph.DrawCircle((0, 0), 12, fill_color=color, line_color=color)
layout = [[sg.Text('My LED Status Indicators', size=(20,1))],
[sg.Text('CPU Use'), LEDIndicator('_cpu_')],
[sg.Text('RAM'), LEDIndicator('_ram_')],
[sg.Text('Temperature'), LEDIndicator('_temp_')],
[sg.Text('Server 1'), LEDIndicator('_server1_')],
[sg.Button('Exit')]]
window = sg.Window('My new window', default_element_size=(12, 1), auto_size_text=False).Layout(layout).Finalize()
i = 0
while True: # Event Loop
event, value = window.Read(timeout=400)
if event == 'Exit' or event is None:
break
if value is None:
break
i += 1
SetLED(window, '_cpu_', 'green' if random.randint(1, 10) > 5 else 'red')
SetLED(window, '_ram_', 'green' if random.randint(1, 10) > 5 else 'red')
SetLED(window, '_temp_', 'green' if random.randint(1, 10) > 5 else 'red')
SetLED(window, '_server1_', 'green' if random.randint(1, 10) > 5 else 'red')

View file

@ -0,0 +1,227 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import os
import mido
import time
import sys
PLAYER_COMMAND_NONE = 0
PLAYER_COMMAND_EXIT = 1
PLAYER_COMMAND_PAUSE = 2
PLAYER_COMMAND_NEXT = 3
PLAYER_COMMAND_RESTART_SONG = 4
# ---------------------------------------------------------------------- #
# PlayerGUI CLASS #
# ---------------------------------------------------------------------- #
class PlayerGUI():
'''
Class implementing GUI for both initial screen but the player itself
'''
def __init__(self):
self.Window = None
self.TextElem = None
self.PortList = mido.get_output_names() # use to get the list of midi ports
self.PortList = self.PortList[::-1] # reverse the list so the last one is first
# ---------------------------------------------------------------------- #
# PlayerChooseSongGUI #
# Show a GUI get to the file to playback #
# ---------------------------------------------------------------------- #
def PlayerChooseSongGUI(self):
# ---------------------- DEFINION OF CHOOSE WHAT TO PLAY GUI ----------------------------
layout = [[sg.Text('MIDI File Player', font=("Helvetica", 15), size=(20, 1), text_color='green')],
[sg.Text('File Selection', font=("Helvetica", 15), size=(20, 1))],
[sg.Text('Single File Playback', justification='right'), sg.InputText(size=(65, 1), key='midifile'), sg.FileBrowse(size=(10, 1), file_types=(("MIDI files", "*.mid"),))],
[sg.Text('Or Batch Play From This Folder', auto_size_text=False, justification='right'), sg.InputText(size=(65, 1), key='folder'), sg.FolderBrowse(size=(10, 1))],
[sg.Text('_' * 250, auto_size_text=False, size=(100, 1))],
[sg.Text('Choose MIDI Output Device', size=(22, 1)),
sg.Listbox(values=self.PortList, size=(30, len(self.PortList) + 1), key='device')],
[sg.Text('_' * 250, auto_size_text=False, size=(100, 1))],
[sg.SimpleButton('PLAY', size=(12, 2), button_color=('red', 'white'), font=("Helvetica", 15), bind_return_key=True), sg.Text(' ' * 2, size=(4, 1)), sg.Cancel(size=(8, 2), font=("Helvetica", 15))]]
window = sg.Window('MIDI File Player', auto_size_text=False, default_element_size=(30, 1), font=("Helvetica", 12)).Layout(layout)
self.Window = window
return window.Read()
def PlayerPlaybackGUIStart(self, NumFiles=1):
# ------- Make a new FlexForm ------- #
image_pause = './ButtonGraphics/Pause.png'
image_restart = './ButtonGraphics/Restart.png'
image_next = './ButtonGraphics/Next.png'
image_exit = './ButtonGraphics/Exit.png'
self.TextElem = sg.T('Song loading....', size=(70, 5 + NumFiles), font=("Helvetica", 14), auto_size_text=False)
self.SliderElem = sg.Slider(range=(1, 100), size=(50, 8), orientation='h', text_color='#f0f0f0')
layout = [
[sg.T('MIDI File Player', size=(30, 1), font=("Helvetica", 25))],
[self.TextElem],
[self.SliderElem],
[sg.Button('', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_pause, image_size=(50,50), image_subsample=2, border_width=0, key='PAUSE'), sg.T(' '),
sg.Button('', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_next, image_size=(50,50), image_subsample=2, border_width=0, key='NEXT'), sg.T(' '),
sg.Button('', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_restart, image_size=(50,50), image_subsample=2, border_width=0, key='Restart Song'), sg.T(' '),
sg.Button('', button_color=sg.TRANSPARENT_BUTTON,
image_filename=image_exit, image_size=(50,50), image_subsample=2, border_width=0,key='EXIT')]
]
window = sg.Window('MIDI File Player', default_element_size=(30, 1), font=("Helvetica", 25)).Layout(layout).Finalize()
self.Window = window
# ------------------------------------------------------------------------- #
# PlayerPlaybackGUIUpdate #
# Refresh the GUI for the main playback interface (must call periodically #
# ------------------------------------------------------------------------- #
def PlayerPlaybackGUIUpdate(self, DisplayString):
window = self.Window
if 'window' not in locals() or window is None: # if the widnow has been destoyed don't mess with it
return PLAYER_COMMAND_EXIT
self.TextElem.Update(DisplayString)
event, (values) = window.Read(timeout=0)
if event is None:
return PLAYER_COMMAND_EXIT
if event == 'PAUSE':
return PLAYER_COMMAND_PAUSE
elif event == 'EXIT':
return PLAYER_COMMAND_EXIT
elif event == 'NEXT':
return PLAYER_COMMAND_NEXT
elif event == 'Restart Song':
return PLAYER_COMMAND_RESTART_SONG
return PLAYER_COMMAND_NONE
# ---------------------------------------------------------------------- #
# MAIN - our main program... this is it #
# Runs the GUI to get the file / path to play #
# Decodes the MIDI-Video into a MID file #
# Plays the decoded MIDI file #
# ---------------------------------------------------------------------- #
def main():
def GetCurrentTime():
'''
Get the current system time in milliseconds
:return: milliseconds
'''
return int(round(time.time() * 1000))
pback = PlayerGUI()
button, values = pback.PlayerChooseSongGUI()
if button != 'PLAY':
sg.PopupCancel('Cancelled...\nAutoclose in 2 sec...', auto_close=True, auto_close_duration=2)
sys.exit(69)
if values['device']:
midi_port = values['device'][0]
else:
sg.PopupCancel('No devices found\nAutoclose in 2 sec...', auto_close=True, auto_close_duration=2)
batch_folder = values['folder']
midi_filename = values['midifile']
# ------ Build list of files to play --------------------------------------------------------- #
if batch_folder:
filelist = os.listdir(batch_folder)
filelist = [batch_folder+'/'+f for f in filelist if f.endswith(('.mid', '.MID'))]
filetitles = [os.path.basename(f) for f in filelist]
elif midi_filename: # an individual filename
filelist = [midi_filename,]
filetitles = [os.path.basename(midi_filename),]
else:
sg.PopupError('*** Error - No MIDI files specified ***')
sys.exit(666)
# ------ LOOP THROUGH MULTIPLE FILES --------------------------------------------------------- #
pback.PlayerPlaybackGUIStart(NumFiles=len(filelist) if len(filelist) <=10 else 10)
port = None
# Loop through the files in the filelist
for now_playing_number, current_midi_filename in enumerate(filelist):
display_string = 'Playing Local File...\n{} of {}\n{}'.format(now_playing_number+1, len(filelist), current_midi_filename)
midi_title = filetitles[now_playing_number]
# --------------------------------- REFRESH THE GUI ----------------------------------------- #
pback.PlayerPlaybackGUIUpdate(display_string)
# ---===--- Output Filename is .MID --- #
midi_filename = current_midi_filename
# --------------------------------- MIDI - STARTS HERE ----------------------------------------- #
if not port: # if the midi output port not opened yet, then open it
port = mido.open_output(midi_port if midi_port else None)
try:
mid = mido.MidiFile(filename=midi_filename)
except:
print('****** Exception trying to play MidiFile filename = {}***************'.format(midi_filename))
sg.PopupError('Exception trying to play MIDI file:', midi_filename, 'Skipping file')
continue
# Build list of data contained in MIDI File using only track 0
midi_length_in_seconds = mid.length
display_file_list = '>> ' + '\n'.join([f for i, f in enumerate(filetitles[now_playing_number:]) if i < 10])
paused = cancelled = next_file = False
######################### Loop through MIDI Messages ###########################
while(True):
start_playback_time = GetCurrentTime()
port.reset()
for midi_msg_number, msg in enumerate(mid.play()):
#################### GUI - read values ##################
if not midi_msg_number % 4: # update the GUI every 4 MIDI messages
t = (GetCurrentTime() - start_playback_time)//1000
display_midi_len = '{:02d}:{:02d}'.format(*divmod(int(midi_length_in_seconds),60))
display_string = 'Now Playing {} of {}\n{}\n {:02d}:{:02d} of {}\nPlaylist:'.\
format(now_playing_number+1, len(filelist), midi_title, *divmod(t, 60), display_midi_len)
# display list of next 10 files to be played.
pback.SliderElem.Update(t, range=(1,midi_length_in_seconds))
rc = pback.PlayerPlaybackGUIUpdate(display_string + '\n' + display_file_list)
else: # fake rest of code as if GUI did nothing
rc = PLAYER_COMMAND_NONE
if paused:
rc = PLAYER_COMMAND_NONE
while rc == PLAYER_COMMAND_NONE: # TIGHT-ASS loop waiting on a GUI command
rc = pback.PlayerPlaybackGUIUpdate(display_string)
time.sleep(.25)
####################################### MIDI send data ##################################
port.send(msg)
# ------- Execute GUI Commands after sending MIDI data ------- #
if rc == PLAYER_COMMAND_EXIT:
cancelled = True
break
elif rc == PLAYER_COMMAND_PAUSE:
paused = not paused
port.reset()
elif rc == PLAYER_COMMAND_NEXT:
next_file = True
break
elif rc == PLAYER_COMMAND_RESTART_SONG:
break
if cancelled or next_file:
break
#------- DONE playing the song ------- #
port.reset() # reset the midi port when done with the song
if cancelled:
break
# ---------------------------------------------------------------------- #
# LAUNCH POINT -- program starts and ends here #
# ---------------------------------------------------------------------- #
if __name__ == '__main__':
main()

View file

@ -0,0 +1,65 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
def MachineLearningGUI():
sg.SetOptions(text_justification='right')
flags = [[sg.Checkbox('Normalize', size=(12, 1), default=True), sg.Checkbox('Verbose', size=(20, 1))],
[sg.Checkbox('Cluster', size=(12, 1)), sg.Checkbox('Flush Output', size=(20, 1), default=True)],
[sg.Checkbox('Write Results', size=(12, 1)), sg.Checkbox('Keep Intermediate Data', size=(20, 1))],
[sg.Checkbox('Normalize', size=(12, 1), default=True), sg.Checkbox('Verbose', size=(20, 1))],
[sg.Checkbox('Cluster', size=(12, 1)), sg.Checkbox('Flush Output', size=(20, 1), default=True)],
[sg.Checkbox('Write Results', size=(12, 1)), sg.Checkbox('Keep Intermediate Data', size=(20, 1))],]
loss_functions = [[sg.Radio('Cross-Entropy', 'loss', size=(12, 1)), sg.Radio('Logistic', 'loss', default=True, size=(12, 1))],
[sg.Radio('Hinge', 'loss', size=(12, 1)), sg.Radio('Huber', 'loss', size=(12, 1))],
[sg.Radio('Kullerback', 'loss', size=(12, 1)), sg.Radio('MAE(L1)', 'loss', size=(12, 1))],
[sg.Radio('MSE(L2)', 'loss', size=(12, 1)), sg.Radio('MB(L0)', 'loss', size=(12, 1))],]
command_line_parms = [[sg.Text('Passes', size=(8, 1)), sg.Spin(values=[i for i in range(1, 1000)], initial_value=20, size=(6, 1)),
sg.Text('Steps', size=(8, 1), pad=((7,3))), sg.Spin(values=[i for i in range(1, 1000)], initial_value=20, size=(6, 1))],
[sg.Text('ooa', size=(8, 1)), sg.In(default_text='6', size=(8, 1)), sg.Text('nn', size=(8, 1)),
sg.In(default_text='10', size=(10, 1))],
[sg.Text('q', size=(8, 1)), sg.In(default_text='ff', size=(8, 1)), sg.Text('ngram', size=(8, 1)),
sg.In(default_text='5', size=(10, 1))],
[sg.Text('l', size=(8, 1)), sg.In(default_text='0.4', size=(8, 1)), sg.Text('Layers', size=(8, 1)),
sg.Drop(values=('BatchNorm', 'other'), auto_size_text=True)],]
layout = [[sg.Frame('Command Line Parameteres', command_line_parms, title_color='green', font='Any 12')],
[sg.Frame('Flags', flags, font='Any 12', title_color='blue')],
[sg.Frame('Loss Functions', loss_functions, font='Any 12', title_color='red')],
[sg.Submit(), sg.Cancel()]]
window = sg.Window('Machine Learning Front End', font=("Helvetica", 12)).Layout(layout)
button, values = window.Read()
sg.SetOptions(text_justification='left')
print(button, values)
def CustomMeter():
# layout the form
layout = [[sg.Text('A custom progress meter')],
[sg.ProgressBar(1000, orientation='h', size=(20,20), key='progress')],
[sg.Cancel()]]
# create the form`
window = sg.Window('Custom Progress Meter').Layout(layout)
progress_bar = window.FindElement('progress')
# loop that would normally do something useful
for i in range(1000):
# check to see if the cancel button was clicked and exit loop if clicked
event, values = window.Read(timeout=0, timeout_key='timeout')
if event == 'Cancel' or event == None:
break
# update bar with loop value +1 so that bar eventually reaches the maximum
progress_bar.UpdateBar(i+1)
# done with loop... need to destroy the window as it's still open
window.CloseNonBlocking()
if __name__ == '__main__':
CustomMeter()
MachineLearningGUI()

View file

@ -0,0 +1,115 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasAgg
import matplotlib.backends.tkagg as tkagg
import tkinter as Tk
"""
Demonstrates one way of embedding Matplotlib figures into a PySimpleGUI window.
Basic steps are:
* Create a Canvas Element
* Layout form
* Display form (NON BLOCKING)
* Draw plots onto convas
* Display form (BLOCKING)
"""
#------------------------------- PASTE YOUR MATPLOTLIB CODE HERE -------------------------------
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter # useful for `logit` scale
# Fixing random state for reproducibility
np.random.seed(19680801)
# make up some data in the interval ]0, 1[
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))
# plot with various axes scales
plt.figure(1)
# linear
plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)
# log
plt.subplot(222)
plt.plot(x, y)
plt.yscale('log')
plt.title('log')
plt.grid(True)
# symmetric log
plt.subplot(223)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthreshy=0.01)
plt.title('symlog')
plt.grid(True)
# logit
plt.subplot(224)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logit')
plt.grid(True)
plt.gca().yaxis.set_minor_formatter(NullFormatter())
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
wspace=0.35)
fig = plt.gcf() # if using Pyplot then get the figure from the plot
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
#------------------------------- END OF YOUR MATPLOTLIB CODE -------------------------------
#------------------------------- Beginning of Matplotlib helper code -----------------------
def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas
loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
return photo
#------------------------------- Beginning of GUI CODE -------------------------------
# define the window layout
layout = [[sg.Text('Plot test', font='Any 18')],
[sg.Canvas(size=(figure_w, figure_h), key='canvas')],
[sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', force_toplevel=True).Layout(layout).Finalize()
# add the plot to the window
fig_photo = draw_figure(window.FindElement('canvas').TKCanvas, fig)
# show it all again and get buttons
event, values = window.Read()

View file

@ -0,0 +1,65 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from random import randint
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg
from matplotlib.figure import Figure
import matplotlib.backends.tkagg as tkagg
import tkinter as tk
def main():
fig = Figure()
ax = fig.add_subplot(111)
ax.set_xlabel("X axis")
ax.set_ylabel("Y axis")
ax.grid()
# define the form layout
layout = [[sg.Text('Animated Matplotlib', size=(40, 1), justification='center', font='Helvetica 20')],
[sg.Canvas(size=(640, 480), key='canvas')],
[sg.Slider(range=(0, 10000), size=(60, 10), orientation='h', key='slider')],
[sg.ReadButton('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize()
canvas_elem = window.FindElement('canvas')
slider_elem = window.FindElement('slider')
graph = FigureCanvasTkAgg(fig, master=canvas_elem.TKCanvas)
canvas = canvas_elem.TKCanvas
dpts = [randint(0, 10) for x in range(10000)]
for i in range(len(dpts)):
event, values = window.Read(timeout=10)
if event is 'Exit' or event is None:
exit(69)
slider_elem.Update(i)
ax.cla()
ax.grid()
DATA_POINTS_PER_SCREEN = 40
ax.plot(range(DATA_POINTS_PER_SCREEN), dpts[i:i+DATA_POINTS_PER_SCREEN], color='purple')
graph.draw()
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(640/2, 480/2, image=photo)
figure_canvas_agg = FigureCanvasAgg(fig)
figure_canvas_agg.draw()
# Unfortunately, there's no accessor for the pointer to the native renderer
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
# time.sleep(.1)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,67 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from random import randint
import PySimpleGUI as sg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg
from matplotlib.figure import Figure
import matplotlib.backends.tkagg as tkagg
import tkinter as tk
def main():
# define the form layout
layout = [[sg.Text('Animated Matplotlib', size=(40, 1), justification='center', font='Helvetica 20')],
[sg.Canvas(size=(640, 480), key='canvas')],
[sg.ReadButton('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize()
canvas_elem = window.FindElement('canvas')
canvas = canvas_elem.TKCanvas
while True:
event, values = window.Read(timeout=10)
if event is 'Exit' or event is None:
exit(69)
def PyplotScatterWithLegend():
import matplotlib.pyplot as plt
from numpy.random import rand
fig, ax = plt.subplots()
for color in ['red', 'green', 'blue']:
n = 750
x, y = rand(2, n)
scale = 200.0 * rand(n)
ax.scatter(x, y, c=color, s=scale, label=color,
alpha=0.3, edgecolors='none')
ax.legend()
ax.grid(True)
return fig
fig = PyplotScatterWithLegend()
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(640/2, 480/2, image=photo)
figure_canvas_agg = FigureCanvasAgg(fig)
figure_canvas_agg.draw()
# Unfortunately, there's no accessor for the pointer to the native renderer
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
# time.sleep(.1)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,906 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasAgg
import matplotlib.backends.tkagg as tkagg
import tkinter as Tk
import inspect
"""
Demonstrates one way of embedding Matplotlib figures into a PySimpleGUI window.
Basic steps are:
* Create a Canvas Element
* Layout form
* Display form (NON BLOCKING)
* Draw plots onto convas
* Display form (BLOCKING)
"""
import numpy as np
import matplotlib.pyplot as plt
def PyplotSimple():
import numpy as np
import matplotlib.pyplot as plt
# evenly sampled time at 200ms intervals
t = np.arange(0., 5., 0.2)
# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t ** 2, 'bs', t, t ** 3, 'g^')
fig = plt.gcf() # get the figure to show
return fig
def PyplotHistogram():
"""
=============================================================
Demo of the histogram (hist) function with multiple data sets
=============================================================
Plot histogram with multiple sample sets and demonstrate:
* Use of legend with multiple sample sets
* Stacked bars
* Step curve with no fill
* Data sets of different sample sizes
Selecting different bin counts and sizes can significantly affect the
shape of a histogram. The Astropy docs have a great section on how to
select these parameters:
http://docs.astropy.org/en/stable/visualization/histogram.html
"""
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
n_bins = 10
x = np.random.randn(1000, 3)
fig, axes = plt.subplots(nrows=2, ncols=2)
ax0, ax1, ax2, ax3 = axes.flatten()
colors = ['red', 'tan', 'lime']
ax0.hist(x, n_bins, normed=1, histtype='bar', color=colors, label=colors)
ax0.legend(prop={'size': 10})
ax0.set_title('bars with legend')
ax1.hist(x, n_bins, normed=1, histtype='bar', stacked=True)
ax1.set_title('stacked bar')
ax2.hist(x, n_bins, histtype='step', stacked=True, fill=False)
ax2.set_title('stack step (unfilled)')
# Make a multiple-histogram of data-sets with different length.
x_multi = [np.random.randn(n) for n in [10000, 5000, 2000]]
ax3.hist(x_multi, n_bins, histtype='bar')
ax3.set_title('different sample sizes')
fig.tight_layout()
return fig
def PyplotArtistBoxPlots():
"""
=========================================
Demo of artist customization in box plots
=========================================
This example demonstrates how to use the various kwargs
to fully customize box plots. The first figure demonstrates
how to remove and add individual components (note that the
mean is the only value not shown by default). The second
figure demonstrates how the styles of the artists can
be customized. It also demonstrates how to set the limit
of the whiskers to specific percentiles (lower right axes)
A good general reference on boxplots and their history can be found
here: http://vita.had.co.nz/papers/boxplots.pdf
"""
import numpy as np
import matplotlib.pyplot as plt
# fake data
np.random.seed(937)
data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75)
labels = list('ABCD')
fs = 10 # fontsize
# demonstrate how to toggle the display of different elements:
fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True)
axes[0, 0].boxplot(data, labels=labels)
axes[0, 0].set_title('Default', fontsize=fs)
axes[0, 1].boxplot(data, labels=labels, showmeans=True)
axes[0, 1].set_title('showmeans=True', fontsize=fs)
axes[0, 2].boxplot(data, labels=labels, showmeans=True, meanline=True)
axes[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs)
axes[1, 0].boxplot(data, labels=labels, showbox=False, showcaps=False)
tufte_title = 'Tufte Style \n(showbox=False,\nshowcaps=False)'
axes[1, 0].set_title(tufte_title, fontsize=fs)
axes[1, 1].boxplot(data, labels=labels, notch=True, bootstrap=10000)
axes[1, 1].set_title('notch=True,\nbootstrap=10000', fontsize=fs)
axes[1, 2].boxplot(data, labels=labels, showfliers=False)
axes[1, 2].set_title('showfliers=False', fontsize=fs)
for ax in axes.flatten():
ax.set_yscale('log')
ax.set_yticklabels([])
fig.subplots_adjust(hspace=0.4)
return fig
def ArtistBoxplot2():
# fake data
np.random.seed(937)
data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75)
labels = list('ABCD')
fs = 10 # fontsize
# demonstrate how to customize the display different elements:
boxprops = dict(linestyle='--', linewidth=3, color='darkgoldenrod')
flierprops = dict(marker='o', markerfacecolor='green', markersize=12,
linestyle='none')
medianprops = dict(linestyle='-.', linewidth=2.5, color='firebrick')
meanpointprops = dict(marker='D', markeredgecolor='black',
markerfacecolor='firebrick')
meanlineprops = dict(linestyle='--', linewidth=2.5, color='purple')
fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True)
axes[0, 0].boxplot(data, boxprops=boxprops)
axes[0, 0].set_title('Custom boxprops', fontsize=fs)
axes[0, 1].boxplot(data, flierprops=flierprops, medianprops=medianprops)
axes[0, 1].set_title('Custom medianprops\nand flierprops', fontsize=fs)
axes[0, 2].boxplot(data, whis='range')
axes[0, 2].set_title('whis="range"', fontsize=fs)
axes[1, 0].boxplot(data, meanprops=meanpointprops, meanline=False,
showmeans=True)
axes[1, 0].set_title('Custom mean\nas point', fontsize=fs)
axes[1, 1].boxplot(data, meanprops=meanlineprops, meanline=True,
showmeans=True)
axes[1, 1].set_title('Custom mean\nas line', fontsize=fs)
axes[1, 2].boxplot(data, whis=[15, 85])
axes[1, 2].set_title('whis=[15, 85]\n#percentiles', fontsize=fs)
for ax in axes.flatten():
ax.set_yscale('log')
ax.set_yticklabels([])
fig.suptitle("I never said they'd be pretty")
fig.subplots_adjust(hspace=0.4)
return fig
def PyplotScatterWithLegend():
import matplotlib.pyplot as plt
from numpy.random import rand
fig, ax = plt.subplots()
for color in ['red', 'green', 'blue']:
n = 750
x, y = rand(2, n)
scale = 200.0 * rand(n)
ax.scatter(x, y, c=color, s=scale, label=color,
alpha=0.3, edgecolors='none')
ax.legend()
ax.grid(True)
return fig
def PyplotLineStyles():
"""
==========
Linestyles
==========
This examples showcases different linestyles copying those of Tikz/PGF.
"""
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
from matplotlib.transforms import blended_transform_factory
linestyles = OrderedDict(
[('solid', (0, ())),
('loosely dotted', (0, (1, 10))),
('dotted', (0, (1, 5))),
('densely dotted', (0, (1, 1))),
('loosely dashed', (0, (5, 10))),
('dashed', (0, (5, 5))),
('densely dashed', (0, (5, 1))),
('loosely dashdotted', (0, (3, 10, 1, 10))),
('dashdotted', (0, (3, 5, 1, 5))),
('densely dashdotted', (0, (3, 1, 1, 1))),
('loosely dashdotdotted', (0, (3, 10, 1, 10, 1, 10))),
('dashdotdotted', (0, (3, 5, 1, 5, 1, 5))),
('densely dashdotdotted', (0, (3, 1, 1, 1, 1, 1)))])
plt.figure(figsize=(10, 6))
ax = plt.subplot(1, 1, 1)
X, Y = np.linspace(0, 100, 10), np.zeros(10)
for i, (name, linestyle) in enumerate(linestyles.items()):
ax.plot(X, Y + i, linestyle=linestyle, linewidth=1.5, color='black')
ax.set_ylim(-0.5, len(linestyles) - 0.5)
plt.yticks(np.arange(len(linestyles)), linestyles.keys())
plt.xticks([])
# For each line style, add a text annotation with a small offset from
# the reference point (0 in Axes coords, y tick value in Data coords).
reference_transform = blended_transform_factory(ax.transAxes, ax.transData)
for i, (name, linestyle) in enumerate(linestyles.items()):
ax.annotate(str(linestyle), xy=(0.0, i), xycoords=reference_transform,
xytext=(-6, -12), textcoords='offset points', color="blue",
fontsize=8, ha="right", family="monospace")
plt.tight_layout()
return plt.gcf()
def PyplotLinePolyCollection():
import matplotlib.pyplot as plt
from matplotlib import collections, colors, transforms
import numpy as np
nverts = 50
npts = 100
# Make some spirals
r = np.arange(nverts)
theta = np.linspace(0, 2 * np.pi, nverts)
xx = r * np.sin(theta)
yy = r * np.cos(theta)
spiral = np.column_stack([xx, yy])
# Fixing random state for reproducibility
rs = np.random.RandomState(19680801)
# Make some offsets
xyo = rs.randn(npts, 2)
# Make a list of colors cycling through the default series.
colors = [colors.to_rgba(c)
for c in plt.rcParams['axes.prop_cycle'].by_key()['color']]
fig, axes = plt.subplots(2, 2)
fig.subplots_adjust(top=0.92, left=0.07, right=0.97,
hspace=0.3, wspace=0.3)
((ax1, ax2), (ax3, ax4)) = axes # unpack the axes
col = collections.LineCollection([spiral], offsets=xyo,
transOffset=ax1.transData)
trans = fig.dpi_scale_trans + transforms.Affine2D().scale(1.0 / 72.0)
col.set_transform(trans) # the points to pixels transform
# Note: the first argument to the collection initializer
# must be a list of sequences of x,y tuples; we have only
# one sequence, but we still have to put it in a list.
ax1.add_collection(col, autolim=True)
# autolim=True enables autoscaling. For collections with
# offsets like this, it is neither efficient nor accurate,
# but it is good enough to generate a plot that you can use
# as a starting point. If you know beforehand the range of
# x and y that you want to show, it is better to set them
# explicitly, leave out the autolim kwarg (or set it to False),
# and omit the 'ax1.autoscale_view()' call below.
# Make a transform for the line segments such that their size is
# given in points:
col.set_color(colors)
ax1.autoscale_view() # See comment above, after ax1.add_collection.
ax1.set_title('LineCollection using offsets')
# The same data as above, but fill the curves.
col = collections.PolyCollection([spiral], offsets=xyo,
transOffset=ax2.transData)
trans = transforms.Affine2D().scale(fig.dpi / 72.0)
col.set_transform(trans) # the points to pixels transform
ax2.add_collection(col, autolim=True)
col.set_color(colors)
ax2.autoscale_view()
ax2.set_title('PolyCollection using offsets')
# 7-sided regular polygons
col = collections.RegularPolyCollection(
7, sizes=np.abs(xx) * 10.0, offsets=xyo, transOffset=ax3.transData)
trans = transforms.Affine2D().scale(fig.dpi / 72.0)
col.set_transform(trans) # the points to pixels transform
ax3.add_collection(col, autolim=True)
col.set_color(colors)
ax3.autoscale_view()
ax3.set_title('RegularPolyCollection using offsets')
# Simulate a series of ocean current profiles, successively
# offset by 0.1 m/s so that they form what is sometimes called
# a "waterfall" plot or a "stagger" plot.
nverts = 60
ncurves = 20
offs = (0.1, 0.0)
yy = np.linspace(0, 2 * np.pi, nverts)
ym = np.max(yy)
xx = (0.2 + (ym - yy) / ym) ** 2 * np.cos(yy - 0.4) * 0.5
segs = []
for i in range(ncurves):
xxx = xx + 0.02 * rs.randn(nverts)
curve = np.column_stack([xxx, yy * 100])
segs.append(curve)
col = collections.LineCollection(segs, offsets=offs)
ax4.add_collection(col, autolim=True)
col.set_color(colors)
ax4.autoscale_view()
ax4.set_title('Successive data offsets')
ax4.set_xlabel('Zonal velocity component (m/s)')
ax4.set_ylabel('Depth (m)')
# Reverse the y-axis so depth increases downward
ax4.set_ylim(ax4.get_ylim()[::-1])
return fig
def PyplotGGPlotSytleSheet():
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, axes = plt.subplots(ncols=2, nrows=2)
ax1, ax2, ax3, ax4 = axes.ravel()
# scatter plot (Note: `plt.scatter` doesn't use default colors)
x, y = np.random.normal(size=(2, 200))
ax1.plot(x, y, 'o')
# sinusoidal lines with colors from default color cycle
L = 2 * np.pi
x = np.linspace(0, L)
ncolors = len(plt.rcParams['axes.prop_cycle'])
shift = np.linspace(0, L, ncolors, endpoint=False)
for s in shift:
ax2.plot(x, np.sin(x + s), '-')
ax2.margins(0)
# bar graphs
x = np.arange(5)
y1, y2 = np.random.randint(1, 25, size=(2, 5))
width = 0.25
ax3.bar(x, y1, width)
ax3.bar(x + width, y2, width,
color=list(plt.rcParams['axes.prop_cycle'])[2]['color'])
ax3.set_xticks(x + width)
ax3.set_xticklabels(['a', 'b', 'c', 'd', 'e'])
# circles with colors from default color cycle
for i, color in enumerate(plt.rcParams['axes.prop_cycle']):
xy = np.random.normal(size=2)
ax4.add_patch(plt.Circle(xy, radius=0.3, color=color['color']))
ax4.axis('equal')
ax4.margins(0)
fig = plt.gcf() # get the figure to show
return fig
def PyplotBoxPlot():
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
# fake up some data
spread = np.random.rand(50) * 100
center = np.ones(25) * 50
flier_high = np.random.rand(10) * 100 + 100
flier_low = np.random.rand(10) * -100
data = np.concatenate((spread, center, flier_high, flier_low), 0)
fig1, ax1 = plt.subplots()
ax1.set_title('Basic Plot')
ax1.boxplot(data)
return fig1
def PyplotRadarChart():
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.spines import Spine
from matplotlib.projections.polar import PolarAxes
from matplotlib.projections import register_projection
def radar_factory(num_vars, frame='circle'):
"""Create a radar chart with `num_vars` axes.
This function creates a RadarAxes projection and registers it.
Parameters
----------
num_vars : int
Number of variables for radar chart.
frame : {'circle' | 'polygon'}
Shape of frame surrounding axes.
"""
# calculate evenly-spaced axis angles
theta = np.linspace(0, 2 * np.pi, num_vars, endpoint=False)
def draw_poly_patch(self):
# rotate theta such that the first axis is at the top
verts = unit_poly_verts(theta + np.pi / 2)
return plt.Polygon(verts, closed=True, edgecolor='k')
def draw_circle_patch(self):
# unit circle centered on (0.5, 0.5)
return plt.Circle((0.5, 0.5), 0.5)
patch_dict = {'polygon': draw_poly_patch, 'circle': draw_circle_patch}
if frame not in patch_dict:
raise ValueError('unknown value for `frame`: %s' % frame)
class RadarAxes(PolarAxes):
name = 'radar'
# use 1 line segment to connect specified points
RESOLUTION = 1
# define draw_frame method
draw_patch = patch_dict[frame]
def __init__(self, *args, **kwargs):
super(RadarAxes, self).__init__(*args, **kwargs)
# rotate plot such that the first axis is at the top
self.set_theta_zero_location('N')
def fill(self, *args, **kwargs):
"""Override fill so that line is closed by default"""
closed = kwargs.pop('closed', True)
return super(RadarAxes, self).fill(closed=closed, *args, **kwargs)
def plot(self, *args, **kwargs):
"""Override plot so that line is closed by default"""
lines = super(RadarAxes, self).plot(*args, **kwargs)
for line in lines:
self._close_line(line)
def _close_line(self, line):
x, y = line.get_data()
# FIXME: markers at x[0], y[0] get doubled-up
if x[0] != x[-1]:
x = np.concatenate((x, [x[0]]))
y = np.concatenate((y, [y[0]]))
line.set_data(x, y)
def set_varlabels(self, labels):
self.set_thetagrids(np.degrees(theta), labels)
def _gen_axes_patch(self):
return self.draw_patch()
def _gen_axes_spines(self):
if frame == 'circle':
return PolarAxes._gen_axes_spines(self)
# The following is a hack to get the spines (i.e. the axes frame)
# to draw correctly for a polygon frame.
# spine_type must be 'left', 'right', 'top', 'bottom', or `circle`.
spine_type = 'circle'
verts = unit_poly_verts(theta + np.pi / 2)
# close off polygon by repeating first vertex
verts.append(verts[0])
path = Path(verts)
spine = Spine(self, spine_type, path)
spine.set_transform(self.transAxes)
return {'polar': spine}
register_projection(RadarAxes)
return theta
def unit_poly_verts(theta):
"""Return vertices of polygon for subplot axes.
This polygon is circumscribed by a unit circle centered at (0.5, 0.5)
"""
x0, y0, r = [0.5] * 3
verts = [(r * np.cos(t) + x0, r * np.sin(t) + y0) for t in theta]
return verts
def example_data():
# The following data is from the Denver Aerosol Sources and Health study.
# See doi:10.1016/j.atmosenv.2008.12.017
#
# The data are pollution source profile estimates for five modeled
# pollution sources (e.g., cars, wood-burning, etc) that emit 7-9 chemical
# species. The radar charts are experimented with here to see if we can
# nicely visualize how the modeled source profiles change across four
# scenarios:
# 1) No gas-phase species present, just seven particulate counts on
# Sulfate
# Nitrate
# Elemental Carbon (EC)
# Organic Carbon fraction 1 (OC)
# Organic Carbon fraction 2 (OC2)
# Organic Carbon fraction 3 (OC3)
# Pyrolized Organic Carbon (OP)
# 2)Inclusion of gas-phase specie carbon monoxide (CO)
# 3)Inclusion of gas-phase specie ozone (O3).
# 4)Inclusion of both gas-phase species is present...
data = [
['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP', 'CO', 'O3'],
('Basecase', [
[0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00],
[0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00],
[0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00],
[0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00],
[0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]]),
('With CO', [
[0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00],
[0.08, 0.94, 0.04, 0.02, 0.00, 0.01, 0.12, 0.04, 0.00],
[0.01, 0.01, 0.79, 0.10, 0.00, 0.05, 0.00, 0.31, 0.00],
[0.00, 0.02, 0.03, 0.38, 0.31, 0.31, 0.00, 0.59, 0.00],
[0.02, 0.02, 0.11, 0.47, 0.69, 0.58, 0.88, 0.00, 0.00]]),
('With O3', [
[0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03],
[0.07, 0.95, 0.05, 0.04, 0.00, 0.02, 0.12, 0.00, 0.00],
[0.01, 0.02, 0.86, 0.27, 0.16, 0.19, 0.00, 0.00, 0.00],
[0.01, 0.03, 0.00, 0.32, 0.29, 0.27, 0.00, 0.00, 0.95],
[0.02, 0.00, 0.03, 0.37, 0.56, 0.47, 0.87, 0.00, 0.00]]),
('CO & O3', [
[0.87, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01],
[0.09, 0.95, 0.02, 0.03, 0.00, 0.01, 0.13, 0.06, 0.00],
[0.01, 0.02, 0.71, 0.24, 0.13, 0.16, 0.00, 0.50, 0.00],
[0.01, 0.03, 0.00, 0.28, 0.24, 0.23, 0.00, 0.44, 0.88],
[0.02, 0.00, 0.18, 0.45, 0.64, 0.55, 0.86, 0.00, 0.16]])
]
return data
N = 9
theta = radar_factory(N, frame='polygon')
data = example_data()
spoke_labels = data.pop(0)
fig, axes = plt.subplots(figsize=(9, 9), nrows=2, ncols=2,
subplot_kw=dict(projection='radar'))
fig.subplots_adjust(wspace=0.25, hspace=0.20, top=0.85, bottom=0.05)
colors = ['b', 'r', 'g', 'm', 'y']
# Plot the four cases from the example data on separate axes
for ax, (title, case_data) in zip(axes.flatten(), data):
ax.set_rgrids([0.2, 0.4, 0.6, 0.8])
ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1),
horizontalalignment='center', verticalalignment='center')
for d, color in zip(case_data, colors):
ax.plot(theta, d, color=color)
ax.fill(theta, d, facecolor=color, alpha=0.25)
ax.set_varlabels(spoke_labels)
# add legend relative to top-left plot
ax = axes[0, 0]
labels = ('Factor 1', 'Factor 2', 'Factor 3', 'Factor 4', 'Factor 5')
legend = ax.legend(labels, loc=(0.9, .95),
labelspacing=0.1, fontsize='small')
fig.text(0.5, 0.965, '5-Factor Solution Profiles Across Four Scenarios',
horizontalalignment='center', color='black', weight='bold',
size='large')
return fig
def DifferentScales():
import numpy as np
import matplotlib.pyplot as plt
# Create some mock data
t = np.arange(0.01, 10.0, 0.01)
data1 = np.exp(t)
data2 = np.sin(2 * np.pi * t)
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel('time (s)')
ax1.set_ylabel('exp', color=color)
ax1.plot(t, data1, color=color)
ax1.tick_params(axis='y', labelcolor=color)
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color = 'tab:blue'
ax2.set_ylabel('sin', color=color) # we already handled the x-label with ax1
ax2.plot(t, data2, color=color)
ax2.tick_params(axis='y', labelcolor=color)
fig.tight_layout() # otherwise the right y-label is slightly clipped
return fig
def ExploringNormalizations():
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
from numpy.random import multivariate_normal
data = np.vstack([
multivariate_normal([10, 10], [[3, 2], [2, 3]], size=100000),
multivariate_normal([30, 20], [[2, 3], [1, 3]], size=1000)
])
gammas = [0.8, 0.5, 0.3]
fig, axes = plt.subplots(nrows=2, ncols=2)
axes[0, 0].set_title('Linear normalization')
axes[0, 0].hist2d(data[:, 0], data[:, 1], bins=100)
for ax, gamma in zip(axes.flat[1:], gammas):
ax.set_title(r'Power law $(\gamma=%1.1f)$' % gamma)
ax.hist2d(data[:, 0], data[:, 1],
bins=100, norm=mcolors.PowerNorm(gamma))
fig.tight_layout()
return fig
def PyplotFormatstr():
def f(t):
return np.exp(-t) * np.cos(2*np.pi*t)
t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)
plt.figure(1)
plt.subplot(211)
plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')
plt.subplot(212)
plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
fig = plt.gcf() # get the figure to show
return fig
def UnicodeMinus():
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
matplotlib.rcParams['axes.unicode_minus'] = False
fig, ax = plt.subplots()
ax.plot(10 * np.random.randn(100), 10 * np.random.randn(100), 'o')
ax.set_title('Using hyphen instead of Unicode minus')
return fig
def Subplot3d():
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib import cm
# from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(1, 2, 1, projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X ** 2 + Y ** 2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet,
linewidth=0, antialiased=False)
ax.set_zlim3d(-1.01, 1.01)
# ax.w_zaxis.set_major_locator(LinearLocator(10))
# ax.w_zaxis.set_major_formatter(FormatStrFormatter('%.03f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
from mpl_toolkits.mplot3d.axes3d import get_test_data
ax = fig.add_subplot(1, 2, 2, projection='3d')
X, Y, Z = get_test_data(0.05)
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
return fig
def PyplotScales():
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter # useful for `logit` scale
# Fixing random state for reproducibility
np.random.seed(19680801)
# make up some data in the interval ]0, 1[
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))
# plot with various axes scales
plt.figure(1)
# linear
plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)
# log
plt.subplot(222)
plt.plot(x, y)
plt.yscale('log')
plt.title('log')
plt.grid(True)
# symmetric log
plt.subplot(223)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthreshy=0.01)
plt.title('symlog')
plt.grid(True)
# logit
plt.subplot(224)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logit')
plt.grid(True)
# Format the minor tick labels of the y-axis into empty strings with
# `NullFormatter`, to avoid cumbering the axis with too many labels.
plt.gca().yaxis.set_minor_formatter(NullFormatter())
# Adjust the subplot layout, because the logit one may take more space
# than usual, due to y-tick labels like "1 - 10^{-3}"
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
wspace=0.35)
return plt.gcf()
def AxesGrid():
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_rgb import RGBAxes
def get_demo_image():
# prepare image
delta = 0.5
extent = (-3, 4, -4, 3)
x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
return Z, extent
def get_rgb():
Z, extent = get_demo_image()
Z[Z < 0] = 0.
Z = Z / Z.max()
R = Z[:13, :13]
G = Z[2:, 2:]
B = Z[:13, 2:]
return R, G, B
fig = plt.figure(1)
ax = RGBAxes(fig, [0.1, 0.1, 0.8, 0.8])
r, g, b = get_rgb()
kwargs = dict(origin="lower", interpolation="nearest")
ax.imshow_rgb(r, g, b, **kwargs)
ax.RGB.set_xlim(0., 9.5)
ax.RGB.set_ylim(0.9, 10.6)
plt.draw()
return plt.gcf()
# The magic function that makes it possible.... glues together tkinter and pyplot using Canvas Widget
def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas
loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
# Position: convert from top-left anchor to center anchor
canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
# Unfortunately, there's no accessor for the pointer to the native renderer
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
# Return a handle which contains a reference to the photo object
# which must be kept live or else the picture disappears
return photo
# -------------------------------- GUI Starts Here -------------------------------#
# fig = your figure you want to display. Assumption is that 'fig' holds the #
# information to display. #
# --------------------------------------------------------------------------------#
# print(inspect.getsource(PyplotSimple))
fig_dict = {'Pyplot Simple':PyplotSimple, 'Pyplot Formatstr':PyplotFormatstr,'PyPlot Three':Subplot3d,
'Unicode Minus': UnicodeMinus, 'Pyplot Scales' : PyplotScales, 'Axes Grid' : AxesGrid,
'Exploring Normalizations' : ExploringNormalizations, 'Different Scales' : DifferentScales,
'Pyplot Box Plot' : PyplotBoxPlot, 'Pyplot ggplot Style Sheet' : PyplotGGPlotSytleSheet,
'Pyplot Line Poly Collection' : PyplotLinePolyCollection, 'Pyplot Line Styles' : PyplotLineStyles,
'Pyplot Scatter With Legend' :PyplotScatterWithLegend, 'Artist Customized Box Plots' : PyplotArtistBoxPlots,
'Artist Customized Box Plots 2' : ArtistBoxplot2, 'Pyplot Histogram' : PyplotHistogram}
sg.ChangeLookAndFeel('LightGreen')
figure_w, figure_h = 650, 650
# define the form layout
listbox_values = [key for key in fig_dict.keys()]
col_listbox = [[sg.Listbox(values=listbox_values, change_submits=True, size=(28, len(listbox_values)), key='func')],
[sg.T(' ' * 12), sg.Exit(size=(5, 2))]]
layout = [[sg.Text('Matplotlib Plot Test', font=('current 18'))],
[sg.Column(col_listbox, pad=(5, (3, 330))), sg.Canvas(size=(figure_w, figure_h), key='canvas') ,
sg.Multiline(size=(70, 35), pad=(5, (3, 90)), key='multiline')],]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', grab_anywhere=False).Layout(layout)
window.Finalize()
canvas_elem = window.FindElement('canvas')
multiline_elem= window.FindElement('multiline')
while True:
event, values = window.Read()
print(event)
# show it all again and get buttons
if event is None or event is 'Exit':
break
try:
choice = values['func'][0]
func = fig_dict[choice]
except:
pass
multiline_elem.Update(inspect.getsource(func))
plt.clf()
fig = func()
fig_photo = draw_figure(canvas_elem.TKCanvas, fig)

View file

@ -0,0 +1,674 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasAgg
import matplotlib.backends.tkagg as tkagg
import tkinter as tk
"""
A graph of time to ping Google.com
Demonstrates Matploylib used in an animated way.
Note this file contains a copy of ping.py. It is contained in the first part of this file
"""
"""
A pure python ping implementation using raw sockets.
(This is Python 3 port of https://github.com/jedie/python-ping)
(Tested and working with python 2.7, should work with 2.6+)
Note that ICMP messages can only be sent from processes running as root
(in Windows, you must run this script as 'Administrator').
Derived from ping.c distributed in Linux's netkit. That code is
copyright (c) 1989 by The Regents of the University of California.
That code is in turn derived from code written by Mike Muuss of the
US Army Ballistic Research Laboratory in December, 1983 and
placed in the public domain. They have my thanks.
Bugs are naturally mine. I'd be glad to hear about them. There are
certainly word - size dependencies here.
Copyright (c) Matthew Dixon Cowles, <http://www.visi.com/~mdc/>.
Distributable under the terms of the GNU General Public License
version 2. Provided with no warranties of any sort.
Original Version from Matthew Dixon Cowles:
-> ftp://ftp.visi.com/users/mdc/ping.py
Rewrite by Jens Diemer:
-> http://www.python-forum.de/post-69122.html#69122
Rewrite by George Notaras:
-> http://www.g-loaded.eu/2009/10/30/python-ping/
Enhancements by Martin Falatic:
-> http://www.falatic.com/index.php/39/pinging-with-python
Enhancements and fixes by Georgi Kolev:
-> http://github.com/jedie/python-ping/
Bug fix by Andrejs Rozitis:
-> http://github.com/rozitis/python-ping/
Revision history
~~~~~~~~~~~~~~~~
May 1, 2014
-----------
Little modifications by Mohammad Emami <emamirazavi@gmail.com>
- Added Python 3 support. For now this project will just support
python 3.x
- Tested with python 3.3
- version was upped to 0.6
March 19, 2013
--------------
* Fixing bug to prevent divide by 0 during run-time.
January 26, 2012
----------------
* Fixing BUG #4 - competability with python 2.x [tested with 2.7]
- Packet data building is different for 2.x and 3.x.
'cose of the string/bytes difference.
* Fixing BUG #10 - the multiple resolv issue.
- When pinging domain names insted of hosts (for exmaple google.com)
you can get different IP every time you try to resolv it, we should
resolv the host only once and stick to that IP.
* Fixing BUGs #3 #10 - Doing hostname resolv only once.
* Fixing BUG #14 - Removing all 'global' stuff.
- You should not use globul! Its bad for you...and its not thread safe!
* Fix - forcing the use of different times on linux/windows for
more accurate mesurments. (time.time - linux/ time.clock - windows)
* Adding quiet_ping function - This way we'll be able to use this script
as external lib.
* Changing default timeout to 3s. (1second is not enought)
* Switching data syze to packet size. It's easyer for the user to ignore the
fact that the packet headr is 8b and the datasize 64 will make packet with
size 72.
October 12, 2011
--------------
Merged updates from the main project
-> https://github.com/jedie/python-ping
September 12, 2011
--------------
Bugfixes + cleanup by Jens Diemer
Tested with Ubuntu + Windows 7
September 6, 2011
--------------
Cleanup by Martin Falatic. Restored lost comments and docs. Improved
functionality: constant time between pings, internal times consistently
use milliseconds. Clarified annotations (e.g., in the checksum routine).
Using unsigned data in IP & ICMP header pack/unpack unless otherwise
necessary. Signal handling. Ping-style output formatting and stats.
August 3, 2011
--------------
Ported to py3k by Zach Ware. Mostly done by 2to3; also minor changes to
deal with bytes vs. string changes (no more ord() in checksum() because
>source_string< is actually bytes, added .encode() to data in
send_one_ping()). That's about it.
March 11, 2010
--------------
changes by Samuel Stauffer:
- replaced time.clock with default_timer which is set to
time.clock on windows and time.time on other systems.
November 8, 2009
----------------
Improved compatibility with GNU/Linux systems.
Fixes by:
* George Notaras -- http://www.g-loaded.eu
Reported by:
* Chris Hallman -- http://cdhallman.blogspot.com
Changes in this release:
- Re-use time.time() instead of time.clock(). The 2007 implementation
worked only under Microsoft Windows. Failed on GNU/Linux.
time.clock() behaves differently under the two OSes[1].
[1] http://docs.python.org/library/time.html#time.clock
May 30, 2007
------------
little rewrite by Jens Diemer:
- change socket asterisk import to a normal import
- replace time.time() with time.clock()
- delete "return None" (or change to "return" only)
- in checksum() rename "str" to "source_string"
December 4, 2000
----------------
Changed the struct.pack() calls to pack the checksum and ID as
unsigned. My thanks to Jerome Poincheval for the fix.
November 22, 1997
-----------------
Initial hack. Doesn't do much, but rather than try to guess
what features I (or others) will want in the future, I've only
put in what I need now.
December 16, 1997
-----------------
For some reason, the checksum bytes are in the wrong order when
this is run under Solaris 2.X for SPARC but it works right under
Linux x86. Since I don't know just what's wrong, I'll swap the
bytes always and then do an htons().
===========================================================================
IP header info from RFC791
-> http://tools.ietf.org/html/rfc791)
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
===========================================================================
ICMP Echo / Echo Reply Message header info from RFC792
-> http://tools.ietf.org/html/rfc792
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ...
+-+-+-+-+-
===========================================================================
ICMP parameter info:
-> http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml
===========================================================================
An example of ping's typical output:
PING heise.de (193.99.144.80): 56 data bytes
64 bytes from 193.99.144.80: icmp_seq=0 ttl=240 time=127 ms
64 bytes from 193.99.144.80: icmp_seq=1 ttl=240 time=127 ms
64 bytes from 193.99.144.80: icmp_seq=2 ttl=240 time=126 ms
64 bytes from 193.99.144.80: icmp_seq=3 ttl=240 time=126 ms
64 bytes from 193.99.144.80: icmp_seq=4 ttl=240 time=127 ms
----heise.de PING Statistics----
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip (ms) min/avg/max/med = 126/127/127/127
===========================================================================
"""
# =============================================================================#
import argparse
import os, sys, socket, struct, select, time, signal
__description__ = 'A pure python ICMP ping implementation using raw sockets.'
if sys.platform == "win32":
# On Windows, the best timer is time.clock()
default_timer = time.clock
else:
# On most other platforms the best timer is time.time()
default_timer = time.time
NUM_PACKETS = 3
PACKET_SIZE = 64
WAIT_TIMEOUT = 3.0
# =============================================================================#
# ICMP parameters
ICMP_ECHOREPLY = 0 # Echo reply (per RFC792)
ICMP_ECHO = 8 # Echo request (per RFC792)
ICMP_MAX_RECV = 2048 # Max size of incoming buffer
MAX_SLEEP = 1000
class MyStats:
thisIP = "0.0.0.0"
pktsSent = 0
pktsRcvd = 0
minTime = 999999999
maxTime = 0
totTime = 0
avrgTime = 0
fracLoss = 1.0
myStats = MyStats # NOT Used globally anymore.
# =============================================================================#
def checksum(source_string):
"""
A port of the functionality of in_cksum() from ping.c
Ideally this would act on the string as a series of 16-bit ints (host
packed), but this works.
Network data is big-endian, hosts are typically little-endian
"""
countTo = (int(len(source_string) / 2)) * 2
sum = 0
count = 0
# Handle bytes in pairs (decoding as short ints)
loByte = 0
hiByte = 0
while count < countTo:
if (sys.byteorder == "little"):
loByte = source_string[count]
hiByte = source_string[count + 1]
else:
loByte = source_string[count + 1]
hiByte = source_string[count]
try: # For Python3
sum = sum + (hiByte * 256 + loByte)
except: # For Python2
sum = sum + (ord(hiByte) * 256 + ord(loByte))
count += 2
# Handle last byte if applicable (odd-number of bytes)
# Endianness should be irrelevant in this case
if countTo < len(source_string): # Check for odd length
loByte = source_string[len(source_string) - 1]
try: # For Python3
sum += loByte
except: # For Python2
sum += ord(loByte)
sum &= 0xffffffff # Truncate sum to 32 bits (a variance from ping.c, which
# uses signed ints, but overflow is unlikely in ping)
sum = (sum >> 16) + (sum & 0xffff) # Add high 16 bits to low 16 bits
sum += (sum >> 16) # Add carry from above (if any)
answer = ~sum & 0xffff # Invert and truncate to 16 bits
answer = socket.htons(answer)
return answer
# =============================================================================#
def do_one(myStats, destIP, hostname, timeout, mySeqNumber, packet_size, quiet=False):
"""
Returns either the delay (in ms) or None on timeout.
"""
delay = None
try: # One could use UDP here, but it's obscure
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))
except socket.error as e:
print("failed. (socket error: '%s')" % e.args[1])
raise # raise the original error
my_ID = os.getpid() & 0xFFFF
sentTime = send_one_ping(mySocket, destIP, my_ID, mySeqNumber, packet_size)
if sentTime == None:
mySocket.close()
return delay
myStats.pktsSent += 1
recvTime, dataSize, iphSrcIP, icmpSeqNumber, iphTTL = receive_one_ping(mySocket, my_ID, timeout)
mySocket.close()
if recvTime:
delay = (recvTime - sentTime) * 1000
if not quiet:
print("%d bytes from %s: icmp_seq=%d ttl=%d time=%d ms" % (
dataSize, socket.inet_ntoa(struct.pack("!I", iphSrcIP)), icmpSeqNumber, iphTTL, delay)
)
myStats.pktsRcvd += 1
myStats.totTime += delay
if myStats.minTime > delay:
myStats.minTime = delay
if myStats.maxTime < delay:
myStats.maxTime = delay
else:
delay = None
print("Request timed out.")
return delay
# =============================================================================#
def send_one_ping(mySocket, destIP, myID, mySeqNumber, packet_size):
"""
Send one ping to the given >destIP<.
"""
# destIP = socket.gethostbyname(destIP)
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
# (packet_size - 8) - Remove header size from packet size
myChecksum = 0
# Make a dummy heder with a 0 checksum.
header = struct.pack(
"!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
padBytes = []
startVal = 0x42
# 'cose of the string/byte changes in python 2/3 we have
# to build the data differnely for different version
# or it will make packets with unexpected size.
if sys.version[:1] == '2':
bytes = struct.calcsize("d")
data = ((packet_size - 8) - bytes) * "Q"
data = struct.pack("d", default_timer()) + data
else:
for i in range(startVal, startVal + (packet_size - 8)):
padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
# data = bytes(padBytes)
data = bytearray(padBytes)
# Calculate the checksum on the data and the dummy header.
myChecksum = checksum(header + data) # Checksum is in network order
# Now that we have the right checksum, we put that in. It's just easier
# to make up a new header than to stuff it into the dummy.
header = struct.pack(
"!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
packet = header + data
sendTime = default_timer()
try:
mySocket.sendto(packet, (destIP, 1)) # Port number is irrelevant for ICMP
except socket.error as e:
print("General failure (%s)" % (e.args[1]))
return
return sendTime
# =============================================================================#
def receive_one_ping(mySocket, myID, timeout):
"""
Receive the ping from the socket. Timeout = in ms
"""
timeLeft = timeout / 1000
while True: # Loop while waiting for packet or timeout
startedSelect = default_timer()
whatReady = select.select([mySocket], [], [], timeLeft)
howLongInSelect = (default_timer() - startedSelect)
if whatReady[0] == []: # Timeout
return None, 0, 0, 0, 0
timeReceived = default_timer()
recPacket, addr = mySocket.recvfrom(ICMP_MAX_RECV)
ipHeader = recPacket[:20]
iphVersion, iphTypeOfSvc, iphLength, \
iphID, iphFlags, iphTTL, iphProtocol, \
iphChecksum, iphSrcIP, iphDestIP = struct.unpack(
"!BBHHHBBHII", ipHeader
)
icmpHeader = recPacket[20:28]
icmpType, icmpCode, icmpChecksum, \
icmpPacketID, icmpSeqNumber = struct.unpack(
"!BBHHH", icmpHeader
)
if icmpPacketID == myID: # Our packet
dataSize = len(recPacket) - 28
# print (len(recPacket.encode()))
return timeReceived, (dataSize + 8), iphSrcIP, icmpSeqNumber, iphTTL
timeLeft = timeLeft - howLongInSelect
if timeLeft <= 0:
return None, 0, 0, 0, 0
# =============================================================================#
def dump_stats(myStats):
"""
Show stats when pings are done
"""
print("\n----%s PYTHON PING Statistics----" % (myStats.thisIP))
if myStats.pktsSent > 0:
myStats.fracLoss = (myStats.pktsSent - myStats.pktsRcvd) / myStats.pktsSent
print("%d packets transmitted, %d packets received, %0.1f%% packet loss" % (
myStats.pktsSent, myStats.pktsRcvd, 100.0 * myStats.fracLoss
))
if myStats.pktsRcvd > 0:
print("round-trip (ms) min/avg/max = %d/%0.1f/%d" % (
myStats.minTime, myStats.totTime / myStats.pktsRcvd, myStats.maxTime
))
print("")
return
# =============================================================================#
def signal_handler(signum, frame):
"""
Handle exit via signals
"""
dump_stats()
print("\n(Terminated with signal %d)\n" % (signum))
sys.exit(0)
# =============================================================================#
def verbose_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
packet_size=PACKET_SIZE, path_finder=False):
"""
Send >count< ping to >destIP< with the given >timeout< and display
the result.
"""
signal.signal(signal.SIGINT, signal_handler) # Handle Ctrl-C
if hasattr(signal, "SIGBREAK"):
# Handle Ctrl-Break e.g. under Windows
signal.signal(signal.SIGBREAK, signal_handler)
myStats = MyStats() # Reset the stats
mySeqNumber = 0 # Starting value
try:
destIP = socket.gethostbyname(hostname)
print("\nPYTHON PING %s (%s): %d data bytes" % (hostname, destIP, packet_size))
except socket.gaierror as e:
print("\nPYTHON PING: Unknown host: %s (%s)" % (hostname, e.args[1]))
print()
return
myStats.thisIP = destIP
for i in range(count):
delay = do_one(myStats, destIP, hostname, timeout, mySeqNumber, packet_size)
if delay == None:
delay = 0
mySeqNumber += 1
# Pause for the remainder of the MAX_SLEEP period (if applicable)
if (MAX_SLEEP > delay):
time.sleep((MAX_SLEEP - delay) / 1000)
dump_stats(myStats)
#=============================================================================#
def quiet_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
packet_size=PACKET_SIZE, path_finder=False):
"""
Same as verbose_ping, but the results are returned as tuple
"""
myStats = MyStats() # Reset the stats
mySeqNumber = 0 # Starting value
try:
destIP = socket.gethostbyname(hostname)
except socket.gaierror as e:
return 0,0,0,0
myStats.thisIP = destIP
# This will send packet that we dont care about 0.5 seconds before it starts
# acrutally pinging. This is needed in big MAN/LAN networks where you sometimes
# loose the first packet. (while the switches find the way... :/ )
if path_finder:
fakeStats = MyStats()
do_one(fakeStats, destIP, hostname, timeout,
mySeqNumber, packet_size, quiet=True)
time.sleep(0.5)
for i in range(count):
delay = do_one(myStats, destIP, hostname, timeout,
mySeqNumber, packet_size, quiet=True)
if delay == None:
delay = 0
mySeqNumber += 1
# Pause for the remainder of the MAX_SLEEP period (if applicable)
if (MAX_SLEEP > delay):
time.sleep((MAX_SLEEP - delay)/1000)
if myStats.pktsSent > 0:
myStats.fracLoss = (myStats.pktsSent - myStats.pktsRcvd)/myStats.pktsSent
if myStats.pktsRcvd > 0:
myStats.avrgTime = myStats.totTime / myStats.pktsRcvd
# return tuple(max_rtt, min_rtt, avrg_rtt, percent_lost)
return myStats.maxTime, myStats.minTime, myStats.avrgTime, myStats.fracLoss
# =============================================================================#
#================================================================================
# Globals
# These are needed because callback functions are used.
# Need to retain state across calls
#================================================================================
SIZE=(320,240)
class MyGlobals:
axis_pings = None
ping_x_array = []
ping_y_array = []
g_my_globals = MyGlobals()
#================================================================================
# Performs *** PING! ***
#================================================================================
def run_a_ping_and_graph():
global g_my_globals # graphs are global so that can be retained across multiple calls to this callback
#===================== Do the ping =====================#
response = quiet_ping('google.com',timeout=1000)
if response[0] == 0:
ping_time = 1000
else:
ping_time = response[0]
#===================== Store current ping in historical array =====================#
g_my_globals.ping_x_array.append(len(g_my_globals.ping_x_array))
g_my_globals.ping_y_array.append(ping_time)
# ===================== Only graph last 100 items =====================#
if len(g_my_globals.ping_x_array) > 100:
x_array = g_my_globals.ping_x_array[-100:]
y_array = g_my_globals.ping_y_array[-100:]
else:
x_array = g_my_globals.ping_x_array
y_array = g_my_globals.ping_y_array
# ===================== Call graphinc functions =====================#
g_my_globals.axis_ping.clear() # clear before graphing
set_chart_labels()
g_my_globals.axis_ping.plot(x_array,y_array) # graph the ping values
#================================================================================
# Function: Set graph titles and Axis labels
# Sets the text for the subplots
# Have to do this in 2 places... initially when creating and when updating
# So, putting into a function so don't have to duplicate code
#================================================================================
def set_chart_labels():
global g_my_globals
g_my_globals.axis_ping.set_xlabel('Time', fontsize=8)
g_my_globals.axis_ping.set_ylabel('Ping (ms)', fontsize=8)
g_my_globals.axis_ping.set_title('Current Ping Duration', fontsize = 8)
def draw(fig, canvas):
# Magic code that draws the figure onto the Canvas Element's canvas
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(SIZE[0] / 2, SIZE[1] / 2, image=photo)
figure_canvas_agg = FigureCanvasAgg(fig)
figure_canvas_agg.draw()
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
return photo
#================================================================================
# Function: MAIN
#================================================================================
def main():
global g_my_globals
# define the form layout
layout = [[ sg.Canvas(size=SIZE, background_color='white',key='canvas') , sg.Button('Exit', pad=(0, (210, 0)))]]
# create the form and show it without the plot
window = sg.Window('Ping Graph', background_color='white', grab_anywhere=True).Layout(layout).Finalize()
canvas_elem = window.FindElement('canvas')
canvas = canvas_elem.TKCanvas
fig = plt.figure(figsize=(3.1, 2.25), tight_layout={'pad':0})
g_my_globals.axis_ping = fig.add_subplot(1,1,1)
plt.rcParams['xtick.labelsize'] = 8
plt.rcParams['ytick.labelsize'] = 8
set_chart_labels()
plt.tight_layout()
while True:
event, values = window.Read(timeout=0)
if event is 'Exit' or event is None:
exit(0)
run_a_ping_and_graph()
photo = draw(fig, canvas)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,110 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import matplotlib.pyplot as plt
import ping
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg
import matplotlib.backends.tkagg as tkagg
import tkinter as tk
#================================================================================
# Globals
# These are needed because callback functions are used.
# Need to retain state across calls
#================================================================================
class MyGlobals:
axis_pings = None
ping_x_array = []
ping_y_array = []
g_my_globals = MyGlobals()
#================================================================================
# Performs *** PING! ***
#================================================================================
def run_a_ping_and_graph():
global g_my_globals # graphs are global so that can be retained across multiple calls to this callback
#===================== Do the ping =====================#
response = ping.quiet_ping('google.com',timeout=1000)
if response[0] == 0:
ping_time = 1000
else:
ping_time = response[0]
#===================== Store current ping in historical array =====================#
g_my_globals.ping_x_array.append(len(g_my_globals.ping_x_array))
g_my_globals.ping_y_array.append(ping_time)
# ===================== Only graph last 100 items =====================#
if len(g_my_globals.ping_x_array) > 100:
x_array = g_my_globals.ping_x_array[-100:]
y_array = g_my_globals.ping_y_array[-100:]
else:
x_array = g_my_globals.ping_x_array
y_array = g_my_globals.ping_y_array
# ===================== Call graphinc functions =====================#
g_my_globals.axis_ping.clear() # clear before graphing
g_my_globals.axis_ping.plot(x_array,y_array) # graph the ping values
#================================================================================
# Function: Set graph titles and Axis labels
# Sets the text for the subplots
# Have to do this in 2 places... initially when creating and when updating
# So, putting into a function so don't have to duplicate code
#================================================================================
def set_chart_labels():
global g_my_globals
g_my_globals.axis_ping.set_xlabel('Time')
g_my_globals.axis_ping.set_ylabel('Ping (ms)')
g_my_globals.axis_ping.set_title('Current Ping Duration', fontsize = 12)
def draw(fig, canvas):
# Magic code that draws the figure onto the Canvas Element's canvas
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(640 / 2, 480 / 2, image=photo)
figure_canvas_agg = FigureCanvasAgg(fig)
figure_canvas_agg.draw()
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
return photo
#================================================================================
# Function: MAIN
#================================================================================
def main():
global g_my_globals
# define the form layout
layout = [[sg.Text('Animated Ping', size=(40, 1), justification='center', font='Helvetica 20')],
[sg.Canvas(size=(640, 480), key='canvas')],
[sg.Button('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize()
canvas_elem = window.FindElement('canvas')
canvas = canvas_elem.TKCanvas
fig = plt.figure()
g_my_globals.axis_ping = fig.add_subplot(1,1,1)
set_chart_labels()
plt.tight_layout()
while True:
event, values = window.Read(timeout=0)
if event is 'Exit' or event is None:
break
run_a_ping_and_graph()
photo = draw(fig, canvas)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,66 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 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():
background = '#F0F0F0'
# Set the backgrounds the same as the background on the buttons
sg.SetOptions(background_color=background, element_background_color=background)
# 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
# define layout of the rows
layout= [[sg.Text('Media File Player',size=(17,1), font=("Helvetica", 25))],
[sg.Text('', size=(15, 2), font=("Helvetica", 14), key='output')],
[sg.Button('', button_color=(background,background),
image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0, key='Restart Song'),
sg.Text(' ' * 2),
sg.Button('', button_color=(background,background),
image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0, key='Pause'),
sg.Text(' ' * 2),
sg.Button('', button_color=(background,background), image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0, key='Next'),
sg.Text(' ' * 2),
sg.Text(' ' * 2), sg.Button('', button_color=(background,background),
image_filename=image_exit, image_size=(50, 50), image_subsample=2, border_width=0, key='Exit')],
[sg.Text('_'*20)],
[sg.Text(' '*30)],
[
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15)),
sg.Text(' ' * 2),
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15)),
sg.Text(' ' * 2),
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15))],
[sg.Text(' Bass', font=("Helvetica", 15), size=(9, 1)),
sg.Text('Treble', font=("Helvetica", 15), size=(7, 1)),
sg.Text('Volume', font=("Helvetica", 15), size=(7, 1))]
]
# Open a form, note that context manager can't be used generally speaking for async forms
window = sg.Window('Media File Player', auto_size_text=True, default_element_size=(20, 1),
font=("Helvetica", 25)).Layout(layout)
# Our event loop
while(True):
event, values = window.Read(timeout=100) # Poll every 100 ms
if event == 'Exit' or event is None:
break
# If a button was pressed, display it on the GUI by updating the text element
if event != sg.TIMEOUT_KEY:
window.FindElement('output').Update(event)
MediaPlayerGUI()

View file

@ -0,0 +1,82 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
def ShowMeTheButtons():
# ------ Menu Definition ------ #
menu_def = [['&File', ['&Open', '&Save', '&Properties', 'E&xit' ]],
['&Edit', ['&Paste', ['Special', 'Normal',], 'Undo'],],
['&Toolbar', ['---', 'Command &1', 'Command &2', '---', 'Command &3', 'Command &4']],
['&Help', '&About...'],]
sg.SetOptions(auto_size_buttons=True, margins=(0,0), button_color=sg.COLOR_SYSTEM_DEFAULT)
toolbar_buttons = [[sg.Button('', image_data=close64[22:],button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0,0), key='_close_'),
sg.Button('', image_data=timer64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_timer_'),
sg.Button('', image_data=house64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_house_'),
sg.Button('', image_data=cpu64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cpu_'),
sg.Button('', image_data=camera64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_camera_'),
sg.Button('', image_data=checkmark64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_checkmark_'),
sg.Button('', image_data=cookbook64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cookbook_'),
sg.Button('', image_data=download64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_download_'),
sg.Button('', image_data=github64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_github_'),
sg.Button('', image_data=psg64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_psg_'),
sg.Button('', image_data=run64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_run_'),
sg.Button('', image_data=storage64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_storage_'),
]]
# layout = toolbar_buttons
# ------ GUI Defintion ------ #
layout = [ [sg.Menu(menu_def, )],
[sg.Frame('', toolbar_buttons,title_color='white', background_color=sg.COLOR_SYSTEM_DEFAULT, pad=(0,0))],
[sg.Text('', size=(20,8))],
[sg.Text('Status Bar', relief=sg.RELIEF_SUNKEN, size=(55, 1), pad=(0, 3), key='_status_')]
]
window = sg.Window('Toolbar').Layout(layout)
# ---===--- Loop taking in user input --- #
while True:
button, value = window.Read()
print(button)
if button in ('_close_', 'Exit') or button is None:
break # exit button clicked
elif button == '_timer_':
pass # add your call to launch a timer program
elif button == '_cpu_':
pass # add your call to launch a CPU measuring utility
if __name__ == '__main__':
house64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAF50lEQVRIiYWVX2wc1RWHf+ece+/szu7a47Vjx+s42CRA/hASAQFCEgcTgkjAjVryQFNXtJUqFfJQqe0DbZ+KKvEcVU1VpAYa+idSq1IKFFTVgUBccKAJSYkViC2TxCZZx2uv7V3Wu56Z24fZNU4aykhXGmnune9+v3N0L/AlDzEDAC/JZPDS/v1bsod++7M9u3cnAUCJ0Jetl//3kYnIWiuu54W/ePKJrV3DIwcnXnn1a11bu+KX6+r6Bs+eDYmIAFw7EIvFKJlM8hcCmBnWWhZjwj88/fS9D50bfqH/9ZfaBsq5ibaPPtmx6/7ulmE38erQuXOWKRJREv3fAojH45xKpei6ACKCtZabMpnw+R8/1dV95Ohf33y7LzW8LTWf2FTvDQ5dydW9eaqrZ3v30nwm8974TPHb8VjdrkKhsEk75sEg8I+JSCAi/wtYiCWdDn/5rccf2nni5AvH3u93L25vDNdvu8Fb1d7K0/WhPjdemHTfOrl16+13ZG7rufv+W9p574ab0tuD0PJYNv9cMpm0nufJVYCFWOLx8I8//MEDO//17sHj/Ucbzj/aMX/nfcu9zuYMnHgSbU0xKTSTHhotzKijH9x6g5nVD3x9nfPIfTerDz8afea9wcvvl8tlmpqaCtXiWMIw5KZly8Jf9e7d0f27w38ZmPrUXnx8bXn5inpv5FIdLs1YGH8KFeXZ1kTFyGNO6sIrF/P5F4+3FGdLvPknXwVMLA0ATU1N3NLSEhV5IZbGxvDArp27H/7HPw+dmByT7N5bg7VbOrxsVuF5vxctG7+BN05fwgdrfk7rVRY3t8xJsDQu2aLvF45+rFS+RBdSDX9/++TQO77vU6EwGwozk7WWxHXDw729PY/0HXn2dPZC4tPvbvRX3NPhtTUtQ25iBqpcwio3j/riEO5p9XFj+RQSDR7S6ZSybUpPTPnFXN+gWellMNnZ+efzo6NBZmmrklq3HNqz5ys7f3/4T/+hEmef3OyvvKvDW+K1QZTG5VwJL8tuxFd349hYgA+XPIq73AtI6RmIU2/TqQTplQmaKFGucuTf63esXr1uMpPpGzhxYla8pia7/95Nj+3pe+PgGVWxk9/bHLRv7PAaU60gHYMii9x0gPrOTdiyKgFz5WPcvmYV1pcHAKqAdIy0E0d9IiZ6uauuVChXev2dO+7u7Owotbe/RU/19Gx4ZnTsxbPDg61jP314rvW2ZfUNiWYQKwAWREC5UIQjAsfRoPIsyCSB8gxKbhrWAhYAgTA3N4Wx8fHKmd8M5KXvTPPaffsOSEtb21wq5mSGNjevuGXHusYGt4XYuCCSCEIKM8U55D+bQ75YQd5nTBXnkPcVtIlBm1h1LkPrpHUNK789Redn1fFxN31IvdzfP/038PefaNsg23R8nziuZRICRa3r+wGe/fVhTI1nobWCDUMABD+0+OZ3enHnxnWoVCogEIjFBkWhlTfeVHxtNf1o/4Hn3lVB4HMQhEEIzivtQMSAWQOwYCIEoY+gOINEZRocEmAtCEChAlT8EErFEAQEIgKRgJWGk6ifDwOaBAAFWzsiWEQ0SEw1/8iAQkY8ZsBJBZKoLgwAcxaiTDRf7OcAMWBisgglAtQIQAhisDgQqRowQUKBUQw3rhYKL2QRIASzgigHEmABQJ/fALYKWHSKgqIdiAEQgplBwnCMQrMxoGp0IMK8nQexBosDFiwyuPr8VFfhiEDVmCIhBgnBKIWkdgBWMBzik4KDXOUzKJFFEQFECqAvANQcWAxYG8BWDXyCoxW8pAFV76c1MYsEEcAGrAw4iADMGrQAoGsBkbqIA2GnGpFAhGG0IOkQQARrAaMY0yUBiQJLDCKIDLjWIMH1DagWkXIAG4JYQAI4WuC5GiCBBaAZSDgqqolyQP4iA2ZY68Pa8HoRMZgNRMwCgNlCaY2GlAsihrWAVoRUwYJZAWwgEkYGYmqFtlqbawC1biWORu2dGT40ZoK4BTMsABUQKmGZ3Gjb1TVR7o4Tw8jISHDy1OkyAPwXWfQkSWcWg6cAAAAASUVORK5CYII='
timer64 = 'data:image/png;base64,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'
close64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAE30lEQVRIiZ2VXYgdRRqGn6+quvucM/85iRoTNevMBJFEWY0GFQTBC1HBlaz/jMpoFFfXBdmFvdiLvRIEFRHFGBXMjUQhF/6Bol6sSNaIruCNir/R/Dlx5iRzck736e6qby/6JDlx9CIWFN10Ue/7vW+9X7XcDn8bryWPL2vERkNQQPj9Q72K7F3s7Hxb9bZ98L0bj91jt1y23kxNTxIEGUQ/aTYR6WW9cud/Prx01zf7/7FP5EHXHG7Y6bVTpBPLMSegCWKEEMKvkihgjEWDP+FbEjxTa1bjv9l/CsIKF3ypHhUDSFGACCKC956iKKjV6/hfkCjgUNK0TW1oCA3h+EJk8UUBYFCsQaSyRajArUWLnEONcTrT68nTLtZaEKmmMTiUlsREGy9HO0dgcL1y6lgtZrAsEYFexhwxq2buYfru+1mcOo+828UYg4rgUH7OSkY3zbDq1lkaV1yFP9TqEyy18jiBCMF7DjYmOOu+hxifnCSKItZuvp/F6fPJ05TEwE+dHhN33MfpGy4iFAVjf7qF8etvBV9y1IilBApGIMt6TExOM372JKqKqhLFMdOz93Jk6jx+bHVoztzLyj9eiHqP2Gq7O3UlGAuq1RwYDlUwhoChMdSAz3ZxaEeD8T/fBggaAnGtxpqZWdKFBSbOPLMCCQGJItJPdrHw4lOYRgNsBM6dSCDGErIuodtGkhoyPEr68U5svcbI1ZsQY0CV2vAw9ZGRKjEiSBTR/fQjDm9/AddcjqoSul182kYHVDhJauRffUH7wD7ilatxzVOwI6PM7XiJLO2x4rob0CgGVTSEKigidD94j/ltW9Dg0b0/4BfmyQ8ewKUdWLZ6wCIB9SXFXJvQ+hLkc6QeEznHf199jY1rpjh1w0ZUFTGm7z18/tSj2Hffor5shKLdhhJCADMcw7IlKRIkAqkJRIa4LPl6d5c/PPJkBd5vpArcArD+ue101l1Md08bFxuIBUlOyOUggUIAVIl94Kv5wKqtz7L+7r/0bRHEmApcFbwnHhljw6tv0b3kEtK5gDWmj/GbfQAWZbdaztjyPOfP3oN6D8GDCO133uDAvx9CyxKsRX1JMjbBBa+8Rnbl5RSpR35RfXUGfVLnYGFBcTfdwLo77yLkPYy14CLa773JngfuoNy7QOh2WPnw09WVkufUm8s598G/s+eT9wmBJZ1m+sVTFNBc4Wi8vJ3v//kAJk7AOhbf3MGezTfjWwuYCcv8s1s58K+/okWOxDGdjz5g7+YZtKRSoL+igCp5FKVntGk48sTTzDWb1C+4mB833wgETD2CELBjEfNbtyAjo4xdcz27N11L6B5GGoZQhN+26KiSoII9LebnJx9BkggzNIQkyfEdItiRQGvbM7S2bQHJMGN1NO8ds2dQhBORYBCjAFEE1kFSw0QxuAiTJCAGce64vz4gviTkOTJcErIMMRbyDIxg7bHTFnc47clcmpdj43VkeBRJEkytgdTqSL2OiRMkSRDroH9t4EtCUaBZhmYpIUurZ9pFfVnuX+w62xfjeq3D3/6vbifXrT1XkzgWdREmipA4RlwMUYRY21cg/X+lJ5gSbIHGOVovCHmOCSX7DrbMx599icIhVI2cA5c5mC1gbGnITm4oqAOr0PoOXs9g51HAGiITyCDByXDp4KuiaoESmP8/YC0Y5GajmEsAAAAASUVORK5CYII='
psg64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAABlbkiVhkyZikyFjmShjkyhlly1mli5nlylnmS5olyppnC5qmi5rmzBpmDBpmTFqmDFqmTFqmjNrmzFrnDNsmzJsnDJtnTFunjNvnzRsmTRtmzVunTVunjVvnzZwnjlxny5toDFvozJwoTNzpjVwoDRzpDRzqDd4rThyojp0ozl1pTt2pjt3pz11ozt4qDl4qTp6rTl6rzx4qD55qTx5qj97qz17rT58rD98rT9+rz5+sEB3pEF9rkB+r0F+sEF/sT2AtD6AtT6Btk+Aqk2CrEOBs0GBtEKCtEGCtUSBskWBs0SCtEWDtUaEtkWFt0aGt0KFuUCEukOGu0eFuEWHu0eJvEiGuUiHukiIuUiIukmJu0uKvEyKvFCAp1CCqlODrVKCrl2Kr1OEs1KGsFWGsFaIsVaPvFqKsl6NsmKNsWeTuG6YvHadvlySwV6UxF+YxW+bxW6dxnKewHGex3SdwHSfx3egwXGizHmgwHqkxnyjxHio0f/RMf7QMv/TMf/SMv3SNf7UOv7UO//UPP/UPf/UPv/VP//WPP/XPv/VQ//WQv7XQ//WRP/XRf/WR//YRv/YSP/YSf/YSv/ZS//aS//ZTf/aTP7aTf7aTv7bT//cT//bUP/cUP7cUv/cU/7cVf/eVf/fVv/eV/7dWP/eWP/eWf/fWv/fW//fYvzcaf/hW//gXP/gXv/gX//hYP/hYf/hY//iYP/jY//gZf/iZP7iZf/jZv/iZ//lZv/jaf/kav7ka//maf/ma//kbf/lbv7mbP/mbv/mb//id//mcP/ncv/nc//ld//ndv/meP/ocf/ocv/oc//odP/odf/odv/peP/pff/qfY+11ZSzz5G41qC81aW/1P/jgf/qiv/qjv7qoMnZ5szb587d6eDm2+fo1+7v3e/x3vXw1fHx3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJblQd8AAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAABcQAAAXEAEYYRHbAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjFjKpxLAAABzUlEQVQoU2P4jwNAJZIEuLJA9M2u/iNgAaiEELOgIFPc//+r6puaalvAQmAJdg4pPj4h5oT/2+raWtqaGmAS/PxC/IJAGdbEB7saW5pb20AyQAkFNiFhUSEgkGZNfjizua29u70XJJHr8j+eV0RGVkRMTJb56u2mvt7eSR0gCT2gPsbMGzbi8hJyPDl3OidPnDRlwhagRHbG/zTXe5WSqqqqmpzXb/VMmz5jztSVIDtSWFLvl3Jrampq8ZY8WThj1tx586ZCXFV9t1xRR1tbR6Lw0f6ZC+YvWDAb6tz/xUom+rrGymWPD8xaunjZ0oUgMZBEsYqZqampWsnTY/PWLF+xZhFIHCRRpW5raWFhUPT/3IJ1a9euW/H//5oTYAlDezs7Kwvv//+XbN6wcev6//+3/z8FltDwcrC3N8/7v3rHtu07Nv3/vxVo0CWQhJGPm5ubdf7/TXt279699//JnTA70j38fH19wv//33b00OGj+w6fPXz5KMRVTiH+/gHuFf//7zl+5szZs2fO7YPo+H/FOSIyPMqz5v//g+dAMocvQCX+XwsMjYmNdgSy9p0/d/bgRZAYWOL//4LgoDAwY+++02AaJoEJcEj8/w8A4UqG4COjF7gAAAAASUVORK5CYII='
cpu64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAFi0lEQVRIiZ1WS2wbRRj+/5l92O6u7WycTW03xLEd6uBUjVO1hfbSByLQEwhxoIhbCwcuvApUiEOFhAQVEiAOvK4gqoojSEhtgUNApaJKG5qkdWpa1JfcZL3teu3sZmeGQ9ZVGhIJ8Z/+2Vn93//45psBWMMqlcoXxWLxEACAaZpju3btOkkIoZRSsnv37hOmaY4BABSLxUOVSuXzteJIq32klMqyLBuqqhoAAKqqGpTSpKIoSUQESZK6OnuKohiKohiUUpkxtvivWCvWBABEOp1+sr+//5V169ZtnJub+6FUKh3Rdf3hVqv1l6Zp5Ww2ezASifQ0Go3fhoaGjsZisYdardaM4zjTiEgAQHQC4j0HkQghAAC4oiiJRCKxBQBIs9m8oOt6iRASa7VaVwEAYrFYP+e85TjOpXg8PiyE4LZtn/F93wYAgogghOD3AYS+UFW1q1AovGoYxp4wGxIEgS2EEIQQCQCAcx7gkslCCB8AwLbt07Va7SPP8xqdWPdmIElSxDTNfZZlncrn828MDg6+VavVPvF9fy4Wi/X19fUdWJHMfSaEYJlMZgwRpVqtdtQwjD31ev2HIAgWpJGRkS8VRTEMw9g9OTm5v7u7+9GpqamXq9XqxwAAmzZt+oBzjpzzYC0QIQRDRJpIJLanUqmdw8PDX1mW9ZPv+5bkOM5FVVVTiURia1i24rruDQCAUqn09sDAwCHGGEdEadnwlgOJZT5BRMIYc5rN5iXP8+ax0y9N04qc84Vt27aduHjx4uuEED46Ovo95xxEOH1ExKWEhQh9DPe4JEl0fn7+14mJiecQUWo2m7MAgNQ0zb3d3d3bhoaGjrTb7Wld1x/p6uoa2bBhw4uyLGsAEFBKKSIi51xQSjFcIiICIQRDAhDXdWue502Vy+X3hRALqqr2SoODg2/KsmzE4/GNlNJ1nPOF9evXPxYEAbiue7lWq72rKIphmub+GzdufBeNRg1d14cZYx4hhBJClFQqNRbOQlBKo8lkcms+n48vLi5a0vj4+OOKoiTT6fQzjuNcJYRIQRCALMswOzv7LSEk0tPT85TjOBeCIKi12+1rtm3/ruv6FgDgAMB7e3vHgiAAQgh1HOfquXPnXr958+Zx3/dtshopltp7nyEiUtd1rxuG8URfX99B13Un2+32rKIo3ZzztRgMdOfOnT/mcrkX+vv79zcajVOapm3XNC3HGINoNNpnWdZJz/P+TiQSOzRNK6bT6WcjkUh/q9WaQUTIZrMHEFEjhECz2fzL9/2ZkZGRz0zT3JfNZp+WqtXq+5FIJJXL5V5kjLVDdgDnnMVisYFyufxVSFHgnO9gjDFElIvF4jth34ExxgCAIiIyxtq2bZ+5cuXK5wsLC3NSvV4/BQDCsqw/hBBBLpeTO+WF/KdhC0TIHAoAIggCjogYMnjpEBAi27Z96ezZsy90aCoVCoXXVFVNZbPZ/TMzMy9xzr1ljSdhYLHicN0DCkFYWKFnGMamUqn06fXr17/xPG9e0nV9Y6jnWqiAPCydrTm5laxY+pcCABdCcEqprmnag4qiWNLExMTBZWI3Ho/Hd2Qymb1CCBpm+V8AQJZluHPnzum5ubnx8+fPH+iI3apync/nX04mk9vDXihCiMX/K9drXTjJZDK5FRHJ3bt3/9R1/cH/e+Esb0FnkKK3t3ff5s2bv+7p6Rm7devWsXK5/GGhUDjsOM5kNBp9oFKpfKNp2kC9Xv9xdHT0eCaTed513fPhlYmd4CsBOiDQarVmu7q6KpZl/XLt2rVjQggvHo8PTE9PH242m1PpdPrRy5cvf3L79u2fo9GoyRi7U61W3wsDL5fv1V8VjLFF3/ct3/ctAADP86wgCBq+7zcAABljtud5FgCA7/uWLMvWai8KAIB/ACsf4Gh+DNwbAAAAAElFTkSuQmCC'
camera64 = 'data:image/png;base64,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'
checkmark64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAD2ElEQVRIibWVe4hUVRzHP+feuTOz82gfrqwu5gM0zTQpS1yfoGQPyc3IjOwlYRElPcDICEP6Q6i/IvojCCIpTA0tSlSqJSvbTLZE0dbWxz4nXWdnxrk7M/feOY/+sHyEa7pt57/z45zP53x/B84R/B/jzeNLgeXADDHE4IeBV2Ihpo6LK1rzNqEhAW9oGw18UhelYeWUGHFL0tx5lqPZyBAI3mi9o8YRm16cWTlsVFLQVwjY2+4SSI2W+j8KXj+ybmwytO69xjo7Wyqzr8sldbaE60ksIdBlhTVo+KuHXppY5azftKzeNsbQkurntOuTKQQobYhFbCgPNsGaA5NDWm94ZlYV7fmAX3pcenIlTucDAqlJRENUxcJQLgwiwfMtYcpq4503JMJjq8M0d+XpyBRJnfUpBpJwyKYqFqbCcSCQg0gQyCeq4qHp90yr5Pd0kY6+ImnXJ1CaeDhEdSJCTSJKzLEHLXhu4oQEuWKZ79uzZAoX2hKPhOn+I6DtuEdfLriC4NE9L4CYhzEP8dH84Hz9kT0NBHLqvMlJmo5nyBQDylITj4RwM5rmw70orcEA0AL8Q/DgN8OBr/DltL8q64G1F52+obomwr6US7boE0hNhRPiVIdHx7H+EvA2sJ0tC3/+e8uFS27c/SS+7ElGrGkbnp5EfV0UArmGxt0Lzq/x5YzKWocz/T4FXyGEINvj0XE410QgJ7Fl4dqL4ecS3PVlJYgdllKzx04ZxqolY8h4mkm315JPl+z+nP8Bd++4hZ2LM/hyuokLCr7Eti28TJnOA5ndGLOUnYtLl+u2YMHnJ4BxY2bWsWj2SA72eoBBG4PnBvy2qwvpq81gVjhJp1Q7q9axLIFVMqSaz3ytfLWEpsbLwgFs6pc1o/R9+e7+eK9joSMWvjR4gSLA4FSGKLS7UyirUmRkbJFTG0VI6N17+oR0/bl8d/+A8HMJAG7bPB7BTmGL8TVz64mMiKGNQSuN0hqvq59CS59Kzq2zo8MrcH/s1V6qMIf9y5uvBL8gALj54xpgG5aYH589klB9BdoYjDY0XJ9k9HURPj2aRZ/ycL/tfouDK17+N/ilAoAbP6wAsRGLB8INI7BGJUAYLGEhLAtLCApfnDymc95NtD4eDMC8ZNiXzNKfSdLbt5K8N6o68nNMwoHqKCAwlkVwKI06ln2MtpWtVwMHBnjspHyNQO1Xe7pRbTmUEchCGbk/laKsdl0tfGBB51OKQM0hUD/ppk7kkTTy11NQku/TuUpdi+DKn/7wdyuAHzDcii0Uykwg/ezJoRMAVL9TCWwFjpJdvfpa4AB/Akx4zQw8GDagAAAAAElFTkSuQmCC'
cookbook64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAIAAAB1KUohAAAACXBIWXMAAAsSAAALEgHS3X78AAADMUlEQVQ4jY1UQUgCWxS9782M5hQzle3NTRGEQdBipCKKECoEg4JaJAqCmyBsYauEaNOqjQtXLSKINuFiKMiwIMtF1iLK2kWRZLUoDRxo5s37ixfSt4+/u7xzz51z7r3nwc7Ojt1uBwCEEPwtWKXL5eIvLy+HhoYIIYIgCIJAKa0Do5RyHPfy8nJycnJ1dcUjhJxOZygUOj09zefzFoulDp5SihCKRqPLy8vJZBI4jgOAo6Mjj8cDABjj/6WdTqdDoRAAfJeyFn8MQohhGADAY4xFUSyVSpIkAYBpmgih+soRQmxm2GazbW5u7u7ujoyMKIrCmP+ePMdxv9nhSqXi8/lmZmb29vay2Syrs1gs8EM/QogQQgipBWOMOzs7397eWlpabDYbAMiyHAwGu7u7mQTWzu/3R6PRxsZG+HERvNVqjcVix8fHfX19Nzc3T09PHo+HUjo1NVUulx8fHwFgbW0tEolQSguFwtbWVpU/rlQqs7Ozc3NzqqrmcjmXy9Xe3m61WgcGBubn5wGgo6NjYWEBAEql0t3dHQBUx8ljjNva2orFYnNzM8/zBwcHFoslGo329/cXCgUA6OnpwRh/fHwsLS3lcjm2qm9wQ0NDPB7f398fHBx8eHjIZrOqqhaLRUmSwuFwPB53OBw+ny+dTn9+ftYujed5AEilUhMTE9U9saTX66WUJhKJmv0dHh4Gg0FgF4YxJoQwANNjGIaiKLFYbHp62ul0Li4umqb5H5crSVIymQwEAolEwu12s6SiKNfX15OTkwDgcDguLi4ikUgVUv0zCIJgs9lUVWWlrP3q6qrf72dfAaCrq2tjY0OW5RowTynVNM1qteq6XqW9srJiGAZCSNd1hNDt7W04HGZm+NeFiaKYTCa3t7fHx8fdbjez+9fXV7UR87Cu66Zp1oI1TQsEAl6vN51Os9smhCCEfpbWmMw0TZbBpmm+v7+3traWy2VKKdP825I/M7Isi6IIAFxTU9P6+nomk+nt7X19fX1+fsYY1/ez0+k8Pz+/v7/nMMblcnl4eDifz5+dnWmaVgfGolQq2e32sbGx7wcok8mMjo7C396wVCpFKSWE/ANWXYLwO0+V8wAAAABJRU5ErkJggg=='
download64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAEl0lEQVRIia2WS2hdZRDHf/Odc+7NvbZpYky1Gk2rEbRa26pt8UWLitYHIqigCD426kIQxCfWhYu68U0RBBERXCq+WrXoorYIFWpbEY0lpk0NTWKtuUlzn+d834yLm5smqQspzurMcM785j+c+eYTTtUcESAzvhIAm/+azHacA4FodszAVNFTrUPm+Q6iecmUyJkEna5OiDCCXCtvJ2cnV+Ep+9R7/VIfQhmeryKeySywok+SSMMKMwqAihDXPIcPDDMURUQCgiPWjJCck6x87ZXXV3cXu3XTO5tkYOvAHbnIfZipTpghLdAMIEngi1cXvtlzwfrHpG0x5ismzsvo0E9D9z7z++M799s2EcSm67OUkAs5cpbzkkoMtPtAAzdXQ9zqjHkt1Ol5SHofx0KWYRUxrdiS3FlLtzz51wd7+v2OQl7qHnPtorUXS3ZxPRUKUT5x4mTDWu559LbCNS+9X9v025Duc4KoYdMAA7A4Mk92EMp/JFIZwR/rx9dL1teVdC2/Qe8yzQg+pS0JvLUzx3hjioPVQamGGlcu47KNq6qrPj+fsd+GeAEYA2SmRQiCNSJKP1Ad3IVaG0nnlWRxKqkkVlYxJxGZwhmFIo34U/fh0Hv4v6YYrY+ihYtkorDUNj+298GPvzv6ZRrkMzA/oyCXh9rEMOOHfiLfcx+5zhXkOnppswxEpJHVxdTjs0CycDHy9XcMlwc5a0E3EoTconOls/dyBsb6lYRLY4m/9T6blDgi8oHw3rPx83fesubl4oVPWFvXBUKoQzqB92Xitpite77n/k/epaN7AZO1CTIROtZ14fJC6ccS9ndGUhRLK0Eum1h2YGpH5eFfD47sjluzcFo+f+vp655F03alNhZhASMjloA1qtzedzab125kiw2QLhHaQ0zIFM2MztUdkBcqx1Lp+0o59NGRP49OVQs0Z3d6nEyMUMP8OGgVtAJaA19CagP4xn4e6DPuPhox1V9HTRFr/h9mRmWkwbJtGSsHK4xXq4cQGQDCDABM0ClEy6DlJiA9DLV90BgktirFzhrPXX0mT6Y9lAaqkAhRItRKGT3bjetTYd2aYM7JYcwm5wwaAP44hDyQYukokg5jliICZoFIoNjZ4Ol1HdhueOPgCLlFjt7twvo63HwztGuipml20lEBBlrGfBXzR5BsDGjOPBrAAkJKRKBwuuepNUXyP5/HN7tKXFGvcuMGY/3qhAO/NLCTJ7kFmIT0OPgjmAhiYKYIASFgGoCUyAILu+o8ckng0jSwsF1YuzxP0hYwm3tizwIIpKPQOIY4BXUYCiiYYWSIKYYHMoRAV1fKTddFxJKQOA/mmW9zFWRjoCmYw6R1lrcg2kxgAfCIeRxKMa+YBSw0Vc7fOScAZuAnMXWYE8yaIUFBDFSbS8sCgscsayZWD3jMAmhT7b8CnDPIeZw6RGTOLmwWFRALMA3BZvkamoBcwM3Zh7MA9Yb5I3v/YKoKTlr9sROKZVrlTGDWsylmkMTGxCQ4h0ObGaT1aRJzHsbtwJJmWSet0/9kIpB69gPbgersJA4oMm/pn6JlQI1/uWX87/YP06p9rkZQnAYAAAAASUVORK5CYII='
github64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAADAwMDQ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhyjGAAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADdUAAA3VAT3WWPEAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAABzSURBVChTbYxRFoAgDMPQ+98Z1zbIeJqPbU3RMRfDECqyGpjMg6ivT6NBbKTw5WySq0jKt/sHrXiJ8PwpAAVIgQGkwABSYAApMIAUGEAalFmK9UJ24dC1i7qdj6IO5F+xnxfLu0jS0c7kqxd3Dk+JY8/5AKFrLuM7mfCAAAAAAElFTkSuQmCC'
run64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAszD0iAAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADdUAAA3VAT3WWPEAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAABqSURBVChTpY5JDsAwCMTy/09TMGvFpVF9aAZPRHpkcXC7OIodPg0uCjPq+MwCrWRGKkiIvLyTqzw3aqoI73eqUNAoXBXlg4zudxF+NONfPIVvbSZPgww5oW0Vz8T4Lgbt/xbjia+rahR5AEYEg4vdzh2JAAAAAElFTkSuQmCC'
storage64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAABwcHDQ0NDg4ODw8PFxcXGRkZGhoaGxsbHh4eIyMjJSUlJiYmJycnKCgoMTExMjIyNTU1NjY2Nzc3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAouNksgAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADdQAAA3UAe+RuhUAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAAC5SURBVChTfZLbDsMgDEPpbb3TDv7/W7PYuAztYUeqhO2QAGowkXIMIeYkaSU4QsNBi4GcyhNINpTglmq4GWSphvy/ldkuLXZ4HmAxy3NmFJaA4guKGCwsjClfV05+fWdhYBtFw+amB292aygW3M7fsPTwjmadZkCvHEtWaAYTViBqVwgTA3tJVnB6D/xhaimItDhjMBvlhtFsaIafnEtOaAY/twAw/eslK70CbX8obUvgJNw9Jv0+Zh8D4s5+VAm/LwAAAABJRU5ErkJggg=='
ShowMeTheButtons()

View file

@ -0,0 +1,64 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Demonstration of MENUS!
How do menus work? Like buttons is how.
Check out the variable menu_def for a hint on how to
define menus
"""
def SecondForm():
layout = [[sg.Text('The second form is small \nHere to show that opening a window using a window works')],
[sg.OK()]]
window = sg.Window('Second Form').Layout(layout)
b, v = window.Read()
def TestMenus():
sg.ChangeLookAndFeel('LightGreen')
sg.SetOptions(element_padding=(0, 0))
# ------ Menu Definition ------ #
menu_def = [['&File', ['&Open', '&Save', '&Properties', 'E&xit' ]],
['&Edit', ['&Paste', ['Special', 'Normal',], 'Undo'],],
['&Toolbar', ['---', 'Command &1', 'Command &2', '---', 'Command &3', 'Command &4']],
['&Help', '&About...'],]
# ------ GUI Defintion ------ #
layout = [
[sg.Menu(menu_def, tearoff=False, pad=(20,1))],
[sg.Output(size=(60,20))],
[sg.Text('Status Bar', relief=sg.RELIEF_SUNKEN, size=(55,1), pad=(0,3),key='_status_')]
]
window = 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)
# ------ Loop & Process button menu choices ------ #
while True:
event, values = window.Read()
if event is None or event == 'Exit':
return
print('Event = ', event)
# ------ Process menu choices ------ #
if event == 'About...':
window.Disappear()
sg.Popup('About this program','Version 1.0', 'PySimpleGUI rocks...', grab_anywhere=True)
window.Reappear()
elif event == 'Open':
filename = sg.PopupGetFile('file to open', no_window=True)
print(filename)
elif event == 'Properties':
SecondForm()
TestMenus()

View file

@ -0,0 +1,46 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout1 = [[ sg.Text('Window 1') ],
[sg.Input(do_not_clear=True)],
[ sg.Button('Read')]]
window1 = sg.Window('My new window', location=(800,500)).Layout(layout1)
layout2 = [[ sg.Text('Window 2') ],
[sg.Input(do_not_clear=True)],
[ sg.Button('Read')]]
window2 = sg.Window('My new window', location=(800, 625), return_keyboard_events=True).Layout(layout2)
layout3 = [[ sg.Text('Window 3') ],
[sg.Input(do_not_clear=False)],
[ sg.Button('Read')]]
window3 = sg.Window('My new window', location=(800,750), return_keyboard_events=True).Layout(layout3)
while True: # Event Loop
event, values = window1.Read(timeout=50)
if event is None:
break
elif event != '__timeout__':
print(event, values)
event, values = window2.Read(timeout=0)
if event is None:
break
elif event != '__timeout__':
print(event, values)
event, values = window3.Read(timeout=0)
if event is None:
break
elif event != '__timeout__':
print(event, values)

View file

@ -0,0 +1,98 @@
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import queue
import logging
import threading
import time
"""
This code originated in this project:
https://github.com/john144/MultiThreading
Thanks to John for writing this in the early days of PySimpleGUI
Demo program showing one way that a threaded application can function with PySimpleGUI
Events are sent from the ThreadedApp thread to the main thread, the GUI, by using a queue
"""
logger = logging.getLogger('mymain')
def externalFunction():
logger.info('Hello from external app')
logger.info('External app sleeping 5 seconds')
time.sleep(5)
logger.info('External app waking up and exiting')
class ThreadedApp(threading.Thread):
def __init__(self):
super().__init__()
self._stop_event = threading.Event()
def run(self):
externalFunction()
def stop(self):
self._stop_event.set()
class QueueHandler(logging.Handler):
def __init__(self, log_queue):
super().__init__()
self.log_queue = log_queue
def emit(self, record):
self.log_queue.put(record)
def main():
window = sg.FlexForm('Log window', default_element_size=(30, 2), font=('Helvetica', ' 10'), default_button_element_size=(8, 2), return_keyboard_events=True)
layout = \
[
[sg.Multiline(size=(50, 15), key='Log')],
[sg.Button('Start', bind_return_key=True, key='_START_'), sg.Button('Exit')]
]
window.LayoutAndRead(layout, non_blocking=True)
appStarted = False
# Setup logging and start app
logging.basicConfig(level=logging.DEBUG)
log_queue = queue.Queue()
queue_handler = QueueHandler(log_queue)
logger.addHandler(queue_handler)
threadedApp = ThreadedApp()
# Loop taking in user input and querying queue
while True:
# Wake every 100ms and look for work
event, values = window.Read(timeout=100)
if event == '_START_':
if appStarted is False:
threadedApp.start()
logger.debug('App started')
window.FindElement('_START_').Update(disabled=True)
appStarted = True
elif event in (None, 'Exit'):
break
# Poll queue
try:
record = log_queue.get(block=False)
except queue.Empty:
pass
else:
msg = queue_handler.format(record)
window.FindElement('Log').Update(msg+'\n', append=True)
window.Close()
exit()
if __name__ == '__main__':
main()

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,82 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import time
# Window that doen't block
# good for applications with an loop that polls hardware
def StatusOutputExample():
# Create a text element that will be updated with status information on the GUI itself
# Create the rows
layout = [[sg.Text('Non-blocking GUI with updates')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='output')],
[sg.Button('LED On'), sg.Button('LED Off'), sg.Button('Quit')]]
# Layout the rows of the Window and perform a read. Indicate the Window is non-blocking!
window = sg.Window('Running Timer', auto_size_text=True).Layout(layout)
#
# Some place later in your code...
# You need to perform a Read on your window every now and then or
# else it won't refresh.
#
# your program's main loop
i=0
while (True):
# This is the code that reads and updates your window
event, values = window.Read(timeout=10)
window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100))
if event == 'Quit' or event is None:
break
if event == 'LED On':
print('Turning on the LED')
elif event == 'LED Off':
print('Turning off the LED')
i += 1
# Your code begins here
# Broke out of main loop. Close the window.
window.Close()
def RemoteControlExample():
layout = [[sg.Text('Robotics Remote Control')],
[sg.T(' '*10), sg.RealtimeButton('Forward')],
[ sg.RealtimeButton('Left'), sg.T(' '*15), sg.RealtimeButton('Right')],
[sg.T(' '*10), sg.RealtimeButton('Reverse')],
[sg.T('')],
[sg.Quit(button_color=('black', 'orange'))]
]
window = sg.Window('Robotics Remote Control', auto_size_text=True).Layout(layout).Finalize()
#
# Some place later in your code...
# You need to perform a ReadNonBlocking on your window every now and then or
# else it won't refresh.
#
# your program's main loop
while (True):
# This is the code that reads and updates your window
event, values = window.Read(timeout=0, timeout_key='timeout')
if event is not 'timeout':
print(event)
if event == 'Quit' or event is None:
break
window.Close()
def main():
RemoteControlExample()
StatusOutputExample()
sg.Popup('End of non-blocking demonstration')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,40 @@
#!/usr/bin/env python
import sys
import time
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
# Demonstrates a notification window that's partially transparent
# The window slowly fades-in
# Includes a small red-X button to close the window
# Base 64 encoded button is in-lined to avoid reading a file
# Free online encoder - https://www.base64-image.de/
red_x ="R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=="
sg.ChangeLookAndFeel('Topanga')
sg.SetOptions(border_width=0, margins=(0,0))
bcolor=('black', '#282923')
sg.SetOptions(border_width=0, margins=(0,0))
layout = [[sg.T('Notification'+' '*14),
sg.CloseButton('', image_data=red_x, button_color=('#282923', '#282923'))],
[sg.T('')],
[sg.T('You have 6 new emails')],]
window = sg.Window('',
no_titlebar=True,
grab_anywhere=True,
keep_on_top=True,
alpha_channel=0
).Layout(layout).Finalize()
# Classy fade-in
for i in range(1, 75, 2):
window.AlphaChannel = float(i)/100
time.sleep(.01)
event, values = window.Read()

View file

@ -0,0 +1,61 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import cv2 as cv
from PIL import Image
import io
from sys import exit as exit
"""
Demo program to open and play a file using OpenCV
Unsure how to get the frames coming from OpenCV into the right format, so saving each frame to
a temp file. Clearly... clearly... this is not the optimal solution and one is being searched for now.
Until then enjoy it working somewhat slowly.
"""
def main():
filename = sg.PopupGetFile('Filename to play')
if filename is None:
exit(69)
vidFile = cv.VideoCapture(filename)
# ---===--- Get some Stats --- #
num_frames = vidFile.get(cv.CAP_PROP_FRAME_COUNT)
fps = vidFile.get(cv.CAP_PROP_FPS)
sg.ChangeLookAndFeel('Dark')
# define the window layout
layout = [[sg.Text('OpenCV Demo', size=(15, 1),pad=((510,0),3), justification='center', font='Helvetica 20')],
[sg.Image(filename='', key='image')],
[sg.Slider(range=(0, num_frames), size=(115, 10), orientation='h', key='slider')],
[sg.Button('Exit', size=(10, 2), pad=((600, 0), 3), font='Helvetica 14')]]
# create the window and show it without the plot
window = sg.Window('Demo Application - OpenCV Integration', no_titlebar=False, location=(0,0)).Layout(layout)
# ---===--- LOOP through video file by frame --- #
i = 0
while vidFile.isOpened():
event, values = window.Read(timeout=0)
if event is 'Exit' or event is None:
exit(69)
ret, frame = vidFile.read()
if not ret: # if out of data stop looping
break
window.FindElement('slider').Update(i)
i += 1
# let img be the PIL image
img = Image.fromarray(frame) # create PIL image from frame
bio = io.BytesIO() # a binary memory resident stream
img.save(bio, format= 'PNG') # save image as png to it
imgbytes = bio.getvalue() # this can be used by OpenCV hopefully
window.FindElement('image').Update(data=imgbytes)
main()

View file

@ -0,0 +1,88 @@
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import cv2
import numpy as np
from sys import exit as exit
"""
Demo program that displays a webcam using OpenCV and applies some very basic image functions
- functions from top to bottom -
none: no processing
threshold: simple b/w-threshold on the luma channel, slider sets the threshold value
canny: edge finding with canny, sliders set the two threshold values for the function => edge sensitivity
contour: colour finding in the frame, first slider sets the hue for the colour to find, second the minimum saturation
for the object. Found objects are drawn with a red contour.
blur: simple Gaussian blur, slider sets the sigma, i.e. the amount of blur smear
hue: moves the image hue values by the amount selected on the slider
enhance: applies local contrast enhancement on the luma channel to make the image fancier - slider controls fanciness.
"""
def main():
sg.ChangeLookAndFeel('LightGreen')
# define the window layout
layout = [[sg.Text('OpenCV Demo', size=(40, 1), justification='center')],
[sg.Image(filename='', key='image')],
[sg.Radio('None', 'Radio', True, size=(10, 1))],
[sg.Radio('threshold', 'Radio', size=(10, 1), key='thresh'),
sg.Slider((0, 255), 128, 1, orientation='h', size=(40, 15), key='thresh_slider')],
[sg.Radio('canny', 'Radio', size=(10, 1), key='canny'),
sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='canny_slider_a'),
sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='canny_slider_b')],
[sg.Radio('contour', 'Radio', size=(10, 1), key='contour'),
sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='contour_slider'),
sg.Slider((0, 255), 80, 1, orientation='h', size=(20, 15), key='base_slider')],
[sg.Radio('blur', 'Radio', size=(10, 1), key='blur'),
sg.Slider((1, 11), 1, 1, orientation='h', size=(40, 15), key='blur_slider')],
[sg.Radio('hue', 'Radio', size=(10, 1), key='hue'),
sg.Slider((0, 225), 0, 1, orientation='h', size=(40, 15), key='hue_slider')],
[sg.Radio('enhance', 'Radio', size=(10, 1), key='enhance'),
sg.Slider((1, 255), 128, 1, orientation='h', size=(40, 15), key='enhance_slider')],
[sg.Button('Exit', size=(10, 1))]]
# create the window and show it without the plot
window = sg.Window('Demo Application - OpenCV Integration',
location=(800, 400))
window.Layout(layout).Finalize()
cap = cv2.VideoCapture(0)
while True:
event, values = window.Read(timeout=0, timeout_key='timeout')
if event == 'Exit' or event is None:
sys.exit(0)
ret, frame = cap.read()
if values['thresh']:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)[:, :, 0]
_, frame = cv2.threshold(frame, values['thresh_slider'], 255, cv2.THRESH_BINARY)
if values['canny']:
frame = cv2.Canny(frame, values['canny_slider_a'], values['canny_slider_b'])
if values['blur']:
frame = cv2.GaussianBlur(frame, (21, 21), values['blur_slider'])
if values['hue']:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
frame[:, :, 0] += values['hue_slider']
frame = cv2.cvtColor(frame, cv2.COLOR_HSV2BGR)
if values['enhance']:
enh_val = values['enhance_slider'] / 40
clahe = cv2.createCLAHE(clipLimit=enh_val, tileGridSize=(8, 8))
lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
lab[:, :, 0] = clahe.apply(lab[:, :, 0])
frame = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
if values['contour']:
hue = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hue = cv2.GaussianBlur(hue, (21, 21), 1)
hue = cv2.inRange(hue, np.array([values['contour_slider'], values['base_slider'], 40]),
np.array([values['contour_slider'] + 30, 255, 220]))
_, cnts, _ = cv2.findContours(hue, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(frame, cnts, -1, (0, 0, 255), 2)
imgbytes = cv2.imencode('.png', frame)[1].tobytes() # ditto
window.FindElement('image').Update(data=imgbytes)
main()

View file

@ -0,0 +1,58 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import cv2
import numpy as np
from sys import exit as exit
"""
Demo program that displays a webcam using OpenCV
"""
def main():
sg.ChangeLookAndFeel('LightGreen')
# define the window layout
layout = [[sg.Text('OpenCV Demo', size=(40, 1), justification='center', font='Helvetica 20')],
[sg.Image(filename='', key='image')],
[sg.Button('Record', size=(10, 1), font='Helvetica 14'),
sg.Button('Stop', size=(10, 1), font='Any 14'),
sg.Button('Exit', size=(10, 1), font='Helvetica 14'),
sg.Button('About', size=(10,1), font='Any 14')]]
# create the window and show it without the plot
window = sg.Window('Demo Application - OpenCV Integration',
location=(800,400))
window.Layout(layout).Finalize()
# ---===--- Event LOOP Read and display frames, operate the GUI --- #
cap = cv2.VideoCapture(0)
recording = False
while True:
event, values = window.Read(timeout=0, timeout_key='timeout')
if event == 'Exit' or event is None:
sys.exit(0)
elif event == 'Record':
recording = True
elif event == 'Stop':
recording = False
img = np.full((480, 640),255)
imgbytes=cv2.imencode('.png', img)[1].tobytes() #this is faster, shorter and needs less includes
window.FindElement('image').Update(data=imgbytes)
elif event == 'About':
sg.PopupNoWait('Made with PySimpleGUI',
'www.PySimpleGUI.org',
'Check out how the video keeps playing behind this window.',
'I finally figured out how to display frames from a webcam.',
'ENJOY! Go make something really cool with this... please!',
keep_on_top=True)
if recording:
ret, frame = cap.read()
imgbytes=cv2.imencode('.png', frame)[1].tobytes() #ditto
window.FindElement('image').Update(data=imgbytes)
main()
exit()

View file

@ -0,0 +1,187 @@
"""
@created: 2018-08-19 18:00:00
@author: (c) 2018 Jorj X. McKie
Display a PyMuPDF Document using Tkinter
-------------------------------------------------------------------------------
Dependencies:
-------------
PyMuPDF, PySimpleGUI > v2.9.0, Tkinter with Tk v8.6+, Python 3
License:
--------
GNU GPL V3+
Description
------------
Read filename from command line and start display with page 1.
Pages can be directly jumped to, or buttons for paging can be used.
For experimental / demonstration purposes, we have included options to zoom
into the four page quadrants (top-left, bottom-right, etc.).
We also interpret keyboard events to support paging by PageDown / PageUp
keys as if the resp. buttons were clicked. Similarly, we do not include
a 'Quit' button. Instead, the ESCAPE key can be used, or cancelling the window.
To improve paging performance, we are not directly creating pixmaps from
pages, but instead from the fitz.DisplayList of the page. A display list
will be stored in a list and looked up by page number. This way, zooming
pixmaps and page re-visits will re-use a once-created display list.
"""
import sys
import fitz
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from sys import exit as exit
from binascii import hexlify
sg.ChangeLookAndFeel('GreenTan')
if len(sys.argv) == 1:
fname = sg.PopupGetFile('PDF Browser', 'PDF file to open', file_types=(("PDF Files", "*.pdf"),))
if fname is None:
sg.PopupCancel('Cancelling')
exit(0)
else:
fname = sys.argv[1]
doc = fitz.open(fname)
page_count = len(doc)
# storage for page display lists
dlist_tab = [None] * page_count
title = "PyMuPDF display of '%s', pages: %i" % (fname, page_count)
def get_page(pno, zoom=0):
"""Return a PNG image for a document page number. If zoom is other than 0, one of the 4 page quadrants are zoomed-in instead and the corresponding clip returned.
"""
dlist = dlist_tab[pno] # get display list
if not dlist: # create if not yet there
dlist_tab[pno] = doc[pno].getDisplayList()
dlist = dlist_tab[pno]
r = dlist.rect # page rectangle
mp = r.tl + (r.br - r.tl) * 0.5 # rect middle point
mt = r.tl + (r.tr - r.tl) * 0.5 # middle of top edge
ml = r.tl + (r.bl - r.tl) * 0.5 # middle of left edge
mr = r.tr + (r.br - r.tr) * 0.5 # middle of right egde
mb = r.bl + (r.br - r.bl) * 0.5 # middle of bottom edge
mat = fitz.Matrix(2, 2) # zoom matrix
if zoom == 1: # top-left quadrant
clip = fitz.Rect(r.tl, mp)
elif zoom == 4: # bot-right quadrant
clip = fitz.Rect(mp, r.br)
elif zoom == 2: # top-right
clip = fitz.Rect(mt, mr)
elif zoom == 3: # bot-left
clip = fitz.Rect(ml, mb)
if zoom == 0: # total page
pix = dlist.getPixmap(alpha=False)
else:
pix = dlist.getPixmap(alpha=False, matrix=mat, clip=clip)
return pix.getPNGData() # return the PNG image
window = sg.Window(title, return_keyboard_events=True, use_default_focus=False)
cur_page = 0
data = get_page(cur_page) # show page 1 for start
image_elem = sg.Image(data=data)
goto = sg.InputText(str(cur_page + 1), size=(5, 1), do_not_clear=True)
layout = [
[
sg.Button('Prev'),
sg.Button('Next'),
sg.Text('Page:'),
goto,
],
[
sg.Text("Zoom:"),
sg.Button('Top-L'),
sg.Button('Top-R'),
sg.Button('Bot-L'),
sg.Button('Bot-R'),
],
[image_elem],
]
window.Layout(layout)
my_keys = ("Next", "Next:34", "Prev", "Prior:33", "Top-L", "Top-R",
"Bot-L", "Bot-R", "MouseWheel:Down", "MouseWheel:Up")
zoom_buttons = ("Top-L", "Top-R", "Bot-L", "Bot-R")
old_page = 0
old_zoom = 0 # used for zoom on/off
# the zoom buttons work in on/off mode.
while True:
event, values = window.Read(timeout=100)
zoom = 0
force_page = False
if event is None:
break
if event in ("Escape:27",): # this spares me a 'Quit' button!
break
# print("hex(button)", hexlify(button.encode()))
if event[0] == chr(13): # surprise: this is 'Enter'!
try:
cur_page = int(values[0]) - 1 # check if valid
while cur_page < 0:
cur_page += page_count
except:
cur_page = 0 # this guy's trying to fool me
goto.Update(str(cur_page + 1))
# goto.TKStringVar.set(str(cur_page + 1))
elif event in ("Next", "Next:34", "MouseWheel:Down"):
cur_page += 1
elif event in ("Prev", "Prior:33", "MouseWheel:Up"):
cur_page -= 1
elif event == "Top-L":
zoom = 1
elif event == "Top-R":
zoom = 2
elif event == "Bot-L":
zoom = 3
elif event == "Bot-R":
zoom = 4
# sanitize page number
if cur_page >= page_count: # wrap around
cur_page = 0
while cur_page < 0: # we show conventional page numbers
cur_page += page_count
# prevent creating same data again
if cur_page != old_page:
zoom = old_zoom = 0
force_page = True
if event in zoom_buttons:
if 0 < zoom == old_zoom:
zoom = 0
force_page = True
if zoom != old_zoom:
force_page = True
if force_page:
data = get_page(cur_page, zoom)
image_elem.Update(data=data)
old_page = cur_page
old_zoom = zoom
# update page number field
if event in my_keys or not values[0]:
goto.Update(str(cur_page + 1))
# goto.TKStringVar.set(str(cur_page + 1))

View file

@ -0,0 +1,130 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import os
from sys import exit as exit
from PIL import Image
import io
import numpy as np
thumbnails = {}
ROWS = 8
COLUMNS = 8
sg.SetOptions(border_width=0)
# Get the folder containing the images from the user
# folder = 'A:/TEMP/pdfs'
folder = sg.PopupGetFolder('Image folder to open')
if folder is None:
sg.PopupCancel('Cancelling')
exit(0)
def image_file_to_bytes(filename, size):
try:
image = Image.open(filename)
image.thumbnail(size, Image.ANTIALIAS)
bio = io.BytesIO() # a binary memory resident stream
image.save(bio, format='PNG') # save image as png to it
imgbytes = bio.getvalue()
except:
imgbytes = None
return imgbytes
def set_image_to_blank(key):
img = Image.new('RGB', (100, 100), (255, 255, 255))
img.thumbnail((1, 1), Image.ANTIALIAS)
bio = io.BytesIO()
img.save(bio, format='PNG')
imgbytes = bio.getvalue()
window.FindElement(key).Update(image_data=imgbytes)
# get list of PNG files in folder
png_files = [os.path.join(folder, f) for f in os.listdir(folder) if '.png' in f]
filenames_only = [f for f in os.listdir(folder) if '.png' in f]
if len(png_files) == 0:
sg.Popup('No PNG images in folder')
exit(0)
# define menu layout
menu = [['&File', ['&Open Folder', 'E&xit']], ['&Help', ['&About',]]]
buttons = []
for display_index in range(ROWS):
row = []
for j in range(COLUMNS):
row.append(sg.Button('',border_width=0,button_color=sg.COLOR_SYSTEM_DEFAULT, key=(display_index, j)))
buttons.append(row)
col_buttons = [[]]
# define layout, show and read the window
col = [[sg.Text(png_files[0], size=(80, 3), key='filename')],
[sg.Image(data=image_file_to_bytes(png_files[0], (500,500)), key='image')],]
layout = [[sg.Menu(menu)], [sg.Column(buttons), sg.Column([[sg.Slider((len(png_files),0),default_value=0,size=(38,20),orientation='v', key='_slider_', change_submits=True)]]), sg.Column(col)]]
window = sg.Window('Image Browser',
return_keyboard_events=True,
use_default_focus=False ).Layout(layout).Finalize()
# -------========= Event Loop =========--------
display_index=0
while True:
for x in range(ROWS): # update thumbnails
for y in range(COLUMNS):
cur_index = display_index + (x * 4) + y
if cur_index < len(png_files):
filename = png_files[cur_index]
if filename not in thumbnails:
imgbytes = image_file_to_bytes(filename, (100,100))
thumbnails[filename] = imgbytes
else:
imgbytes = thumbnails[filename]
button_elem = window.FindElement(key=(x,y))
button_elem.Update(image_data=imgbytes)
else:
set_image_to_blank((x,y))
event, values = window.Read()
display_index = values['_slider_']
# --------------------- Button & Keyboard ---------------------
if event in (None, 'Exit'):
break
elif event in ('MouseWheel:Down', 'Down:40',) and display_index < len(png_files)-1:
display_index += 4
elif event in ('MouseWheel:Up', 'Up:38',) and display_index > 0:
display_index -= 4
elif event in ('Prior:33', 'Prev'):
display_index -= 16
elif event in ('Next:34', 'Next'):
display_index += 16
window.FindElement('_slider_').Update(display_index)
# ----------------- Menu choices -----------------
if event == 'Open Folder':
newfolder = sg.PopupGetFolder('New folder', no_window=True)
if newfolder is None:
continue
folder = newfolder
png_files = [os.path.join(folder, f) for f in os.listdir(folder) if '.png' in f]
filenames_only = [f for f in os.listdir(folder) if '.png' in f]
display_index = 0
thumbnail = {}
for j in range(ROWS):
for i in range(COLUMNS):
set_image_to_blank((i,j))
elif event == 'About':
sg.Popup('Demo PNG Viewer Program', 'Please give PySimpleGUI a try!')
elif type(event) is tuple:
x, y = event
image_index = display_index + (x * 4) + y
if image_index < len(png_files):
filename = png_files[image_index]
imgbytes = image_file_to_bytes(filename, (500, 500))
window.FindElement('image').Update(data=imgbytes)
window.FindElement('filename').Update(filename)

View file

@ -0,0 +1,78 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import os
from sys import exit as exit
# Simple Image Browser based on PySimpleGUI
# Get the folder containing the images from the user
folder = sg.PopupGetFolder('Image folder to open')
if folder is None:
sg.PopupCancel('Cancelling')
exit(0)
# get list of PNG files in folder
png_files = [folder + '\\' + f for f in os.listdir(folder) if '.png' in f]
filenames_only = [f for f in os.listdir(folder) if '.png' in f]
if len(png_files) == 0:
sg.Popup('No PNG images in folder')
exit(0)
# define menu layout
menu = [['File', ['Open Folder', 'Exit']], ['Help', ['About',]]]
# define layout, show and read the window
col = [[sg.Text(png_files[0], size=(80, 3), key='filename')],
[sg.Image(filename=png_files[0], key='image')],
[sg.Button('Next', size=(8,2)), sg.Button('Prev', size=(8,2)),
sg.Text('File 1 of {}'.format(len(png_files)), size=(15,1), key='filenum')]]
col_files = [[sg.Listbox(values=filenames_only, size=(60,30), key='listbox')],
[sg.Button('Read')]]
layout = [[sg.Menu(menu)], [sg.Column(col_files), sg.Column(col)]]
window = sg.Window('Image Browser', return_keyboard_events=True, location=(0,0), use_default_focus=False ).Layout(layout)
# loop reading the user input and displaying image, filename
i=0
while True:
event, values = window.Read()
# --------------------- Button & Keyboard ---------------------
if event is None:
break
elif event in ('Next', 'MouseWheel:Down', 'Down:40', 'Next:34') and i < len(png_files)-1:
i += 1
elif event in ('Prev', 'MouseWheel:Up', 'Up:38', 'Prior:33') and i > 0:
i -= 1
elif event == 'Exit':
exit(69)
filename = folder + '/' + values['listbox'][0] if event == 'Read' else png_files[i]
# ----------------- Menu choices -----------------
if event == 'Open Folder':
newfolder = sg.PopupGetFolder('New folder', no_window=True)
if newfolder is None:
continue
folder = newfolder
png_files = [folder + '/' + f for f in os.listdir(folder) if '.png' in f]
filenames_only = [f for f in os.listdir(folder) if '.png' in f]
window.FindElement('listbox').Update(values=filenames_only)
window.Refresh()
i = 0
elif event == 'About':
sg.Popup('Demo PNG Viewer Program', 'Please give PySimpleGUI a try!')
# update window with new image
window.FindElement('image').Update(filename=filename)
# update window with filename
window.FindElement('filename').Update(filename)
# update page display
window.FindElement('filenum').Update('File {} of {}'.format(i+1, len(png_files)))

View file

@ -0,0 +1,656 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
from tkinter import Tk
else:
import PySimpleGUI27 as sg
from Tkinter import Tk
desc_text = """
Text( text
size=(None, None)
auto_size_text=None
click_submits=None
relief=None
font=None
text_color=None
background_color=None
justification=None
pad=None
key=None
tooltip=None)
Shortcuts: Txt, T
"""
desc_inputtext = """
InputText( default_text =''
size=(None, None)
disabled=False
auto_size_text=None
password_char=''
justification=None
background_color=None
text_color=None
font=None
tooltip=None
do_not_clear=False
key=None
focus=False
pad=None)
Shortcuts: In, Input
"""
desc_inputcombo = """
InputCombo( values
default_value=None
size=(None, None)
auto_size_text=None
background_color=None
text_color=None
change_submits=False
disabled=False
key=None
pad=None
tooltip=None)
Shortcuts: Combo, DropDown, Drop
"""
desc_inputoptionmenu = """
InputOptionMenu(values
default_value=None
size=(None, None)
disabled=False
auto_size_text=None
background_color=None
text_color=None
key=None
pad=None
tooltip=None)
Shortcuts: OptionMenu
"""
desc_listbox = """
Listbox(values
default_values=None
select_mode=None
change_submits=False
bind_return_key=False
size=(None, None)
auto_size_text=None
font=None
background_color=None
text_color=None
key=None
pad=None
tooltip=None)
"""
desc_checkbox = """
CheckBox( text
default=False
size=(None, None)
auto_size_text=None
font=None
background_color=None
text_color=None
change_submits=False
disabled=False
key=None
pad=None
tooltip=None)
Shortcuts: CB, CBox, Check
"""
desc_radio = """
Radio( text
group_id
default=False
disabled=False
size=(None, None)
auto_size_text=None
background_color=None
text_color=None
font=None
key=None
pad=None
tooltip=None)
"""
desc_spin = """
Spin( values
initial_value=None
disabled=False
change_submits=False
size=(None, None)
auto_size_text=None
font=None
background_color=None
text_color=None
key=None
pad=None
tooltip=None)
"""
desc_multiline = """
MultiLine( default_text=''
enter_submits = False
disabled=False
autoscroll=False
size=(None,None)
auto_size_text=None
background_color=None
text_color=None
do_not_clear=False
key=None
focus=False
pad=None
tooltip=None)
"""
desc_output = """
Output( size=(None, None)
background_color=None
text_color=None
pad=None
font=None
tooltip=None
key=None)
"""
desc_button = """
Button( button_text=''
button_type=BUTTON_TYPE_CLOSES_WIN
target=(None, None)
tooltip=None
file_types=(("ALL Files", "*.*"),)
initial_folder=None
disabled=False
image_filename=None
image_size=(None, None)
image_subsample=None
border_width=None
size=(None, None)
auto_size_button=None
button_color=None
default_value = None
font=None
bind_return_key=False
focus=False
pad=None
key=None)
"""
desc_progressbar = """
ProgressBar(max_value
orientation=None
size=(None, None)
auto_size_text=None
bar_color=(None, None)
style=None
border_width=None
relief=None
key=None
pad=None)
"""
desc_image = """
Image( filename=None
data=None
size=(None, None)
pad=None
key=None
tooltip=None)
"""
desc_canvas = """
Canvas( canvas=None
background_color=None
size=(None, None)
pad=None
key=None
tooltip=None)
"""
desc_graph = """
Graph( canvas_size
graph_bottom_left
graph_top_right
background_color=None
pad=None
key=None
tooltip=None)
"""
desc_frame = """
Frame( title
layout
title_color=None
background_color=None
title_location=None
relief=DEFAULT_FRAME_RELIEF
size=(None, None)
font=None
pad=None
border_width=None
key=None
tooltip=None)
"""
desc_tab = """
Tab(title
layout
title_color=None
background_color=None
font=None
pad=None
disabled=False
border_width=None
key=None
tooltip=None)
"""
desc_tabgroup = """
TabGroup( layout
tab_location=None
title_color=None
selected_title_color=None
background_color=None
font=None
change_submits=False
pad=None
border_width=None
theme=None
key=None
tooltip=None)
"""
desc_slider = """
Slider( range=(None,None)
default_value=None
resolution=None
orientation=None
border_width=None
relief=None
change_submits=False
disabled=False
size=(None, None)
font=None
background_color=None
text_color=None
key=None
pad=None
tooltip=None)
"""
desc_spin = """
Spin( values
initial_value=None
disabled=False
change_submits=False
size=(None, None)
auto_size_text=None
font=None
background_color=None
text_color=None
key=None
pad=None
tooltip=None)
"""
desc_tree = """
Tree( data=None,
headings=None,
visible_column_map=None,
col_widths=None,
col0_width=10,
def_col_width=10,
auto_size_columns=True,
max_col_width=20,
select_mode=None,
font=None,
justification='right',
text_color=None,
background_color=None,
num_rows=None,
pad=None,
key=None,
tooltip=None):
"""
desc_column = """
Column( layout
background_color = None
size=(None, None)
pad=None
scrollable=False
key=None)
"""
desc_table = """
Table( values
headings=None
visible_column_map=None
col_widths=None
def_col_width=10
auto_size_columns=True
max_col_width=20
select_mode=None
display_row_numbers=False
scrollable=None
font=None
justification='right'
text_color=None
background_color=None
size=(None, None)
pad=None
key=None
tooltip=None)
"""
desc_window = """
Window( title
default_element_size=DEFAULT_ELEMENT_SIZE
default_button_element_size = (None, None)
auto_size_text=None
auto_size_buttons=None
location=(None, None)
button_color=None
font=None
progress_bar_color=(None, None)
background_color=None
border_depth=None
auto_close=False
auto_close_duration=DEFAULT_AUTOCLOSE_TIME
icon=DEFAULT_WINDOW_ICON
force_toplevel = False
return_keyboard_events=False
use_default_focus=True
text_justification=None
no_titlebar=False
grab_anywhere=False
keep_on_top=False
resizable=False)
"""
desc_window_methods = """
Layout(rows)
Call to set the window layout. Must be called prior to Read. Most likely "chained" in line with the Window creation.
Finalize()
Call to force a window to go through the final stages of initialization. This will cause the tkinter resources to be allocated so that they can then be modified.
Read()
Read the Window's input values and button clicks in a blocking-fashion
Returns event, values
ReadNonBlocking()
Read the Window's input values and button clicks but without blocking. It will immediately return.
Refresh()
Cause changes to the window to be displayed on the screen. Normally not needed unless the changes are immediately required or if it's going to be a while before another call to Read.
SetIcon(icon)
Sets the window's icon that will be shown on the titlebar.
Fill(values_dict)
Populates the windows fields with the values shown in the dictionary.
FindElement(key)
Rerturns the Element that has a matching key. If the key is not found, an Error Element is returned so that the program will not crash should the user try to perform an "update". A Popup message will be shown
SaveToDisk(filename)
Saves the window's values to disk
LoadFromDisk(filename)
Fills in a window's fields based on previously saved file
GetScreenDimensions()
Returns the size (w,h) of the screen in pixels
Move(x, y)
Move window to (x,y) position on the screen
Minimize()
Sends the window to the taskbar
CloseNonBlocking()
Closes a non-blocking window
Disable()
Stops a window from responding until Enable is called
Enable()
Re-enables a previously disabled window
Hide()
Completely hides a window, including removing from the taskbar
UnHide()
Restores a window hidden using Hide
Disappear()
Makes a window disappear while leaving the icon on the taskbar
Reappear()
Makes a window reappear that was previously made to disappear using Disappear()
SetAlpha(alpha)
Sets the window's transparency. 0 is completely transparent. 1 is fully visible, normal
"""
desc_menu= """
Menu(menu_definition
background_color=None
size=(None, None)
tearoff=True
pad=None
key=None)
"""
desc_button_types = """
There are multiple button types / names to choose from
CloseButton = CButton = SimpleButton
Button = ReadFormButton = ReadButton = RButton
RealtimeButton
DummyButton
FolderBrowse
FileBrowse
FilesBrowse
FileSaveAs = SaveAs
CalendarButton
ColorChooserButton
Shortcuts - Normal buttons with predefined text
Save, Open, OK, Ok, Cancel, Quit, Exit, Yes, No, Help
"""
desc_popup= """
Popup(button_color=None
background_color=None
text_color=None
button_type=POPUP_BUTTONS_OK
auto_close=False
auto_close_duration=None
non_blocking=False
icon=DEFAULT_WINDOW_ICON
line_width=None
font=None
no_titlebar=False
grab_anywhere=False
keep_on_top=False
location=(None,None))
"""
desc_popups = """
PopupScrolled
PopupGetFolder
PopupGetFile
PopupGetText
POopup
PopupNoButtons
PopupNonBlocking = PopupNoWait
PopupQuick
PopupNoTitleBar = PopupNoFrame = PopupNoBorder = PopupAnnoying
PopupAutoClose = PopupTimed
PopupError
PopupCancel
PopupOK
PopupOKCancel
PopupYesNo
"""
desc_one_line_progress_meter = """
OneLineProgressMeter(title
current_value
max_value
key
*args
orientation=None
bar_color=(None,None)
button_color=None
size=DEFAULT_PROGRESS_BAR_SIZE
border_width=None
grab_anywhere=False):
"""
element_list = ('Window',
'Text',
'InputText',
'CheckBox',
'RadioButton',
'Listbox',
'InputCombo',
'Slider',
'Spinner',
'Multiline',
'Output',
'ProgressBar',
'OptionMenu',
'Menu',
'Frame',
'Column',
'Graph',
'Image',
'Table',
'Tab',
'TabGroup',
'Button Types')
descriptions = {'Window': desc_window, 'Text': desc_text, 'InputText': desc_inputtext, 'Checkbox': desc_checkbox,
'Radio': desc_radio, 'Listbox': desc_listbox, 'Slider': desc_slider, 'Spinner':desc_spin, 'Multiline': desc_multiline,
'Output': desc_output, 'ProgressBar': desc_progressbar, 'OptionMenu': desc_inputoptionmenu,
'Combo': desc_inputcombo, 'Menu': desc_menu, 'Frame': desc_frame, 'Column': desc_column,
'Graph': desc_graph, 'Image': desc_image, 'Table': desc_table, 'Tree': desc_tree,'Tab': desc_tab,
'TabGroup': desc_tabgroup, 'Button':desc_button, 'Button Types': desc_button_types,
'Popup':desc_popup, 'Popups':desc_popups, 'One Line Prog Meter':desc_one_line_progress_meter}
tab_text = [[sg.Column([[sg.T('This is sample text')],[ sg.Text(desc_text, font=('Consolas 12'))]])]]
tab_input = [[sg.Column([[sg.Input(size=(15,1))],[sg.Text(desc_inputtext, font=('Consolas 12'))]])]]
tab_checkbox = [[sg.Column([[sg.Checkbox('Checkbox', size=(15,1))],[sg.Text(desc_checkbox, font=('Consolas 12'))]])]]
tab_radio = [[sg.Column([[sg.Radio('Radio Button', group_id=1, size=(15,1))],[sg.Text(desc_radio, font=('Consolas 12'))]])]]
tab_listbox = [[sg.Column([[sg.Listbox(values=[1,2,3,4] ,size=(15,4))],[sg.Text(desc_listbox, font=('Consolas 12'))]])]]
tab_slider = [[sg.Column([[sg.Slider((1,100), orientation='h', size=(15,15))],[sg.Text(desc_slider, font=('Consolas 12'))]])]]
tab_spinner = [[sg.Column([[sg.Spin((1,2,3,4,5),initial_value=1,size=(15,1))],[sg.Text(desc_spin, font=('Consolas 12'))]])]]
tab_multiline = [[sg.Column([[sg.Multiline(size=(15,1))],[sg.Text(desc_multiline, font=('Consolas 12'))]])]]
tab_output= [[sg.Column([[sg.Text(desc_output, font=('Consolas 12'))]])]]
tab_progressbar = [[sg.Column([[sg.Text(desc_progressbar, font=('Consolas 12'))]])]]
tab_optionmenu = [[sg.Column([[sg.OptionMenu([1,2,3,4,5], size=(15,1))],[sg.Text(desc_inputoptionmenu, font=('Consolas 12'))]])]]
tab_combo = [[sg.Column([[sg.Combo([1,2,3,4,5], size=(15,1))],[sg.Text(desc_inputcombo, font=('Consolas 12'))]])]]
tab_frame = [[sg.Column([[sg.Frame('Frame',[[sg.T(' ')]], size=(15,1))],[sg.Text(desc_frame, font=('Consolas 12'))]])]]
tab_column = [[sg.Text(desc_column, font=('Consolas 12'))]]
tab_graph = [[sg.Text(desc_graph, font=('Consolas 12'))]]
tab_tab = [[sg.Text(desc_tab, font=('Consolas 12'))]]
tab_tabgroup = [[sg.Text(desc_tabgroup, font=('Consolas 12'))]]
tab_image = [[sg.Text(desc_image, font=('Consolas 12'))]]
tab_table = [[sg.Text(desc_table, font=('Consolas 12'))]]
tab_tree = [[sg.Text(desc_tree, font=('Consolas 12'))]]
tab_menu = [[sg.Text(desc_menu, font=('Consolas 12'))]]
tab_button = [[sg.Text(desc_button, font=('Consolas 12'))]]
tab_button_types = [[sg.Text(desc_button_types, font=('Consolas 12'))]]
tab_popup = [[sg.Text(desc_popup, font=('Consolas 12'))]]
tab_popups = [[sg.Text(desc_popups, font=('Consolas 12'))]]
tab_one_line_prog_meter = [[sg.Text(desc_one_line_progress_meter, font=('Consolas 12'))]]
tab_window = [[ sg.TabGroup([[sg.Tab('Parms',[[sg.Text(desc_window, font=('Consolas 12'))]]),
sg.Tab('Methods', [[sg.Column([[sg.Text(desc_window_methods)]], size=(500,500), scrollable=True, )]])]])]]
layout = [[sg.TabGroup([[sg.Tab('Window',tab_window),
sg.Tab('Text',tab_text),
sg.Tab('InputText', tab_input),
sg.Tab('Checkbox', tab_checkbox),
sg.Tab('Radio',tab_radio),
sg.Tab('Listbox', tab_listbox),
sg.Tab('Slider', tab_slider),
sg.Tab('Spinner',tab_spinner),
sg.Tab('Multiline', tab_multiline),
sg.Tab('OptionMenu', tab_optionmenu),
sg.Tab('Combo', tab_combo),
sg.Tab('Image', tab_image),
sg.Tab('Output', tab_output),
sg.Tab('Table', tab_table),
sg.Tab('Tree', tab_tree),
sg.Tab('Graph', tab_graph),
sg.Tab('ProgressBar', tab_progressbar),
sg.Tab('Frame', tab_frame),
sg.Tab('Column', tab_column),
sg.Tab('Tab', tab_tab),
sg.Tab('TabGroup', tab_tabgroup),
sg.Tab('Menu', tab_menu),
sg.Tab('Button', tab_button),
sg.Tab('Button Types', tab_button_types),
sg.Tab('Popup', tab_popup),
sg.Tab('Popups', tab_popups),
sg.Tab('One Line Prog Meter', tab_one_line_prog_meter),
]], tab_location='lefttop', title_color='blue', selected_title_color='red', key='_TABS_')],
[sg.Button('Copy')] ]
# layout = [[sg.Text('The PySimpleGUI SDK Quick Reference Guide', font='Any 15', relief=sg.RELIEF_RAISED)],
# [sg.Listbox(values=element_list, size=(15, len(element_list) + 2), key='_in_', change_submits=True,
# font=('Consolas 12')),
# sg.Text(desc_text, size=(55, 25), font=('Consolas 13'), text_color='darkblue', key='_out_')]]
window = sg.Window('PySimpleGUI SDK Quick Reference',
font='Consolas 12',
).Layout(layout)
while True:
event, values = window.Read()
if event is None or event == 'Exit':
break
elif event == 'Methods':
sg.PopupScrolled(desc_window_methods, size=(50,20))
elif event == 'Copy':
tab = values['_TABS_']
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append(descriptions[tab])
r.update() # now it stays on the clipboard after the window is closed
r.destroy()
# element = values['_in_'][0]
# try:
# desc = descriptions[element]
# except:
# desc = ''
# window.FindElement('_out_').Update(desc)
# print(button, values)

View file

@ -0,0 +1,65 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import hashlib
from sys import exit as exit
"""
Create a secure login for your scripts without having to include your password
in the program. Create an SHA1 hash code for your password using the GUI. Paste into variable in final program
1. Choose a password
2. Generate a hash code for your chosen password by running program and entering 'gui' as the password
3. Type password into the GUI
4. Copy and paste hash code from GUI into variable named login_password_hash
5. Run program again and test your login!
6. Are you paying attention? The first person that can post an issue on GitHub with the
matching password to the hash code in this example gets a $5 PayPal payment
"""
# Use this GUI to get your password's hash code
def HashGeneratorGUI():
layout = [[sg.T('Password Hash Generator', size=(30,1), font='Any 15')],
[sg.T('Password'), sg.In(key='password')],
[sg.T('SHA Hash'), sg.In('', size=(40,1), key='hash')],
]
window = 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)
while True:
event, values = window.Read()
if event is None:
exit(69)
password = values['password']
try:
password_utf = password.encode('utf-8')
sha1hash = hashlib.sha1()
sha1hash.update(password_utf)
password_hash = sha1hash.hexdigest()
window.FindElement('hash').Update(password_hash)
except:
pass
# ----------------------------- Paste this code into your program / script -----------------------------
# determine if a password matches the secret password by comparing SHA1 hash codes
def PasswordMatches(password, hash):
password_utf = password.encode('utf-8')
sha1hash = hashlib.sha1()
sha1hash.update(password_utf)
password_hash = sha1hash.hexdigest()
return password_hash == hash
login_password_hash = 'e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4'
password = sg.PopupGetText('Password', password_char='*')
if password == 'gui': # Remove when pasting into your program
HashGeneratorGUI() # Remove when pasting into your program
exit(69) # Remove when pasting into your program
if PasswordMatches(password, login_password_hash):
print('Login SUCCESSFUL')
else:
print('Login FAILED!!')

View file

@ -0,0 +1,57 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
# GUI for switching an LED on and off to GPIO14
# GPIO and time library:
import RPi.GPIO as GPIO
import time
# determine that GPIO numbers are used:
GPIO.setmode(GPIO.BCM)
GPIO.setup(14, GPIO.OUT)
def SwitchLED():
varLEDStatus = GPIO.input(14)
varLedStatus = 0
if varLedStatus == 0:
GPIO.output(14, GPIO.HIGH)
return "LED is switched ON"
else:
GPIO.output(14, GPIO.LOW)
return "LED is switched OFF"
def FlashLED():
for i in range(5):
GPIO.output(14, GPIO.HIGH)
time.sleep(0.5)
GPIO.output(14, GPIO.LOW)
time.sleep(0.5)
layout = [[sg.T('Raspberry Pi LEDs')],
[sg.T('', size=(14, 1), key='output')],
[sg.Button('Switch LED')],
[sg.Button('Flash LED')],
[sg.Exit()]]
window = sg.Window('Raspberry Pi GUI', grab_anywhere=False).Layout(layout)
while True:
event, values = window.Read()
if event is None:
break
if event is 'Switch LED':
window.FindElement('output').Update(SwitchLED())
elif event is 'Flash LED':
window.FindElement('output').Update('LED is Flashing')
window.Refresh()
FlashLED()
window.FindElement('output').Update('')
window.Close()
sg.Popup('Done... exiting')

View file

@ -0,0 +1,101 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
# Robotics design pattern
# Uses Realtime Buttons to simulate the controls for a robot
# Rather than sending a single click when a button is clicked, Realtime Buttons
# send button presses continuously while the button is pressed down.
# Two examples, one using fancy graphics, one plain.
def RemoteControlExample():
# Make a form, but don't use context manager
sg.SetOptions(element_padding=(0,0))
back ='#eeeeee'
image_forward = 'ButtonGraphics/RobotForward.png'
image_backward = 'ButtonGraphics/RobotBack.png'
image_left = 'ButtonGraphics/RobotLeft.png'
image_right = 'ButtonGraphics/RobotRight.png'
sg.SetOptions(border_width=0, button_color=('black', back), background_color=back, element_background_color=back, text_element_background_color=back)
layout = [[sg.Text('Robotics Remote Control')],
[sg.T('', justification='center', size=(19,1), key='status')],
[ sg.RealtimeButton('', key='Forward', image_filename=image_forward, pad=((50,0),0))],
[ sg.RealtimeButton('', key='Left', image_filename=image_left),
sg.RealtimeButton('', key='Right', image_filename=image_right, pad=((50,0), 0))],
[ sg.RealtimeButton('', key='Reverse', image_filename=image_backward, pad=((50,0),0))],
[sg.T('')],
[sg.Quit(button_color=('black', 'orange'))]]
window = sg.Window('Robotics Remote Control', auto_size_text=True, grab_anywhere=False).Layout(layout)
#
# 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.
#
# your program's main loop
while (True):
# This is the code that reads and updates your window
event, values = window.Read(timeout=0, timeout_key='timeout')
if event is not None:
window.FindElement('status').Update(event)
elif event != 'timeout':
window.FindElement('status').Update('')
# if user clicked quit button OR closed the form using the X, then break out of loop
if event == 'Quit' or values is None:
break
window.Close()
def RemoteControlExample_NoGraphics():
# Make a form, but don't use context manager
layout = [[sg.Text('Robotics Remote Control', justification='center')],
[sg.T('', justification='center', size=(19,1), key='status')],
[sg.T(' '*8), sg.RealtimeButton('Forward')],
[ sg.RealtimeButton('Left'), sg.T(' '), sg.RealtimeButton('Right')],
[sg.T(' '*8), sg.RealtimeButton('Reverse')],
[sg.T('')],
[sg.Quit(button_color=('black', 'orange'))]]
# Display form to user
window = sg.Window('Robotics Remote Control', auto_size_text=True, grab_anywhere=False).Layout(layout)
#
# Some place later in your code...
# You need to perform a Read on your form every now and then or
# else it won't refresh.
# Notice how the timeout is 100ms. You don't have to use a timeout = 0 for all of your hardware
# applications. Leave some CPU for other threads or for your GUI. The longer you are in the GUI, the more
# responsive the GUI itself will be Match your timeout with your hardware's capabilities
#
# your program's main loop
while (True):
# This is the code that reads and updates your window
event, values = window.Read(timeout=100, timeout_key='timeout')
# print(event, values)
if event != 'timeout':
window.FindElement('status').Update(event)
else:
window.FindElement('status').Update('')
# if user clicked quit button OR closed the form using the X, then break out of loop
if event in (None, 'Quit'):
break
window.Close()
# ------------------------------------- main -------------------------------------
def main():
RemoteControlExample_NoGraphics()
# Uncomment to get the fancy graphics version. Be sure and download the button images!
RemoteControlExample()
# sg.Popup('End of non-blocking demonstration')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,665 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from threading import Thread
import time
from sys import exit as exit
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A pure python ping implementation using raw sockets.
(This is Python 3 port of https://github.com/jedie/python-ping)
(Tested and working with python 2.7, should work with 2.6+)
Note that ICMP messages can only be sent from processes running as root
(in Windows, you must run this script as 'Administrator').
Derived from ping.c distributed in Linux's netkit. That code is
copyright (c) 1989 by The Regents of the University of California.
That code is in turn derived from code written by Mike Muuss of the
US Army Ballistic Research Laboratory in December, 1983 and
placed in the public domain. They have my thanks.
Bugs are naturally mine. I'd be glad to hear about them. There are
certainly word - size dependencies here.
Copyright (c) Matthew Dixon Cowles, <http://www.visi.com/~mdc/>.
Distributable under the terms of the GNU General Public License
version 2. Provided with no warranties of any sort.
Original Version from Matthew Dixon Cowles:
-> ftp://ftp.visi.com/users/mdc/ping.py
Rewrite by Jens Diemer:
-> http://www.python-forum.de/post-69122.html#69122
Rewrite by George Notaras:
-> http://www.g-loaded.eu/2009/10/30/python-ping/
Enhancements by Martin Falatic:
-> http://www.falatic.com/index.php/39/pinging-with-python
Enhancements and fixes by Georgi Kolev:
-> http://github.com/jedie/python-ping/
Bug fix by Andrejs Rozitis:
-> http://github.com/rozitis/python-ping/
Revision history
~~~~~~~~~~~~~~~~
May 1, 2014
-----------
Little modifications by Mohammad Emami <emamirazavi@gmail.com>
- Added Python 3 support. For now this project will just support
python 3.x
- Tested with python 3.3
- version was upped to 0.6
March 19, 2013
--------------
* Fixing bug to prevent divide by 0 during run-time.
January 26, 2012
----------------
* Fixing BUG #4 - competability with python 2.x [tested with 2.7]
- Packet data building is different for 2.x and 3.x.
'cose of the string/bytes difference.
* Fixing BUG #10 - the multiple resolv issue.
- When pinging domain names insted of hosts (for exmaple google.com)
you can get different IP every time you try to resolv it, we should
resolv the host only once and stick to that IP.
* Fixing BUGs #3 #10 - Doing hostname resolv only once.
* Fixing BUG #14 - Removing all 'global' stuff.
- You should not use globul! Its bad for you...and its not thread safe!
* Fix - forcing the use of different times on linux/windows for
more accurate mesurments. (time.time - linux/ time.clock - windows)
* Adding quiet_ping function - This way we'll be able to use this script
as external lib.
* Changing default timeout to 3s. (1second is not enought)
* Switching data syze to packet size. It's easyer for the user to ignore the
fact that the packet headr is 8b and the datasize 64 will make packet with
size 72.
October 12, 2011
--------------
Merged updates from the main project
-> https://github.com/jedie/python-ping
September 12, 2011
--------------
Bugfixes + cleanup by Jens Diemer
Tested with Ubuntu + Windows 7
September 6, 2011
--------------
Cleanup by Martin Falatic. Restored lost comments and docs. Improved
functionality: constant time between pings, internal times consistently
use milliseconds. Clarified annotations (e.g., in the checksum routine).
Using unsigned data in IP & ICMP header pack/unpack unless otherwise
necessary. Signal handling. Ping-style output formatting and stats.
August 3, 2011
--------------
Ported to py3k by Zach Ware. Mostly done by 2to3; also minor changes to
deal with bytes vs. string changes (no more ord() in checksum() because
>source_string< is actually bytes, added .encode() to data in
send_one_ping()). That's about it.
March 11, 2010
--------------
changes by Samuel Stauffer:
- replaced time.clock with default_timer which is set to
time.clock on windows and time.time on other systems.
November 8, 2009
----------------
Improved compatibility with GNU/Linux systems.
Fixes by:
* George Notaras -- http://www.g-loaded.eu
Reported by:
* Chris Hallman -- http://cdhallman.blogspot.com
Changes in this release:
- Re-use time.time() instead of time.clock(). The 2007 implementation
worked only under Microsoft Windows. Failed on GNU/Linux.
time.clock() behaves differently under the two OSes[1].
[1] http://docs.python.org/library/time.html#time.clock
May 30, 2007
------------
little rewrite by Jens Diemer:
- change socket asterisk import to a normal import
- replace time.time() with time.clock()
- delete "return None" (or change to "return" only)
- in checksum() rename "str" to "source_string"
December 4, 2000
----------------
Changed the struct.pack() calls to pack the checksum and ID as
unsigned. My thanks to Jerome Poincheval for the fix.
November 22, 1997
-----------------
Initial hack. Doesn't do much, but rather than try to guess
what features I (or others) will want in the future, I've only
put in what I need now.
December 16, 1997
-----------------
For some reason, the checksum bytes are in the wrong order when
this is run under Solaris 2.X for SPARC but it works right under
Linux x86. Since I don't know just what's wrong, I'll swap the
bytes always and then do an htons().
===========================================================================
IP header info from RFC791
-> http://tools.ietf.org/html/rfc791)
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
===========================================================================
ICMP Echo / Echo Reply Message header info from RFC792
-> http://tools.ietf.org/html/rfc792
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ...
+-+-+-+-+-
===========================================================================
ICMP parameter info:
-> http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml
===========================================================================
An example of ping's typical output:
PING heise.de (193.99.144.80): 56 data bytes
64 bytes from 193.99.144.80: icmp_seq=0 ttl=240 time=127 ms
64 bytes from 193.99.144.80: icmp_seq=1 ttl=240 time=127 ms
64 bytes from 193.99.144.80: icmp_seq=2 ttl=240 time=126 ms
64 bytes from 193.99.144.80: icmp_seq=3 ttl=240 time=126 ms
64 bytes from 193.99.144.80: icmp_seq=4 ttl=240 time=127 ms
----heise.de PING Statistics----
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip (ms) min/avg/max/med = 126/127/127/127
===========================================================================
"""
# =============================================================================#
import argparse
import os, sys, socket, struct, select, time, signal
__description__ = 'A pure python ICMP ping implementation using raw sockets.'
if sys.platform == "win32":
# On Windows, the best timer is time.clock()
default_timer = time.clock
else:
# On most other platforms the best timer is time.time()
default_timer = time.time
NUM_PACKETS = 3
PACKET_SIZE = 64
WAIT_TIMEOUT = 3.0
# =============================================================================#
# ICMP parameters
ICMP_ECHOREPLY = 0 # Echo reply (per RFC792)
ICMP_ECHO = 8 # Echo request (per RFC792)
ICMP_MAX_RECV = 2048 # Max size of incoming buffer
MAX_SLEEP = 1000
class MyStats:
thisIP = "0.0.0.0"
pktsSent = 0
pktsRcvd = 0
minTime = 999999999
maxTime = 0
totTime = 0
avrgTime = 0
fracLoss = 1.0
myStats = MyStats # NOT Used globally anymore.
# =============================================================================#
def checksum(source_string):
"""
A port of the functionality of in_cksum() from ping.c
Ideally this would act on the string as a series of 16-bit ints (host
packed), but this works.
Network data is big-endian, hosts are typically little-endian
"""
countTo = (int(len(source_string) / 2)) * 2
sum = 0
count = 0
# Handle bytes in pairs (decoding as short ints)
loByte = 0
hiByte = 0
while count < countTo:
if (sys.byteorder == "little"):
loByte = source_string[count]
hiByte = source_string[count + 1]
else:
loByte = source_string[count + 1]
hiByte = source_string[count]
try: # For Python3
sum = sum + (hiByte * 256 + loByte)
except: # For Python2
sum = sum + (ord(hiByte) * 256 + ord(loByte))
count += 2
# Handle last byte if applicable (odd-number of bytes)
# Endianness should be irrelevant in this case
if countTo < len(source_string): # Check for odd length
loByte = source_string[len(source_string) - 1]
try: # For Python3
sum += loByte
except: # For Python2
sum += ord(loByte)
sum &= 0xffffffff # Truncate sum to 32 bits (a variance from ping.c, which
# uses signed ints, but overflow is unlikely in ping)
sum = (sum >> 16) + (sum & 0xffff) # Add high 16 bits to low 16 bits
sum += (sum >> 16) # Add carry from above (if any)
answer = ~sum & 0xffff # Invert and truncate to 16 bits
answer = socket.htons(answer)
return answer
# =============================================================================#
def do_one(myStats, destIP, hostname, timeout, mySeqNumber, packet_size, quiet=False):
"""
Returns either the delay (in ms) or None on timeout.
"""
delay = None
try: # One could use UDP here, but it's obscure
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))
except socket.error as e:
print("failed. (socket error: '%s')" % e.args[1])
raise # raise the original error
my_ID = os.getpid() & 0xFFFF
sentTime = send_one_ping(mySocket, destIP, my_ID, mySeqNumber, packet_size)
if sentTime == None:
mySocket.close()
return delay
myStats.pktsSent += 1
recvTime, dataSize, iphSrcIP, icmpSeqNumber, iphTTL = receive_one_ping(mySocket, my_ID, timeout)
mySocket.close()
if recvTime:
delay = (recvTime - sentTime) * 1000
if not quiet:
print("%d bytes from %s: icmp_seq=%d ttl=%d time=%d ms" % (
dataSize, socket.inet_ntoa(struct.pack("!I", iphSrcIP)), icmpSeqNumber, iphTTL, delay)
)
myStats.pktsRcvd += 1
myStats.totTime += delay
if myStats.minTime > delay:
myStats.minTime = delay
if myStats.maxTime < delay:
myStats.maxTime = delay
else:
delay = None
print("Request timed out.")
return delay
# =============================================================================#
def send_one_ping(mySocket, destIP, myID, mySeqNumber, packet_size):
"""
Send one ping to the given >destIP<.
"""
# destIP = socket.gethostbyname(destIP)
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
# (packet_size - 8) - Remove header size from packet size
myChecksum = 0
# Make a dummy heder with a 0 checksum.
header = struct.pack(
"!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
padBytes = []
startVal = 0x42
# 'cose of the string/byte changes in python 2/3 we have
# to build the data differnely for different version
# or it will make packets with unexpected size.
if sys.version[:1] == '2':
bytes = struct.calcsize("d")
data = ((packet_size - 8) - bytes) * "Q"
data = struct.pack("d", default_timer()) + data
else:
for i in range(startVal, startVal + (packet_size - 8)):
padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
# data = bytes(padBytes)
data = bytearray(padBytes)
# Calculate the checksum on the data and the dummy header.
myChecksum = checksum(header + data) # Checksum is in network order
# Now that we have the right checksum, we put that in. It's just easier
# to make up a new header than to stuff it into the dummy.
header = struct.pack(
"!BBHHH", ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
packet = header + data
sendTime = default_timer()
try:
mySocket.sendto(packet, (destIP, 1)) # Port number is irrelevant for ICMP
except socket.error as e:
print("General failure (%s)" % (e.args[1]))
return
return sendTime
# =============================================================================#
def receive_one_ping(mySocket, myID, timeout):
"""
Receive the ping from the socket. Timeout = in ms
"""
timeLeft = timeout / 1000
while True: # Loop while waiting for packet or timeout
startedSelect = default_timer()
whatReady = select.select([mySocket], [], [], timeLeft)
howLongInSelect = (default_timer() - startedSelect)
if whatReady[0] == []: # Timeout
return None, 0, 0, 0, 0
timeReceived = default_timer()
recPacket, addr = mySocket.recvfrom(ICMP_MAX_RECV)
ipHeader = recPacket[:20]
iphVersion, iphTypeOfSvc, iphLength, \
iphID, iphFlags, iphTTL, iphProtocol, \
iphChecksum, iphSrcIP, iphDestIP = struct.unpack(
"!BBHHHBBHII", ipHeader
)
icmpHeader = recPacket[20:28]
icmpType, icmpCode, icmpChecksum, \
icmpPacketID, icmpSeqNumber = struct.unpack(
"!BBHHH", icmpHeader
)
if icmpPacketID == myID: # Our packet
dataSize = len(recPacket) - 28
# print (len(recPacket.encode()))
return timeReceived, (dataSize + 8), iphSrcIP, icmpSeqNumber, iphTTL
timeLeft = timeLeft - howLongInSelect
if timeLeft <= 0:
return None, 0, 0, 0, 0
# =============================================================================#
def dump_stats(myStats):
"""
Show stats when pings are done
"""
print("\n----%s PYTHON PING Statistics----" % (myStats.thisIP))
if myStats.pktsSent > 0:
myStats.fracLoss = (myStats.pktsSent - myStats.pktsRcvd) / myStats.pktsSent
print("%d packets transmitted, %d packets received, %0.1f%% packet loss" % (
myStats.pktsSent, myStats.pktsRcvd, 100.0 * myStats.fracLoss
))
if myStats.pktsRcvd > 0:
print("round-trip (ms) min/avg/max = %d/%0.1f/%d" % (
myStats.minTime, myStats.totTime / myStats.pktsRcvd, myStats.maxTime
))
print("")
return
# =============================================================================#
def signal_handler(signum, frame):
"""
Handle exit via signals
"""
dump_stats()
print("\n(Terminated with signal %d)\n" % (signum))
sys.exit(0)
# =============================================================================#
def verbose_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
packet_size=PACKET_SIZE, path_finder=False):
"""
Send >count< ping to >destIP< with the given >timeout< and display
the result.
"""
signal.signal(signal.SIGINT, signal_handler) # Handle Ctrl-C
if hasattr(signal, "SIGBREAK"):
# Handle Ctrl-Break e.g. under Windows
signal.signal(signal.SIGBREAK, signal_handler)
myStats = MyStats() # Reset the stats
mySeqNumber = 0 # Starting value
try:
destIP = socket.gethostbyname(hostname)
print("\nPYTHON PING %s (%s): %d data bytes" % (hostname, destIP, packet_size))
except socket.gaierror as e:
print("\nPYTHON PING: Unknown host: %s (%s)" % (hostname, e.args[1]))
print()
return
myStats.thisIP = destIP
for i in range(count):
delay = do_one(myStats, destIP, hostname, timeout, mySeqNumber, packet_size)
if delay == None:
delay = 0
mySeqNumber += 1
# Pause for the remainder of the MAX_SLEEP period (if applicable)
if (MAX_SLEEP > delay):
time.sleep((MAX_SLEEP - delay) / 1000)
dump_stats(myStats)
# =============================================================================#
def quiet_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
packet_size=PACKET_SIZE, path_finder=False):
"""
Same as verbose_ping, but the results are returned as tuple
"""
myStats = MyStats() # Reset the stats
mySeqNumber = 0 # Starting value
try:
destIP = socket.gethostbyname(hostname)
except socket.gaierror as e:
return False
myStats.thisIP = destIP
# This will send packet that we dont care about 0.5 seconds before it starts
# acrutally pinging. This is needed in big MAN/LAN networks where you sometimes
# loose the first packet. (while the switches find the way... :/ )
if path_finder:
fakeStats = MyStats()
do_one(fakeStats, destIP, hostname, timeout,
mySeqNumber, packet_size, quiet=True)
time.sleep(0.5)
for i in range(count):
delay = do_one(myStats, destIP, hostname, timeout,
mySeqNumber, packet_size, quiet=True)
if delay == None:
delay = 0
mySeqNumber += 1
# Pause for the remainder of the MAX_SLEEP period (if applicable)
if (MAX_SLEEP > delay):
time.sleep((MAX_SLEEP - delay) / 1000)
if myStats.pktsSent > 0:
myStats.fracLoss = (myStats.pktsSent - myStats.pktsRcvd) / myStats.pktsSent
if myStats.pktsRcvd > 0:
myStats.avrgTime = myStats.totTime / myStats.pktsRcvd
# return tuple(max_rtt, min_rtt, avrg_rtt, percent_lost)
return myStats.maxTime, myStats.minTime, myStats.avrgTime, myStats.fracLoss
# =============================================================================#
def main():
parser = argparse.ArgumentParser(description=__description__)
parser.add_argument('-q', '--quiet', action='store_true',
help='quiet output')
parser.add_argument('-c', '--count', type=int, default=NUM_PACKETS,
help=('number of packets to be sent '
'(default: %(default)s)'))
parser.add_argument('-W', '--timeout', type=float, default=WAIT_TIMEOUT,
help=('time to wait for a response in seoncds '
'(default: %(default)s)'))
parser.add_argument('-s', '--packet-size', type=int, default=PACKET_SIZE,
help=('number of data bytes to be sent '
'(default: %(default)s)'))
parser.add_argument('destination')
# args = parser.parse_args()
ping = verbose_ping
# if args.quiet:
# ping = quiet_ping
ping('Google.com', timeout=1000)
# ping(args.destination, timeout=args.timeout*1000, count=args.count,
# packet_size=args.packet_size)
# set coordinate system
canvas_right = 300
canvas_left = 0
canvas_top = 0
canvas_bottom = 300
# define the coordinates you'll use for your graph
x_right = 100
x_left = 0
y_bottom = 0
y_top = 500
# globale used to communicate with thread.. yea yea... it's working fine
g_exit = False
g_response_time = None
def ping_thread(args):
global g_exit, g_response_time
while not g_exit:
g_response_time = quiet_ping('google.com', timeout=1000)
def convert_xy_to_canvas_xy(x_in,y_in):
scale_x = (canvas_right - canvas_left) / (x_right - x_left)
scale_y = (canvas_top - canvas_bottom) / (y_top - y_bottom)
new_x = canvas_left + scale_x * (x_in - x_left)
new_y = canvas_bottom + scale_y * (y_in - y_bottom)
return new_x, new_y
# start ping measurement thread
thread = Thread(target=ping_thread, args=(None,))
thread.start()
layout = [ [sg.T('Ping times to Google.com', font='Any 18')],
[sg.Canvas(size=(canvas_right, canvas_bottom), background_color='white', key='canvas')],
[sg.Quit()] ]
window = sg.Window('Ping Times To Google.com', grab_anywhere=True).Layout(layout).Finalize()
canvas = window.FindElement('canvas').TKCanvas
prev_response_time = None
i=0
prev_x, prev_y = canvas_left, canvas_bottom
while True:
time.sleep(.2)
event, values = window.Read(timeout=0)
if event == 'Quit' or event is None:
break
if g_response_time is None or prev_response_time == g_response_time:
continue
try:
new_x, new_y = convert_xy_to_canvas_xy(i, g_response_time[0])
except: continue
prev_response_time = g_response_time
canvas.create_line(prev_x, prev_y, new_x, new_y, width=1, fill='black')
prev_x, prev_y = new_x, new_y
if i >= x_right:
i = 0
prev_x = prev_y = last_x = last_y = 0
canvas.delete('all')
else: i += 1
# tell thread we're done. wait for thread to exit
g_exit = True
thread.join()
exit(69)

186
DemoPrograms/Demo_Pong.py Normal file
View file

@ -0,0 +1,186 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import random
import time
from sys import exit as exit
"""
Pong code supplied by Daniel Young (Neonzz)
Modified. Original code: https://www.pygame.org/project/3649/5739
"""
class Ball:
def __init__(self, canvas, bat, bat2, color):
self.canvas = canvas
self.bat = bat
self.bat2 = bat2
self.playerScore = 0
self.player1Score = 0
self.drawP1 = None
self.drawP = None
self.id = self.canvas.create_oval(10, 10, 35, 35, fill=color)
self.canvas.move(self.id, 327, 220)
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
self.x = random.choice([-2.5, 2.5])
self.y = -2.5
def checkwin(self):
winner = None
if self.playerScore >= 10:
winner = 'Player left wins'
if self.player1Score >= 10:
winner = 'Player Right'
return winner
def updatep(self, val):
self.canvas.delete(self.drawP)
self.drawP = self.canvas.create_text(170, 50, font=('freesansbold.ttf', 40), text=str(val), fill='white')
def updatep1(self, val):
self.canvas.delete(self.drawP1)
self.drawP1 = self.canvas.create_text(550, 50, font=('freesansbold.ttf', 40), text=str(val), fill='white')
def hit_bat(self, pos):
bat_pos = self.canvas.coords(self.bat.id)
if pos[2] >= bat_pos[0] and pos[0] <= bat_pos[2]:
if pos[3] >= bat_pos[1] and pos[3] <= bat_pos[3]:
return True
return False
def hit_bat2(self, pos):
bat_pos = self.canvas.coords(self.bat2.id)
if pos[2] >= bat_pos[0] and pos[0] <= bat_pos[2]:
if pos[3] >= bat_pos[1] and pos[3] <= bat_pos[3]:
return True
return False
def draw(self):
self.canvas.move(self.id, self.x, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 4
if pos[3] >= self.canvas_height:
self.y = -4
if pos[0] <= 0:
self.player1Score += 1
self.canvas.move(self.id, 327, 220)
self.x = 4
self.updatep1(self.player1Score)
if pos[2] >= self.canvas_width:
self.playerScore += 1
self.canvas.move(self.id, -327, -220)
self.x = -4
self.updatep(self.playerScore)
if self.hit_bat(pos):
self.x = 4
if self.hit_bat2(pos):
self.x = -4
class pongbat():
def __init__(self, canvas, color):
self.canvas = canvas
self.id = self.canvas.create_rectangle(40, 200, 25, 310, fill=color)
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
self.y = 0
def up(self, evt):
self.y = -5
def down(self, evt):
self.y = 5
def draw(self):
self.canvas.move(self.id, 0, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 0
if pos[3] >= 400:
self.y = 0
class pongbat2():
def __init__(self, canvas, color):
self.canvas = canvas
self.id = self.canvas.create_rectangle(680, 200, 660, 310, fill=color)
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
self.y = 0
def up(self, evt):
self.y = -5
def down(self, evt):
self.y = 5
def draw(self):
self.canvas.move(self.id, 0, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 0
if pos[3] >= 400:
self.y = 0
def pong():
# ------------- Define GUI layout -------------
layout = [[sg.Canvas(size=(700, 400), background_color='black', key='canvas')],
[sg.T(''), sg.Button('Quit')]]
# ------------- Create window -------------
window = sg.Window('The Classic Game of Pong', return_keyboard_events=True).Layout(layout).Finalize()
# window.Finalize() # TODO Replace with call to window.Finalize once code released
# ------------- Get the tkinter Canvas we're drawing on -------------
canvas = window.FindElement('canvas').TKCanvas
# ------------- Create line down center, the bats and ball -------------
canvas.create_line(350, 0, 350, 400, fill='white')
bat1 = pongbat(canvas, 'white')
bat2 = pongbat2(canvas, 'white')
ball1 = Ball(canvas, bat1, bat2, 'green')
# ------------- Event Loop -------------
while True:
# ------------- Draw ball and bats -------------
ball1.draw()
bat1.draw()
bat2.draw()
# ------------- Read the form, get keypresses -------------
event, values = window.Read(timeout=0)
# ------------- If quit -------------
if event is None or event == 'Quit':
exit(69)
# ------------- Keypresses -------------
if event is not None:
if event.startswith('Up'):
bat2.up(2)
elif event.startswith('Down'):
bat2.down(2)
elif event == 'w':
bat1.up(1)
elif event == 's':
bat1.down(1)
if ball1.checkwin():
sg.Popup('Game Over', ball1.checkwin() + ' won!!')
break
# ------------- Bottom of loop, delay between animations -------------
# time.sleep(.01)
canvas.after(10)
if __name__ == '__main__':
pong()

View file

@ -0,0 +1,33 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
'''
Use this code as a starting point for creating your own Popup functions.
Rather than creating a long list of Popup high-level API calls, PySimpleGUI provides
you with the tools to easily create your own. If you need more than what the standard PopupGetText and
other calls provide, then it's time for you to graduate into making your own windows. Or, maybe you need
another window that pops-up over your primary window. Whatever the need, don't hesitate to dive in
and create your own Popup call.
This example is for a DropDown / Combobox Popup. You provide it with a title, a message and the list
of values to choose from. It mimics the return values of existing Popup calls (None if nothing was input)
'''
def PopupDropDown(title, text, values):
window = sg.Window(title).Layout([[sg.Text(text)],
[sg.DropDown(values, key='_DROP_')],
[sg.OK(), sg.Cancel()]])
event, values = window.Read()
return None if event != 'OK' else values['_DROP_']
# ----------------------- Calling your PopupDropDown function -----------------------
values = ['choice {}'.format(x) for x in range(30)]
print(PopupDropDown('My Title', 'Please make a selection', values))

View file

@ -0,0 +1,37 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from PySimpleGUI import Print as print
print('test')
sg.PopupGetFile('Get file', save_as=True,file_types=(("ALL Files", "*.jpg"),))
# Here, have some windows on me....
[sg.PopupNoWait('No-wait Popup', location=(500+100*x,500)) for x in range(10)]
answer = sg.PopupYesNo('Do not worry about all those open windows... they will disappear at the end', 'Are you OK with that?')
if answer == 'No':
sg.PopupCancel('OK, we will destroy those windows as soon as you close this window')
sys.exit()
sg.PopupNonBlocking('Your answer was',answer, location=(1000,600))
text = sg.PopupGetText('This is a call to PopopGetText', location=(1000,200))
sg.PopupGetFile('Get file')
sg.PopupGetFolder('Get folder')
sg.Popup('Simple popup')
sg.PopupNoTitlebar('No titlebar')
sg.PopupNoBorder('No border')
sg.PopupNoFrame('No frame')
sg.PopupCancel('Cancel')
sg.PopupOKCancel('OK Cancel')
sg.PopupAutoClose('Autoclose')

View file

@ -0,0 +1,89 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from time import sleep
from sys import exit as exit
"""
Demonstration of simple and multiple OneLineProgressMeter's
Shows how 2 progress meters can be running at the same time.
Note -- If the user wants to cancel a meter, it's important to use the "Cancel" button, not the X
If the software determined that a meter should be cancelled early,
calling OneLineProgresMeterCancel(key) will cancel the meter with the matching key
"""
# sg.ChangeLookAndFeel('Dark')
"""
The simple case is that you want to add a single meter to your code. The one-line solution
"""
def DemoOneLineProgressMeter():
# Display a progress meter. Allow user to break out of loop using cancel button
for i in range(1000):
if not sg.OneLineProgressMeter('My 1-line progress meter', i+1, 1000, 'meter key' ):
break
layout = [
[sg.T('One-Line Progress Meter Demo', font=('Any 18'))],
[sg.T('Outer Loop Count', size=(15,1), justification='r'), sg.In(default_text='100', size=(5,1), key='CountOuter', do_not_clear=True),
sg.T('Delay'), sg.In(default_text='10', key='TimeOuter', size=(5,1), do_not_clear=True), sg.T('ms')],
[sg.T('Inner Loop Count', size=(15,1), justification='r'), sg.In(default_text='100', size=(5,1), key='CountInner', do_not_clear=True) ,
sg.T('Delay'), sg.In(default_text='10', key='TimeInner', size=(5,1), do_not_clear=True), sg.T('ms')],
[sg.Button('Show', pad=((0,0), 3), bind_return_key=True), sg.T('me the meters!')]
]
window = sg.Window('One-Line Progress Meter Demo').Layout(layout)
while True:
event, values = window.Read()
if event is None:
break
if event == 'Show':
max_outer = int(values['CountOuter'])
max_inner = int(values['CountInner'])
delay_inner = int(values['TimeInner'])
delay_outer = int(values['TimeOuter'])
for i in range(max_outer):
if not sg.OneLineProgressMeter('Outer Loop', i+1, max_outer, 'outer'):
break
sleep(delay_outer/1000)
for j in range(max_inner):
if not sg.OneLineProgressMeter('Inner Loop', j+1, max_inner, 'inner'):
break
sleep(delay_inner/1000)
'''
Make your own progress meter!
Embed the meter right into your window
'''
def CustomMeter():
# layout the form
layout = [[sg.Text('A custom progress meter')],
[sg.ProgressBar(1000, orientation='h', size=(20,20), key='progress')],
[sg.Cancel()]]
# create the form`
window = sg.Window('Custom Progress Meter').Layout(layout)
progress_bar = window.FindElement('progress')
# loop that would normally do something useful
for i in range(1000):
# check to see if the cancel button was clicked and exit loop if clicked
event, values = window.Read(timeout=0)
if event == 'Cancel' or event == None:
break
# update bar with loop value +1 so that bar eventually reaches the maximum
progress_bar.UpdateBar(i+1)
# done with loop... need to destroy the window as it's still open
window.Close()
CustomMeter()
DemoOneLineProgressMeter()

View file

@ -0,0 +1,87 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasAgg
import matplotlib.backends.tkagg as tkagg
import tkinter as Tk
"""
Demonstrates one way of embedding Matplotlib figures into a PySimpleGUI window.
Paste your Pyplot code into the section marked below.
Do all of your plotting as you normally would, but do NOT call plt.show().
Stop just short of calling plt.show() and let the GUI do the rest.
The remainder of the program will convert your plot and display it in the GUI.
If you want to change the GUI, make changes to the GUI portion marked below.
"""
# ------------------------------- PASTE YOUR MATPLOTLIB CODE HERE -------------------------------
import numpy as np
import matplotlib.pyplot as plt
values_to_plot = (20, 35, 30, 35, 27)
ind = np.arange(len(values_to_plot))
width = 0.4
p1 = plt.bar(ind, values_to_plot, width)
plt.ylabel('Y-Axis Values')
plt.title('Plot Title')
plt.xticks(ind, ('Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0],), ('Data Group 1',))
# ------------------------------- END OF YOUR MATPLOTLIB CODE -------------------------------
# ------------------------------- Beginning of Matplotlib helper code -----------------------
def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas
loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(loc[0] + figure_w / 2, loc[1] + figure_h / 2, image=photo)
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
return photo
# ------------------------------- Beginning of GUI CODE -------------------------------
fig = plt.gcf() # if using Pyplot then get the figure from the plot
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
# define the window layout
layout = [[sg.Text('Plot test', font='Any 18')],
[sg.Canvas(size=(figure_w, figure_h), key='canvas')],
[sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', force_toplevel=True).Layout(
layout).Finalize()
# add the plot to the window
fig_photo = draw_figure(window.FindElement('canvas').TKCanvas, fig)
# show it all again and get buttons
event, values = window.Read()

View file

@ -0,0 +1,79 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasAgg
import matplotlib.backends.tkagg as tkagg
import tkinter as Tk
"""
Demonstrates one way of embedding Matplotlib figures into a PySimpleGUI window.
Paste your Pyplot code into the section marked below.
Do all of your plotting as you normally would, but do NOT call plt.show().
Stop just short of calling plt.show() and let the GUI do the rest.
The remainder of the program will convert your plot and display it in the GUI.
If you want to change the GUI, make changes to the GUI portion marked below.
"""
#------------------------------- PASTE YOUR MATPLOTLIB CODE HERE -------------------------------
import matplotlib.pyplot as plt
import numpy as np
label = ['Adventure', 'Action', 'Drama', 'Comedy', 'Thriller/Suspense', 'Horror', 'Romantic Comedy', 'Musical',
'Documentary', 'Black Comedy', 'Western', 'Concert/Performance', 'Multiple Genres', 'Reality']
no_movies = [941, 854, 4595, 2125, 942, 509, 548, 149, 1952, 161, 64, 61, 35, 5]
index = np.arange(len(label))
plt.bar(index, no_movies)
plt.xlabel('Genre', fontsize=5)
plt.ylabel('No of Movies', fontsize=5)
plt.xticks(index, label, fontsize=5, rotation=30)
plt.title('Market Share for Each Genre 1995-2017')
#------------------------------- END OF YOUR MATPLOTLIB CODE -------------------------------
#------------------------------- Beginning of Matplotlib helper code -----------------------
def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas
loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
return photo
#------------------------------- Beginning of GUI CODE -------------------------------
fig = plt.gcf() # if using Pyplot then get the figure from the plot
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
# define the window layout
layout = [[sg.Text('Plot test', font='Any 18')],
[sg.Canvas(size=(figure_w, figure_h), key='canvas')],
[sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', force_toplevel=True).Layout(layout).Finalize()
# add the plot to the window
fig_photo = draw_figure(window.FindElement('canvas').TKCanvas, fig)
# show it all again and get buttons
event, values = window.Read()

View file

@ -0,0 +1,77 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import glob
import ntpath
import subprocess
LOCATION_OF_YOUR_SCRIPTS = ''
# Execute the command. Will not see the output from the command until it completes.
def execute_command_blocking(command, *args):
expanded_args = []
for a in args:
expanded_args.append(a)
# expanded_args += a
try:
sp = subprocess.Popen([command,expanded_args], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = sp.communicate()
if out:
print(out.decode("utf-8"))
if err:
print(err.decode("utf-8"))
except:
out = ''
return out
# Executes command and immediately returns. Will not see anything the script outputs
def execute_command_nonblocking(command, *args):
expanded_args = []
for a in args:
expanded_args += a
try:
sp = subprocess.Popen([command,expanded_args], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except: pass
def Launcher2():
sg.ChangeLookAndFeel('GreenTan')
window = sg.Window('Script launcher')
filelist = glob.glob(LOCATION_OF_YOUR_SCRIPTS+'*.py')
namesonly = []
for file in filelist:
namesonly.append(ntpath.basename(file))
layout = [
[sg.Listbox(values=namesonly, size=(30, 19), select_mode=sg.SELECT_MODE_EXTENDED, key='demolist'), sg.Output(size=(88, 20), font='Courier 10')],
[sg.Checkbox('Wait for program to complete', default=False, key='wait')],
[sg.Button('Run'), sg.Button('Shortcut 1'), sg.Button('Fav Program'), sg.Button('EXIT')],
]
window.Layout(layout)
# ---===--- Loop taking in user input --- #
while True:
event, values = window.Read()
if event in ('EXIT', None):
break # exit button clicked
if event in ('Shortcut 1', 'Fav Program'):
print('Quickly launch your favorite programs using these shortcuts')
print('Or copy files to your github folder. Or anything else you type on the command line')
# copyfile(source, dest)
elif event is 'Run':
for index, file in enumerate(values['demolist']):
print('Launching %s'%file)
window.Refresh() # make the print appear immediately
if values['wait']:
execute_command_blocking(LOCATION_OF_YOUR_SCRIPTS + file)
else:
execute_command_nonblocking(LOCATION_OF_YOUR_SCRIPTS + file)
if __name__ == '__main__':
Launcher2()

View file

@ -0,0 +1,30 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
'''
Quickly add a GUI to your script!
This simple script shows a 1-line-GUI addition to a typical Python command line script.
Previously this script accepted 1 parameter on the command line. When executed, that
parameter is read into the variable fname.
The 1-line-GUI shows a form that allows the user to browse to find the filename. The GUI
stores the result in the variable fname, just like the command line parsing did.
'''
if len(sys.argv) == 1:
event, (fname,) = sg.Window('My Script').Layout([[sg.T('Document to open')],
[sg.In(), sg.FileBrowse()],
[sg.CloseButton('Open'), sg.CloseButton('Cancel')]]).Read()
else:
fname = sys.argv[1]
if not fname:
sg.Popup("Cancel", "No filename supplied")
raise SystemExit("Cancelling: no filename supplied")

View file

@ -0,0 +1,37 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Demo of how to combine elements into your own custom element
"""
sg.SetOptions(element_padding=(0,0))
# sg.ChangeLookAndFeel('Dark')
# --- Define our "Big-Button-Spinner" compound element. Has 2 buttons and an input field --- #
NewSpinner = [sg.Button('-', size=(2,1), font='Any 12'),
sg.In('0', size=(2,1), font='Any 14', justification='r', key='spin'),
sg.Button('+', size=(2,1), font='Any 12')]
# --- Define Window --- #
layout = [
[sg.Text('Spinner simulation')],
NewSpinner,
[sg.T('')],
[sg.Ok()]
]
window = sg.Window('Spinner simulation').Layout(layout)
# --- Event Loop --- #
counter = 0
while True:
event, values = window.Read()
if event == 'Ok' or event is None: # be nice to your user, always have an exit from your form
break
# --- do spinner stuff --- #
counter += 1 if event == '+' else -1 if event == '-' else 0
window.FindElement('spin').Update(counter)

View file

@ -0,0 +1,23 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Simple Form showing how to use keys on your input fields
"""
layout = [
[sg.Text('Please enter your Name, Address, Phone')],
[sg.Text('Name', size=(15, 1)), sg.InputText('1', key='name'), sg.FileBrowse()],
[sg.Text('Address', size=(15, 1)), sg.InputText('2', key='address')],
[sg.Text('Phone', size=(15, 1)), sg.InputText('3', key='phone')],
[sg.CloseButton('Submit'), sg.CloseButton('Cancel')]
]
window = sg.Window('Simple Data Entry Window').Layout(layout)
event, values = window.Read()
sg.Popup(event, values, values['name'], values['address'], values['phone'])

View file

@ -0,0 +1,45 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import csv
def table_example():
filename = sg.PopupGetFile('filename to open', no_window=True, file_types=(("CSV Files","*.csv"),))
# --- populate table with file contents --- #
if filename == '':
sys.exit(69)
data = []
header_list = []
button = sg.PopupYesNo('Does this file have column names already?')
if filename is not None:
with open(filename, "r") as infile:
reader = csv.reader(infile)
if button == 'Yes':
header_list = next(reader)
try:
data = list(reader) # read everything else into a list of rows
if button == 'No':
header_list = ['column' + str(x) for x in range(len(data[0]))]
except:
sg.PopupError('Error reading file')
sys.exit(69)
sg.SetOptions(element_padding=(0, 0))
layout = [[sg.Table(values=data,
headings=header_list,
max_col_width=25,
auto_size_columns=True,
justification='right',
alternating_row_color='lightblue',
num_rows=min(len(data), 20))]]
window = sg.Window('Table', grab_anywhere=False).Layout(layout)
event, values = window.Read()
sys.exit(69)
table_example()

View file

@ -0,0 +1,46 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import csv
filename = sg.PopupGetFile('filename to open', no_window=True, file_types=(("CSV Files","*.csv"),))
# --- populate table with file contents --- #
data = []
if filename is not None:
with open(filename, "r") as infile:
reader = csv.reader(infile)
try:
data = list(reader) # read everything else into a list of rows
except:
sg.PopupError('Error reading file')
sys.exit(69)
else:
sys.exit()
# sg.SetOptions(element_padding=(0,0))
headings = [data[0][x] for x in range(len(data[0]))]
layout = [[sg.Table(values=data[1:][:], headings=headings, max_col_width=25,
auto_size_columns=True, display_row_numbers=True, justification='right', num_rows=20, alternating_row_color='lightblue', key='_table_')],
[sg.Button('Read'), sg.Button('Double')],
[sg.T('Read = read which rows are selected')],[sg.T('Double = double the amount of data in the table')]]
window = sg.Window('Table', grab_anywhere=False, resizable=True).Layout(layout)
while True:
event, values = window.Read()
if event is None:
break
if event == 'Double':
for i in range(len(data)):
data.append(data[i])
window.FindElement('_table_').Update(values = data)
sg.Popup(event, values)
# print(event, values)
sys.exit(69)

View file

@ -0,0 +1,40 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import pandas as pd
def table_example():
sg.SetOptions(auto_size_buttons=True)
filename = sg.PopupGetFile('filename to open', no_window=True, file_types=(("CSV Files", "*.csv"),))
# --- populate table with file contents --- #
if filename == '':
sys.exit(69)
data = []
header_list = []
button = sg.PopupYesNo('Does this file have column names already?')
if filename is not None:
try:
df = pd.read_csv(filename, sep=',', engine='python', header=None) # Header=None means you directly pass the columns names to the dataframe
data = df.values.tolist() # read everything else into a list of rows
if button == 'Yes': # Press if you named your columns in the csv
header_list = df.iloc[0].tolist() # Uses the first row (which should be column names) as columns names
data = df[1:].values.tolist() # Drops the first row in the table (otherwise the header names and the first row will be the same)
elif button == 'No': # Press if you didn't name the columns in the csv
header_list = ['column' + str(x) for x in range(len(data[0]))] # Creates columns names for each column ('column0', 'column1', etc)
except:
sg.PopupError('Error reading file')
sys.exit(69)
layout = [[sg.Table(values=data, headings=header_list, display_row_numbers=True,
auto_size_columns=False, num_rows=min(25,len(data)))]]
window = sg.Window('Table', grab_anywhere=False)
event, values = window.Layout(layout).Read()
sys.exit(69)
table_example()

View file

@ -0,0 +1,80 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import csv
def TableSimulation():
"""
Display data in a table format
"""
sg.SetOptions(element_padding=(0,0))
menu_def = [['File', ['Open', 'Save', 'Exit']],
['Edit', ['Paste', ['Special', 'Normal',], 'Undo'],],
['Help', 'About...'],]
columm_layout = [[]]
MAX_ROWS = 20
MAX_COL = 10
for i in range(MAX_ROWS):
inputs = [sg.T('{}'.format(i), size=(4,1), justification='right')] + [sg.In(size=(10, 1), pad=(1, 1), justification='right', key=(i,j), do_not_clear=True) for j in range(MAX_COL)]
columm_layout.append(inputs)
layout = [ [sg.Menu(menu_def)],
[sg.T('Table Using Combos and Input Elements', font='Any 18')],
[sg.T('Type in a row, column and value. The form will update the values in realtime as you type'),
sg.In(key='inputrow', justification='right', size=(8,1), pad=(1,1), do_not_clear=True),
sg.In(key='inputcol', size=(8,1), pad=(1,1), justification='right', do_not_clear=True),
sg.In(key='value', size=(8,1), pad=(1,1), justification='right', do_not_clear=True)],
[sg.Column(columm_layout, size=(800,600), scrollable=True)] ]
window = sg.Window('Table', return_keyboard_events=True).Layout(layout)
while True:
event, values = window.Read()
# --- Process buttons --- #
if event is None or event == 'Exit':
break
elif event == 'About...':
sg.Popup('Demo of table capabilities')
elif event == 'Open':
filename = sg.PopupGetFile('filename to open', no_window=True, file_types=(("CSV Files","*.csv"),))
# --- populate table with file contents --- #
if filename is not None:
with open(filename, "r") as infile:
reader = csv.reader(infile)
try:
data = list(reader) # read everything else into a list of rows
except:
sg.PopupError('Error reading file')
continue
# clear the table
[window.FindElement((i,j)).Update('') for j in range(MAX_COL) for i in range(MAX_ROWS)]
for i, row in enumerate(data):
for j, item in enumerate(row):
location = (i,j)
try: # try the best we can at reading and filling the table
target_element = window.FindElement(location)
new_value = item
if target_element is not None and new_value != '':
target_element.Update(new_value)
except:
pass
# if a valid table location entered, change that location's value
try:
location = (int(values['inputrow']), int(values['inputcol']))
target_element = window.FindElement(location)
new_value = values['value']
if target_element is not None and new_value != '':
target_element.Update(new_value)
except:
pass
TableSimulation()

51
DemoPrograms/Demo_Tabs.py Normal file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
sg.SetOptions(background_color='cornsilk4', element_background_color='cornsilk2', input_elements_background_color='cornsilk2')
tab1_layout = [[sg.T('This is inside tab 1', background_color='darkslateblue', text_color='white')],
[sg.In(key='_in0_')]]
tab2_layout = [[sg.T('This is inside tab 2', background_color='tan1')],
[sg.In(key='_in2_')]]
tab3_layout = [[sg.T('This is inside tab 3')],
[sg.In(key='_in2_')]]
tab4_layout = [[sg.T('This is inside tab 4', background_color='darkseagreen')],
[sg.In(key='_in3_')]]
tab5_layout = [[sg.T('This is inside tab 5')],
[sg.In(key='_in4_')]]
layout = [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout, background_color='darkslateblue', key='_mykey_'),
sg.Tab('Tab 2', tab2_layout, background_color='tan1'),
sg.Tab('Tab 3', tab3_layout)]],
key='_group2_', title_color='red',
selected_title_color='green', tab_location='right'),
sg.TabGroup([[sg.Tab('Tab 4', tab4_layout,background_color='darkseagreen', key='_mykey_'),
sg.Tab('Tab 5', tab5_layout)]], key='_group1_', tab_location='top', selected_title_color='purple')],
[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout, background_color='darkslateblue', key='_mykey_'),
sg.Tab('Tab 2', tab2_layout, background_color='tan1'),
sg.Tab('Tab 3', tab3_layout)]],
key='_group3_', title_color='red',
selected_title_color='green', tab_location='left'),
sg.TabGroup([[sg.Tab('Tab 4', tab4_layout,background_color='darkseagreen', key='_mykey_'),
sg.Tab('Tab 5', tab5_layout)]], key='_group4_', tab_location='bottom', selected_title_color='purple')],
[sg.Button('Read')]]
window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout)
while True:
event, values = window.Read()
sg.PopupNonBlocking(event, values)
print(event, values)
if event is None: # always, always give a way out!
break

View file

@ -0,0 +1,39 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
sg.ChangeLookAndFeel('GreenTan')
tab2_layout = [[sg.T('This is inside tab 2')],
[sg.T('Tabs can be anywhere now!')]]
tab1_layout = [[sg.T('Type something here and click button'), sg.In(key='in')]]
tab3_layout = [[sg.T('This is inside tab 3')]]
tab4_layout = [[sg.T('This is inside tab 4')]]
tab_layout = [[sg.T('This is inside of a tab')]]
tab_group = sg.TabGroup([[sg.Tab('Tab 7', tab_layout), sg.Tab('Tab 8', tab_layout)]])
tab5_layout = [[sg.T('Watch this window')],
[sg.Output(size=(40,5))]]
tab6_layout = [[sg.T('This is inside tab 6')],
[sg.T('How about a second row of stuff in tab 6?'), tab_group]]
layout = [[sg.T('My Window!')], [sg.Frame('A Frame', layout=
[[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout), sg.Tab('Tab 2', tab2_layout)]]), sg.TabGroup([[sg.Tab('Tab3', tab3_layout), sg.Tab('Tab 4', tab4_layout)]])]])],
[sg.T('This text is on a row with a column'),sg.Column(layout=[[sg.T('In a column')],
[sg.TabGroup([[sg.Tab('Tab 5', tab5_layout), sg.Tab('Tab 6', tab6_layout)]])],
[sg.Button('Click me')]])],]
window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout).Finalize()
print('Are there enough tabs for you?')
while True:
event, values = window.Read()
print(event, values)
if event is None: # always, always give a way out!
break

View file

@ -0,0 +1,25 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
tab1_layout = [[sg.T('Tab 1')],
[sg.T('Put your layout in here')],
[sg.T('Input something'),sg.In(key='_IN0_')]]
tab2_layout = [[sg.T('Tab2')]]
layout = [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout), sg.Tab('Tab 2', tab2_layout)]], key='_TABGROUP_')],
[sg.Button('Read')]]
window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout)
while True:
event, values = window.Read()
sg.PopupNonBlocking('button = %s' % event, 'Values dictionary', values)
if event is None: # always, always give a way out!
break

View file

@ -0,0 +1,39 @@
#choose one of these are your starting point. Copy, paste, have fun
# ---------------------------------#
# DESIGN PATTERN 1 - Simple Window #
# ---------------------------------#
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[ sg.Text('My layout') ],
[ sg.CloseButton('Next Window')]]
window = sg.Window('My window').Layout(layout)
event, values = window.Read()
# --------------------------------------------------#
# DESIGN PATTERN 2 - Persistent Window (stays open) #
# --------------------------------------------------#
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[ sg.Text('My Window') ],
[ sg.Button('Read The Window')]]
window = sg.Window('My Window Title').Layout(layout)
while True: # Event Loop
event, values = window.Read()
if event is None: # if window closed with X
break
print(event, values)

View file

@ -0,0 +1,43 @@
import PySimpleGUI as sg
import time
# form that doen't block
# good for applications with an loop that polls hardware
def Timer():
sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0,0))
# Make a form, but don't use context manager
form = sg.FlexForm('Running Timer', no_titlebar=True, auto_size_buttons=False)
# Create a text element that will be updated with status information on the GUI itself
# Create the rows
form_rows = [[sg.Text('')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
[sg.ReadFormButton('Pause'), sg.ReadFormButton('Reset'), sg.Exit(button_color=('white','firebrick4'))]]
# Layout the rows of the form and perform a read. Indicate the form is non-blocking!
form.Layout(form_rows)
#
# your program's main loop
i = 0
paused = False
while (True):
# This is the code that reads and updates your window
button, values = form.ReadNonBlocking()
form.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100))
if values is None or button == 'Exit':
break
if button is 'Reset':
i=0
elif button is 'Pause':
paused = not paused
if not paused:
i += 1
# Your code begins here
time.sleep(.01)
# Broke out of main loop. Close the window.
form.CloseNonBlockingForm()
Timer()

View file

@ -0,0 +1,99 @@
import PySimpleGUI as sg
class keyboard():
def __init__(self, font=('Arial', 16)):
self.font = font
numberRow = '1234567890'
topRow = 'QWERTYUIOP'
midRow = 'ASDFGHJKL'
bottomRow = 'ZXCVBNM'
keyboard_layout = [[sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in numberRow] + [
sg.Button('', key='back', pad=(0, 0), size=(4, 2), font=self.font),
sg.Button('Esc', key='close', pad=(0, 0), size=(4, 2), font=self.font)],
[sg.T(' ' * 4)] + [sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in
topRow],
[sg.T(' ' * 11)] + [sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in
midRow],
[sg.T(' ' * 18)] + [sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in
bottomRow]]
self.window = sg.Window('keyboard',
grab_anywhere=True,
keep_on_top=True,
alpha_channel=0,
location=(850,350),
no_titlebar=True,
).Layout(keyboard_layout).Finalize()
self.hide()
def _keyboardhandler(self):
if self.event is not None:
if self.event == 'close':
self.hide()
elif len(self.event) == 1:
self.focus.Update(self.focus.Get() + self.event)
elif self.event == 'back':
Text = self.focus.Get()
if len(Text) > 0:
Text = Text[:-1]
self.focus.Update(Text)
def hide(self):
self.visible = False
self.window.Disappear()
def show(self):
self.visible = True
self.window.Reappear()
def togglevis(self):
if self.visible:
self.hide()
else:
self.show()
def update(self, focus):
self.event, _ = self.window.Read(timeout=0)
if focus is not None:
self.focus = focus
self._keyboardhandler()
def close(self):
self.window.Close()
class GUI():
def __init__(self):
layout = [[sg.Text('Enter Text')],
[sg.Input(size=(17, 1), key='input1', do_not_clear=True)],
[sg.InputText(size=(17, 1), key='input2', do_not_clear=True)],
[sg.Button('on-screen keyboard', key='keyboard')],
[sg.Button('close', key='close')]]
self.mainWindow = sg.Window('On-screen test',
grab_anywhere=True,
no_titlebar=False,
).Layout(layout).Finalize()
self.keyboard = keyboard()
self.focus = None
def run(self):
while True:
cur_focus = self.mainWindow.FindElementWithFocus()
if cur_focus is not None:
self.focus = cur_focus
event, values = self.mainWindow.Read(timeout=200, timeout_key='timeout')
if self.focus is not None:
self.keyboard.update(self.focus)
if event == 'keyboard':
self.keyboard.togglevis()
elif event == 'close' or event is None:
break
self.keyboard.close()
self.mainWindow.Close()
if __name__ == '__main__':
app = GUI()
app.run()

View file

@ -0,0 +1,45 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
treedata = sg.TreeData()
treedata.Insert("", '_A_', 'A', [1,2,3])
treedata.Insert("", '_B_', 'B', [4,5,6])
treedata.Insert("_A_", '_A1_', 'A1', ['can','be','anything'])
treedata.Insert("", '_C_', 'C', [])
treedata.Insert("_C_", '_C1_', 'C1', ['or'])
treedata.Insert("_A_", '_A2_', 'A2', [None, None])
treedata.Insert("_A1_", '_A3_', 'A30', ['getting deep'])
treedata.Insert("_C_", '_C2_', 'C2', ['nothing', 'at', 'all'])
for i in range(100):
treedata.Insert('_C_', i, i, [])
layout = [[ sg.Text('Tree Test') ],
[ sg.Tree(data=treedata, headings=['col1', 'col2', 'col3'],change_submits=True, auto_size_columns=True, num_rows=10, col0_width=10, key='_TREE_', show_expanded=True),
],
[ sg.Button('Read'), sg.Button('Update')]]
window = sg.Window('Tree Element Test').Layout(layout)
print(treedata)
while True: # Event Loop
event, values = window.Read()
if event is None:
break
if event == 'Update':
treedata = sg.TreeData()
treedata.Insert("", '_A_', 'A', [1, 2, 3])
treedata.Insert("", '_B_', 'B', [4, 5, 6])
treedata.Insert("_A_", '_A1_', 'A1', ['can', 'be', 'anything'])
treedata.Insert("", '_C_', 'C', [])
treedata.Insert("_C_", '_C1_', 'C1', ['or'])
treedata.Insert("_A_", '_A2_', 'A2', [None, None])
window.FindElement('_TREE_').Update(treedata)
elif event == 'Read':
print(event, values)

View file

@ -0,0 +1,84 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
import turtle
"""
Demo showing how to integrate drawing on a Canvas using Turtle with PySimpleGUI
The patern to follow:
Create Window & Finalize
Get the tkinter Canvas from the Canvas element
Draw on the tkinter Canvas using turtle commands.
Results are shown on the canvas immiedately after button press / drawing command
"""
layout = [[ sg.Text('My layout') ],
[sg.Canvas(size=(800,800), key='_canvas_')],
[ sg.Button('F'), sg.Button('B'), sg.Button('L'), sg.Button('R')],
[sg.Button('Spiral'), sg.Button('Inside Out'), sg.Button('Circles')]]
window = sg.Window('My new window').Layout(layout).Finalize()
canvas = window.FindElement('_canvas_').TKCanvas
t = turtle.RawTurtle(canvas)
t.pencolor("#ff0000") # Red
t.penup()
t.pendown()
while True: # Event Loop
event, values = window.Read()
if event is None:
break
if event == 'F':
t.forward(100)
elif event == 'B':
t.back(100)
elif event == 'L':
t.left(90)
elif event == 'R':
t.right(90)
elif event == 'Spiral':
canvas.config(bg='light green')
t.color("blue")
def sqrfunc(size):
for i in range(4):
t.fd(size)
t.left(90)
size = size - 5
sqrfunc(146)
sqrfunc(126)
sqrfunc(106)
sqrfunc(86)
sqrfunc(66)
sqrfunc(46)
sqrfunc(26)
elif event == 'Inside Out':
canvas.config(bg = "light green")
t.color("blue")
def sqrfunc(size):
for i in range(4):
t.fd(size)
t.left(90)
size = size + 5
sqrfunc(6)
sqrfunc(26)
sqrfunc(46)
sqrfunc(66)
sqrfunc(86)
sqrfunc(106)
sqrfunc(126)
sqrfunc(146)
elif event == 'Circles':
t.speed(0)
for i in range(400):
t.circle(2 * i*.25)
t.circle(-2 * i*.25)
t.left(i)

View file

@ -0,0 +1,21 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[ sg.Text('My Window') ],
[ sg.Button('Disappear')]]
window = sg.Window('My window').Layout(layout)
while True:
event, values = window.Read()
if event is None:
break
if event == 'Disappear':
window.Disappear()
sg.Popup('Click OK to make window reappear')
window.Reappear()

Some files were not shown because too many files have changed in this diff Show more