Moved all demo programs into subfolder
This commit is contained in:
parent
a294d0a347
commit
1881bd7126
107 changed files with 51 additions and 144 deletions
54
DemoPrograms/Demo_All_Widgets.py
Normal file
54
DemoPrograms/Demo_All_Widgets.py
Normal 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)
|
||||
|
||||
|
42
DemoPrograms/Demo_Borderless_Window.py
Normal file
42
DemoPrograms/Demo_Borderless_Window.py
Normal 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
|
||||
|
||||
|
33
DemoPrograms/Demo_Button_Click.py
Normal file
33
DemoPrograms/Demo_Button_Click.py
Normal 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)
|
51
DemoPrograms/Demo_Button_States.py
Normal file
51
DemoPrograms/Demo_Button_States.py
Normal 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
|
50
DemoPrograms/Demo_Buttons_Mac.py
Normal file
50
DemoPrograms/Demo_Buttons_Mac.py
Normal 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()
|
75
DemoPrograms/Demo_Buttons_Nice_Graphics.py
Normal file
75
DemoPrograms/Demo_Buttons_Nice_Graphics.py
Normal file
File diff suppressed because one or more lines are too long
15
DemoPrograms/Demo_Calendar.py
Normal file
15
DemoPrograms/Demo_Calendar.py
Normal 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'])
|
24
DemoPrograms/Demo_Canvas.py
Normal file
24
DemoPrograms/Demo_Canvas.py
Normal 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")
|
48
DemoPrograms/Demo_Change_Submits_InputText.py
Normal file
48
DemoPrograms/Demo_Change_Submits_InputText.py
Normal 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
32
DemoPrograms/Demo_Chat.py
Normal 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)
|
||||
|
60
DemoPrograms/Demo_Chat_With_History.py
Normal file
60
DemoPrograms/Demo_Chat_With_History.py
Normal 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()
|
76
DemoPrograms/Demo_Chatterbot.py
Normal file
76
DemoPrograms/Demo_Chatterbot.py
Normal 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
1728
DemoPrograms/Demo_Color.py
Normal file
File diff suppressed because it is too large
Load diff
703
DemoPrograms/Demo_Color_Names.py
Normal file
703
DemoPrograms/Demo_Color_Names.py
Normal 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)
|
113
DemoPrograms/Demo_Color_Names_Smaller_List.py
Normal file
113
DemoPrograms/Demo_Color_Names_Smaller_List.py
Normal 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)
|
25
DemoPrograms/Demo_Columns.py
Normal file
25
DemoPrograms/Demo_Columns.py
Normal 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)
|
||||
|
45
DemoPrograms/Demo_Compare_Files.py
Normal file
45
DemoPrograms/Demo_Compare_Files.py
Normal 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()
|
242
DemoPrograms/Demo_DOC_Viewer_PIL.py
Normal file
242
DemoPrograms/Demo_DOC_Viewer_PIL.py
Normal 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")
|
25
DemoPrograms/Demo_Design_Pattern_Persistent_Window.py
Normal file
25
DemoPrograms/Demo_Design_Pattern_Persistent_Window.py
Normal 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()
|
60
DemoPrograms/Demo_Design_Patterns.py
Normal file
60
DemoPrograms/Demo_Design_Patterns.py
Normal 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
|
90
DemoPrograms/Demo_Desktop_Floating_Toolbar.py
Normal file
90
DemoPrograms/Demo_Desktop_Floating_Toolbar.py
Normal 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()
|
||||
|
110
DemoPrograms/Demo_Desktop_Widget_CPU_Dashboard.py
Normal file
110
DemoPrograms/Demo_Desktop_Widget_CPU_Dashboard.py
Normal 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)
|
79
DemoPrograms/Demo_Desktop_Widget_CPU_Graph.py
Normal file
79
DemoPrograms/Demo_Desktop_Widget_CPU_Graph.py
Normal 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()
|
99
DemoPrograms/Demo_Desktop_Widget_CPU_Utilization.py
Normal file
99
DemoPrograms/Demo_Desktop_Widget_CPU_Utilization.py
Normal 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()
|
45
DemoPrograms/Demo_Desktop_Widget_CPU_Utilization_Simple.py
Normal file
45
DemoPrograms/Demo_Desktop_Widget_CPU_Utilization_Simple.py
Normal 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()
|
108
DemoPrograms/Demo_Desktop_Widget_Email_Notification.py
Normal file
108
DemoPrograms/Demo_Desktop_Widget_Email_Notification.py
Normal 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()
|
71
DemoPrograms/Demo_Desktop_Widget_Timer.py
Normal file
71
DemoPrograms/Demo_Desktop_Widget_Timer.py
Normal 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 --------
|
138
DemoPrograms/Demo_Desktop_Widget_psutil_Dashboard.py
Normal file
138
DemoPrograms/Demo_Desktop_Widget_psutil_Dashboard.py
Normal 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)
|
45
DemoPrograms/Demo_Disable_Elements.py
Normal file
45
DemoPrograms/Demo_Disable_Elements.py
Normal 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)
|
62
DemoPrograms/Demo_DuplicateFileFinder.py
Normal file
62
DemoPrograms/Demo_DuplicateFileFinder.py
Normal 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)
|
75
DemoPrograms/Demo_EXE_Maker.py
Normal file
75
DemoPrograms/Demo_EXE_Maker.py
Normal 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()
|
||||
|
66
DemoPrograms/Demo_Fill_Form.py
Normal file
66
DemoPrograms/Demo_Fill_Form.py
Normal 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()
|
77
DemoPrograms/Demo_Floating_Toolbar.py
Normal file
77
DemoPrograms/Demo_Floating_Toolbar.py
Normal 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()
|
||||
|
49
DemoPrograms/Demo_Font_Previewer.py
Normal file
49
DemoPrograms/Demo_Font_Previewer.py
Normal 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))
|
30
DemoPrograms/Demo_Font_Sizer.py
Normal file
30
DemoPrograms/Demo_Font_Sizer.py
Normal 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.")
|
34
DemoPrograms/Demo_Font_String.py
Normal file
34
DemoPrograms/Demo_Font_String.py
Normal 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)
|
54
DemoPrograms/Demo_GoodColors.py
Normal file
54
DemoPrograms/Demo_GoodColors.py
Normal 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()
|
53
DemoPrograms/Demo_Google_TTS.py
Normal file
53
DemoPrograms/Demo_Google_TTS.py
Normal 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
|
33
DemoPrograms/Demo_Graph_Drawing.py
Normal file
33
DemoPrograms/Demo_Graph_Drawing.py
Normal 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)
|
67
DemoPrograms/Demo_Graph_Element.py
Normal file
67
DemoPrograms/Demo_Graph_Element.py
Normal 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()
|
40
DemoPrograms/Demo_Graph_Element_Sine_Wave.py
Normal file
40
DemoPrograms/Demo_Graph_Element_Sine_Wave.py
Normal 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()
|
70
DemoPrograms/Demo_Graph_Noise.py
Normal file
70
DemoPrograms/Demo_Graph_Noise.py
Normal 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()
|
66
DemoPrograms/Demo_Graph__Element.py
Normal file
66
DemoPrograms/Demo_Graph__Element.py
Normal 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)
|
85
DemoPrograms/Demo_HowDoI.py
Normal file
85
DemoPrograms/Demo_HowDoI.py
Normal 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()
|
||||
|
120
DemoPrograms/Demo_Img_Viewer.py
Normal file
120
DemoPrograms/Demo_Img_Viewer.py
Normal 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))
|
||||
|
||||
|
29
DemoPrograms/Demo_Keyboard.py
Normal file
29
DemoPrograms/Demo_Keyboard.py
Normal 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)
|
||||
|
||||
|
25
DemoPrograms/Demo_Keyboard_Realtime.py
Normal file
25
DemoPrograms/Demo_Keyboard_Realtime.py
Normal 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
|
44
DemoPrograms/Demo_Keypad.py
Normal file
44
DemoPrograms/Demo_Keypad.py
Normal 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
|
176
DemoPrograms/Demo_LED_Clock_Weather.py
Normal file
176
DemoPrograms/Demo_LED_Clock_Weather.py
Normal file
File diff suppressed because one or more lines are too long
51
DemoPrograms/Demo_LED_Indicators.py
Normal file
51
DemoPrograms/Demo_LED_Indicators.py
Normal 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')
|
227
DemoPrograms/Demo_MIDI_Player.py
Normal file
227
DemoPrograms/Demo_MIDI_Player.py
Normal 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()
|
||||
|
65
DemoPrograms/Demo_Machine_Learning.py
Normal file
65
DemoPrograms/Demo_Machine_Learning.py
Normal 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()
|
115
DemoPrograms/Demo_Matplotlib.py
Normal file
115
DemoPrograms/Demo_Matplotlib.py
Normal 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()
|
65
DemoPrograms/Demo_Matplotlib_Animated.py
Normal file
65
DemoPrograms/Demo_Matplotlib_Animated.py
Normal 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()
|
67
DemoPrograms/Demo_Matplotlib_Animated_Scatter.py
Normal file
67
DemoPrograms/Demo_Matplotlib_Animated_Scatter.py
Normal 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()
|
906
DemoPrograms/Demo_Matplotlib_Browser.py
Normal file
906
DemoPrograms/Demo_Matplotlib_Browser.py
Normal 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)
|
||||
|
||||
|
674
DemoPrograms/Demo_Matplotlib_Ping_Graph.py
Normal file
674
DemoPrograms/Demo_Matplotlib_Ping_Graph.py
Normal 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()
|
110
DemoPrograms/Demo_Matplotlib_Ping_Graph_Large.py
Normal file
110
DemoPrograms/Demo_Matplotlib_Ping_Graph_Large.py
Normal 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()
|
||||
|
||||
|
66
DemoPrograms/Demo_Media_Player.py
Normal file
66
DemoPrograms/Demo_Media_Player.py
Normal 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()
|
||||
|
82
DemoPrograms/Demo_Menu_With_Toolbar.py
Normal file
82
DemoPrograms/Demo_Menu_With_Toolbar.py
Normal 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,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'
|
||||
|
||||
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()
|
||||
|
64
DemoPrograms/Demo_Menus.py
Normal file
64
DemoPrograms/Demo_Menus.py
Normal 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()
|
46
DemoPrograms/Demo_Multiple_Windows_Experimental.py
Normal file
46
DemoPrograms/Demo_Multiple_Windows_Experimental.py
Normal 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)
|
98
DemoPrograms/Demo_Multithreaded_Logging.py
Normal file
98
DemoPrograms/Demo_Multithreaded_Logging.py
Normal 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()
|
74
DemoPrograms/Demo_Nice_Buttons.py
Normal file
74
DemoPrograms/Demo_Nice_Buttons.py
Normal file
File diff suppressed because one or more lines are too long
82
DemoPrograms/Demo_NonBlocking_Form.py
Normal file
82
DemoPrograms/Demo_NonBlocking_Form.py
Normal 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()
|
40
DemoPrograms/Demo_Notification_Window_Alpha_Channel.py
Normal file
40
DemoPrograms/Demo_Notification_Window_Alpha_Channel.py
Normal 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()
|
61
DemoPrograms/Demo_OpenCV.py
Normal file
61
DemoPrograms/Demo_OpenCV.py
Normal 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()
|
88
DemoPrograms/Demo_OpenCV_Simple_GUI.py
Normal file
88
DemoPrograms/Demo_OpenCV_Simple_GUI.py
Normal 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()
|
58
DemoPrograms/Demo_OpenCV_Webcam.py
Normal file
58
DemoPrograms/Demo_OpenCV_Webcam.py
Normal 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()
|
187
DemoPrograms/Demo_PDF_Viewer.py
Normal file
187
DemoPrograms/Demo_PDF_Viewer.py
Normal 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))
|
130
DemoPrograms/Demo_PNG_Thumbnail_Viewer.py
Normal file
130
DemoPrograms/Demo_PNG_Thumbnail_Viewer.py
Normal 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)
|
||||
|
78
DemoPrograms/Demo_PNG_Viewer.py
Normal file
78
DemoPrograms/Demo_PNG_Viewer.py
Normal 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)))
|
||||
|
656
DemoPrograms/Demo_PSG_SDK_Quick_Ref.py
Normal file
656
DemoPrograms/Demo_PSG_SDK_Quick_Ref.py
Normal 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)
|
65
DemoPrograms/Demo_Password_Login.py
Normal file
65
DemoPrograms/Demo_Password_Login.py
Normal 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!!')
|
57
DemoPrograms/Demo_Pi_LEDs.py
Normal file
57
DemoPrograms/Demo_Pi_LEDs.py
Normal 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')
|
101
DemoPrograms/Demo_Pi_Robotics.py
Normal file
101
DemoPrograms/Demo_Pi_Robotics.py
Normal 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()
|
665
DemoPrograms/Demo_Ping_Line_Graph.py
Normal file
665
DemoPrograms/Demo_Ping_Line_Graph.py
Normal 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
186
DemoPrograms/Demo_Pong.py
Normal 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()
|
||||
|
||||
|
||||
|
33
DemoPrograms/Demo_Popup_Custom.py
Normal file
33
DemoPrograms/Demo_Popup_Custom.py
Normal 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))
|
37
DemoPrograms/Demo_Popups.py
Normal file
37
DemoPrograms/Demo_Popups.py
Normal 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')
|
89
DemoPrograms/Demo_Progress_Meters.py
Normal file
89
DemoPrograms/Demo_Progress_Meters.py
Normal 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()
|
87
DemoPrograms/Demo_Pyplot_Bar_Chart.py
Normal file
87
DemoPrograms/Demo_Pyplot_Bar_Chart.py
Normal 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()
|
79
DemoPrograms/Demo_Pyploy_Bar_Chart2.py
Normal file
79
DemoPrograms/Demo_Pyploy_Bar_Chart2.py
Normal 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()
|
77
DemoPrograms/Demo_Script_Launcher.py
Normal file
77
DemoPrograms/Demo_Script_Launcher.py
Normal 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()
|
||||
|
30
DemoPrograms/Demo_Script_Parameters.py
Normal file
30
DemoPrograms/Demo_Script_Parameters.py
Normal 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")
|
37
DemoPrograms/Demo_Spinner_Compound_Element.py
Normal file
37
DemoPrograms/Demo_Spinner_Compound_Element.py
Normal 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)
|
23
DemoPrograms/Demo_Super_Simple_Form.py
Normal file
23
DemoPrograms/Demo_Super_Simple_Form.py
Normal 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'])
|
45
DemoPrograms/Demo_Table_CSV.py
Normal file
45
DemoPrograms/Demo_Table_CSV.py
Normal 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()
|
46
DemoPrograms/Demo_Table_Element.py
Normal file
46
DemoPrograms/Demo_Table_Element.py
Normal 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)
|
40
DemoPrograms/Demo_Table_Pandas.py
Normal file
40
DemoPrograms/Demo_Table_Pandas.py
Normal 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()
|
80
DemoPrograms/Demo_Table_Simulation.py
Normal file
80
DemoPrograms/Demo_Table_Simulation.py
Normal 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
51
DemoPrograms/Demo_Tabs.py
Normal 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
|
39
DemoPrograms/Demo_Tabs_Nested.py
Normal file
39
DemoPrograms/Demo_Tabs_Nested.py
Normal 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
|
25
DemoPrograms/Demo_Tabs_Simple.py
Normal file
25
DemoPrograms/Demo_Tabs_Simple.py
Normal 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
|
39
DemoPrograms/Demo_Template.py
Normal file
39
DemoPrograms/Demo_Template.py
Normal 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)
|
43
DemoPrograms/Demo_Timer.py
Normal file
43
DemoPrograms/Demo_Timer.py
Normal 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()
|
99
DemoPrograms/Demo_Touch_Keyboard.py
Normal file
99
DemoPrograms/Demo_Touch_Keyboard.py
Normal 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()
|
45
DemoPrograms/Demo_Tree_Element.py
Normal file
45
DemoPrograms/Demo_Tree_Element.py
Normal 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)
|
84
DemoPrograms/Demo_Turtle.py
Normal file
84
DemoPrograms/Demo_Turtle.py
Normal 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)
|
21
DemoPrograms/Demo_Window_Disappear.py
Normal file
21
DemoPrograms/Demo_Window_Disappear.py
Normal 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
Loading…
Add table
Add a link
Reference in a new issue