Merge pull request #4377 from PySimpleGUI/Dev-latest
Removed the very old demo programs folder
This commit is contained in:
commit
e3b6d58765
156 changed files with 0 additions and 17364 deletions
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 95 KiB |
Binary file not shown.
Before Width: | Height: | Size: 298 KiB |
|
@ -1,54 +0,0 @@
|
|||
#!/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)
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -1,29 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
import random
|
||||
|
||||
BAR_WIDTH = 50
|
||||
BAR_SPACING = 75
|
||||
EDGE_OFFSET = 3
|
||||
GRAPH_SIZE = (500,500)
|
||||
DATA_SIZE = (500,500)
|
||||
|
||||
graph = sg.Graph(GRAPH_SIZE, (0,0), DATA_SIZE)
|
||||
|
||||
layout = [[sg.Text('Bar graphs using PySimpleGUI')],
|
||||
[graph],
|
||||
[sg.Button('OK')]]
|
||||
|
||||
window = sg.Window('Window Title', layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
graph.Erase()
|
||||
if event is None:
|
||||
break
|
||||
|
||||
for i in range(7):
|
||||
graph_value = random.randint(0, 400)
|
||||
graph.DrawRectangle(top_left=(i * BAR_SPACING + EDGE_OFFSET, graph_value),
|
||||
bottom_right=(i * BAR_SPACING + EDGE_OFFSET + BAR_WIDTH, 0), fill_color='blue')
|
||||
graph.DrawText(text=graph_value, location=(i*BAR_SPACING+EDGE_OFFSET+25, graph_value+10))
|
||||
window.Close()
|
|
@ -1,40 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
import os
|
||||
import base64
|
||||
|
||||
'''
|
||||
Base64 Encoder - encodes a folder of PNG files and creates a .py file with definitions
|
||||
'''
|
||||
|
||||
OUTPUT_FILENAME = 'output.py'
|
||||
|
||||
def main():
|
||||
# folder = r'C:\Python\PycharmProjects\GooeyGUI\Uno Cards'
|
||||
folder=''
|
||||
folder = sg.PopupGetFolder('Source folder for images\nImages will be encoded and results saved to %s'%OUTPUT_FILENAME,
|
||||
title='Base64 Encoder',
|
||||
default_path=folder, initial_folder=folder )
|
||||
|
||||
if folder is None or folder == '':
|
||||
sg.PopupCancel('Cancelled - No valid folder entered')
|
||||
return
|
||||
try:
|
||||
namesonly = [f for f in os.listdir(folder) if f.endswith('.png') or f.endswith('.ico') or f.endswith('.gif')]
|
||||
except:
|
||||
sg.PopupCancel('Cancelled - No valid folder entered')
|
||||
return
|
||||
|
||||
outfile = open(os.path.join(folder, OUTPUT_FILENAME), 'w')
|
||||
|
||||
for i, file in enumerate(namesonly):
|
||||
contents = open(os.path.join(folder, file), 'rb').read()
|
||||
encoded = base64.b64encode(contents)
|
||||
outfile.write('\n{} = {}\n\n'.format(file[:file.index(".")], encoded))
|
||||
sg.OneLineProgressMeter('Base64 Encoding', i+1, len(namesonly),key='_METER_')
|
||||
|
||||
outfile.close()
|
||||
sg.Popup('Completed!', 'Encoded %s files'%(i+1))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,42 +0,0 @@
|
|||
#!/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.Button('Start', button_color=('white', 'black')),
|
||||
sg.Button('Stop', button_color=('gray50', 'black')),
|
||||
sg.Button('Reset', button_color=('white', '#9B0023')),
|
||||
sg.Button('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
|
||||
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#!/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.Button('Start', button_color=('white', 'black'), key='start'),
|
||||
sg.Button('Stop', button_color=('white', 'black'), key='stop'),
|
||||
sg.Button('Reset', button_color=('white', 'firebrick3'), key='reset'),
|
||||
sg.Button('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(timeout=100)
|
||||
if event is None:
|
||||
sys.exit(69)
|
||||
winsound.PlaySound("ButtonClick.wav", 1) if event != sg.TIMEOUT_KEY else None
|
|
@ -1,43 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
else:
|
||||
import PySimpleGUI27 as sg
|
||||
|
||||
|
||||
"""
|
||||
Demo Button Function Calls
|
||||
Typically GUI packages in Python (tkinter, Qt, WxPython, etc) will call a user's function
|
||||
when a button is clicked. This "Callback" model versus "Message Passing" model is a fundamental
|
||||
difference between PySimpleGUI and all other GUI.
|
||||
|
||||
There are NO BUTTON CALLBACKS in the PySimpleGUI Architecture
|
||||
|
||||
It is quite easy to simulate these callbacks however. The way to do this is to add the calls
|
||||
to your Event Loop
|
||||
"""
|
||||
|
||||
def callback_function1():
|
||||
sg.Popup('In Callback Function 1')
|
||||
print('In the callback function 1')
|
||||
|
||||
def callback_function2():
|
||||
sg.Popup('In Callback Function 2')
|
||||
print('In the callback function 2')
|
||||
|
||||
layout = [ [sg.Text('Demo of Button Callbacks')],
|
||||
[sg.Button('Button 1'), sg.Button('Button 2')] ]
|
||||
|
||||
window = sg.Window('Button Callback Simulation').Layout(layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
if event is None:
|
||||
break
|
||||
elif event == 'Button 1':
|
||||
callback_function1() # call the "Callback" function
|
||||
elif event == 'Button 2':
|
||||
callback_function2() # call the "Callback" function
|
||||
|
||||
window.Close()
|
|
@ -1,51 +0,0 @@
|
|||
#!/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.Button('Start', button_color=('white', 'black'), key='_Start_'),
|
||||
sg.Button('Stop', button_color=('white', 'black'), key='_Stop_'),
|
||||
sg.Button('Reset', button_color=('white', 'firebrick3'), key='_Reset_'),
|
||||
sg.Button('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 == '_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
|
File diff suppressed because one or more lines are too long
|
@ -1,50 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
import time
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
else:
|
||||
import PySimpleGUI27 as sg
|
||||
|
||||
def show_win():
|
||||
sg.SetOptions(border_width=0, margins=(0,0), element_padding=(5,3))
|
||||
|
||||
|
||||
frame_layout = [ [sg.Button('', image_data=mac_red, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_exit_'),
|
||||
sg.Button('', image_data=mac_orange, button_color=('white', sg.COLOR_SYSTEM_DEFAULT)),
|
||||
sg.Button('', image_data=mac_green, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_minimize_'),
|
||||
sg.Text(' '*40)],]
|
||||
|
||||
layout = [[sg.Frame('',frame_layout)],
|
||||
[sg.T('')],
|
||||
[ sg.Text(' My Mac-alike window', size=(25,2)) ],]
|
||||
|
||||
window = sg.Window('My new window',
|
||||
no_titlebar=True,
|
||||
grab_anywhere=True,
|
||||
alpha_channel=0,
|
||||
).Layout(layout).Finalize()
|
||||
|
||||
for i in range(100):
|
||||
window.SetAlpha(i/100)
|
||||
time.sleep(.01)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
if event is None or event == '_exit_':
|
||||
break
|
||||
if event == '_minimize_':
|
||||
# window.Minimize() # cannot minimize a window with no titlebar
|
||||
pass
|
||||
print(event, values)
|
||||
|
||||
|
||||
|
||||
mac_red = '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'
|
||||
|
||||
mac_green = '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'
|
||||
|
||||
mac_orange = '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'
|
||||
|
||||
|
||||
show_win()
|
File diff suppressed because one or more lines are too long
|
@ -1,15 +0,0 @@
|
|||
#!/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'])
|
|
@ -1,22 +0,0 @@
|
|||
#!/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.Button('Red'), sg.Button('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 in ('Blue', 'Red'):
|
||||
window.FindElement('canvas').TKCanvas.itemconfig(cir, fill=event)
|
|
@ -1,25 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
"""
|
||||
Allows you to "browse" through the look and feel settings. Click on one and you'll see a
|
||||
Popup window using the color scheme you chose. It's a simply little program that demonstrates
|
||||
how snappy a GUI can feel if you enable an element's events rather than waiting on a button click.
|
||||
In this program, as soon as a listbox entry is clicked, the read returns.
|
||||
"""
|
||||
|
||||
sg.ChangeLookAndFeel('GreenTan')
|
||||
|
||||
layout = [ [sg.Text('Look and Feel Browser')],
|
||||
[sg.Text('Click a look and feel color to see demo window')],
|
||||
[sg.Listbox(values=sg.list_of_look_and_feel_values(), size=(20,12), key='-LIST-', enable_events=True)],
|
||||
[sg.Button('Show Window'), sg.Button('Exit')] ]
|
||||
|
||||
window = sg.Window('Look and Feel Browser', layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.read()
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
sg.change_look_and_feel(values['-LIST-'][0])
|
||||
sg.popup_get_text('This is {}'.format(values['-LIST-'][0]))
|
||||
window.close()
|
|
@ -1,48 +0,0 @@
|
|||
#!/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
|
|
@ -1,41 +0,0 @@
|
|||
#!/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
|
||||
|
||||
To see this program RUN on the web go here:
|
||||
https://repl.it/@PySimpleGUI/Chat-Application-Demo
|
||||
|
||||
Note that the size of the display on repl.it is smaller than most, so the sizes of the
|
||||
Multiline and Output text areas were reduced in the online version. Nothing else was changed
|
||||
'''
|
||||
|
||||
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.Button('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 == 'SEND':
|
||||
query = value['query'].rstrip()
|
||||
# EXECUTE YOUR COMMAND HERE
|
||||
print('The command you entered was {}'.format(query))
|
||||
elif event in (None, 'EXIT'): # quit if exit button or X
|
||||
break
|
||||
sys.exit(69)
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#!/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.Button('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 == '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 in (None, '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()
|
|
@ -1,76 +0,0 @@
|
|||
#!/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 != '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)
|
|
@ -1,97 +0,0 @@
|
|||
#!/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
|
||||
|
||||
from gtts import gTTS
|
||||
from pygame import mixer
|
||||
import time
|
||||
import os
|
||||
|
||||
'''
|
||||
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('NeutralBlue')
|
||||
# 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
|
||||
|
||||
def speak(text):
|
||||
global i
|
||||
tts = gTTS(text=text, 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
|
||||
|
||||
i = 0
|
||||
mixer.init()
|
||||
|
||||
# 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 != '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)
|
||||
speak(str(response))
|
File diff suppressed because it is too large
Load diff
|
@ -1,709 +0,0 @@
|
|||
#!/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 --
|
||||
color_list = [key for key in color_map]
|
||||
for rows in range(40):
|
||||
|
||||
row = []
|
||||
for i in range(12):
|
||||
try:
|
||||
color = color_list[rows+40*i]
|
||||
row.append(sg.Button(color, button_color=('black', color), key=color, tooltip=color_map[color]))
|
||||
except:
|
||||
pass
|
||||
layout.append(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)
|
|
@ -1,132 +0,0 @@
|
|||
#!/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 = []
|
||||
layout = []
|
||||
# -- Create primary color viewer window --
|
||||
for rows in range(40):
|
||||
|
||||
row = []
|
||||
for i in range(12):
|
||||
try:
|
||||
color = COLORS[rows+40*i]
|
||||
row.append(sg.Button(color, button_color=('black', color), key=color))
|
||||
except:
|
||||
pass
|
||||
layout.append(row)
|
||||
|
||||
|
||||
# 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)
|
|
@ -1,48 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
# this one long import has the effect of making the code more compact as there is no 'sg.' prefix required for Elements
|
||||
from PySimpleGUI import InputCombo, Combo, Multiline, ML, MLine, Checkbox, CB, Check, Button, B, Btn, ButtonMenu, Canvas, Column, Col, Combo, Frame, Graph, Image, InputText, Input, In, Listbox, LBox, Menu, Multiline, ML, MLine, OptionMenu, Output, Pane, ProgressBar, Radio, Slider, Spin, StatusBar, Tab, TabGroup, Table, Text, Txt, T, Tree, TreeData, VerticalSeparator, Window, Sizer
|
||||
|
||||
"""
|
||||
Demo Columns and Frames
|
||||
Demonstrates using mixture of Column and Frame elements to create a nice window layout.
|
||||
A couple of the concepts shown here include:
|
||||
* Using Columns and Frames with specific sizes on them
|
||||
* Importing all required classes so that "sg." is not required on any objects. This makes the code more compact and readable
|
||||
|
||||
There are 3 columns. Two are side by side at the top and the third is along the bottom
|
||||
"""
|
||||
|
||||
sg.change_look_and_feel('GreenTan')
|
||||
|
||||
col2 = Column([[Frame('Accounts:', [[Column([[Listbox(['Account '+str(i) for i in range(1,16)], key='-ACCT-LIST-', size=(15,20)),]],size=(150,400))]])]], pad=(0,0))
|
||||
|
||||
col1 = Column([
|
||||
# Categories frame
|
||||
[Frame('Categories:', [[Radio('Websites', 'radio1', default=True, key='-WEBSITES-', size=(10, 1)),
|
||||
Radio('Software', 'radio1',key='-SOFTWARE-', size=(10, 1))]],)],
|
||||
# Information frame
|
||||
[Frame('Information:', [[Column([[Text('Account:')],
|
||||
[Input(key='-ACCOUNT-IN-', size=(19, 1))],
|
||||
[Text('User Id:')],
|
||||
[Input(key='-USERID-IN-', size=(19, 1)), Button('Copy', key='-USERID-')],
|
||||
[Text('Password:')],
|
||||
[Input(key='-PW-IN-', size=(19, 1)), Button('Copy', key='-PASS-')],
|
||||
[Text('Location:')],
|
||||
[Input(key='-LOC-IN-', size=(19, 1)), Button('Copy', key='-LOC')],
|
||||
[Text('Notes:')],
|
||||
[Multiline(key='-NOTES-', size=(25, 5))],
|
||||
], size=(235,350),pad=(0,0))]])],], pad=(0,0))
|
||||
|
||||
col3 = Column([[Frame('Actions:', [[Column([[Button('Save'), Button('Clear'), Button('Delete'),]], size=(450,45), pad=(0,0))]])]], pad=(0,0))
|
||||
|
||||
layout = [ [col1, col2],
|
||||
[col3]]
|
||||
|
||||
window = Window('Passwords', layout)
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
print(event, values)
|
||||
if event is None:
|
||||
break
|
||||
window.close()
|
|
@ -1,25 +0,0 @@
|
|||
#!/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)
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
# Import the elements individually to save space
|
||||
from PySimpleGUI import InputCombo, Combo, Multiline, ML, MLine, Checkbox, CB, Check, Button, B, Btn, ButtonMenu,BMenu, Canvas, Column, Col, Combo, DropDown, Drop, DD, Frame, Graph, Image, InputText, Input, In, I, Listbox, LBox, LB, Menu, Multiline, ML, MLine, OptionMenu, Output, Pane, ProgressBar, Prog, PBar, Radio, R, Rad, Sizer, Slider, Spin, StatusBar, Tab, TabGroup, Table, Text, Txt, T, Tree, TreeData, VerticalSeparator, Window, Print
|
||||
|
||||
|
||||
"""
|
||||
Demo - Compact Layouts and Element Renaming
|
||||
|
||||
Some layouts contain many many elements such that space becomes a premium. For experienced PySimpleGUI
|
||||
programmers, there is little additional knowledge to be gained by writing
|
||||
sg.Text('My text')
|
||||
rather than using one of the shortcuts such as
|
||||
sg.T('My text')
|
||||
However, even with shortcut usage, you continue to have the package prefix of
|
||||
sg.
|
||||
That's 3 characters per element that are added to your layout!
|
||||
The very long import statement st the top can be copied into your code to give you the ability to write
|
||||
T('My text')
|
||||
|
||||
If you don't want to use that very-long import or perhaps want to use your own shortcut names, you can easily
|
||||
create your shortcut by simple assignment:
|
||||
T = sg.Text
|
||||
This enables you to use T just as if you imported the Class T from PySimpleGUI. You could develop your own
|
||||
template that you copy and paste at the top of all of your PySimpleGUI programs. Or perhaps perform an import
|
||||
of those assignments from a .py file you create.
|
||||
|
||||
Note that you may lose docstrings in PyCharm using these shortcuts. You can still see the parameters when pressing
|
||||
Control+P, but the Control+Q doesn't bring up the full list of parms and their descriptions. Looking for a fix
|
||||
for this.
|
||||
|
||||
PLEASE OH PLEASE OH PLEASE NEVER EVER EVER do this:
|
||||
from PySimpleGUI import *
|
||||
There is a bot scanning GitHub for this statement. If found in your code, a squad of assassins will be dispatched
|
||||
from the PySimpleGUI headquarters and you will be hunted down and forced to change your code.
|
||||
"""
|
||||
|
||||
# A user created shortcut....
|
||||
# Suppose this user's layout contains many Multiline Elements. It could be advantageous to have a single letter
|
||||
# shortcut version for Multiline
|
||||
M = sg.Multiline
|
||||
|
||||
# This layout uses the user defined "M" element as well as the PySimpleGUI Button shortcut, B.
|
||||
layout = [[M(size=(30,3))],
|
||||
[B('OK')]]
|
||||
|
||||
event, values = Window('Shortcuts', layout).read()
|
||||
sg.popup_scrolled(event, values)
|
|
@ -1,45 +0,0 @@
|
|||
#!/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()
|
|
@ -1,170 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# John Conway's "Game of Life" using a GUI.
|
||||
# Copyright (C) 2018 PySimpleGUI.org
|
||||
# GUI provided by PySimpleGUI.
|
||||
# Core game engine provied by Christian Jacobs
|
||||
|
||||
# An implementation of Conway's Game of Life in Python.
|
||||
|
||||
# Copyright (C) 2013 Christian Jacobs.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import numpy
|
||||
import PySimpleGUI as sg # Take your pick! Tkinter
|
||||
# import PySimpleGUIWeb as sg # Or the Web! (Remi!)
|
||||
|
||||
BOX_SIZE = 15
|
||||
|
||||
class GameOfLife:
|
||||
|
||||
def __init__(self, N=20, T=200):
|
||||
""" Set up Conway's Game of Life. """
|
||||
# Here we create two grids to hold the old and new configurations.
|
||||
# This assumes an N*N grid of points.
|
||||
# Each point is either alive or dead, represented by integer values of 1 and 0, respectively.
|
||||
self.N = N
|
||||
self.old_grid = numpy.zeros(N * N, dtype='i').reshape(N, N)
|
||||
self.new_grid = numpy.zeros(N * N, dtype='i').reshape(N, N)
|
||||
self.T = T # The maximum number of generations
|
||||
|
||||
# Set up a random initial configuration for the grid.
|
||||
for i in range(0, self.N):
|
||||
for j in range(0, self.N):
|
||||
self.old_grid[i][j] = 0
|
||||
self.init_graphics()
|
||||
self.manual_board_setup()
|
||||
|
||||
def live_neighbours(self, i, j):
|
||||
""" Count the number of live neighbours around point (i, j). """
|
||||
s = 0 # The total number of live neighbours.
|
||||
# Loop over all the neighbours.
|
||||
for x in [i - 1, i, i + 1]:
|
||||
for y in [j - 1, j, j + 1]:
|
||||
if (x == i and y == j):
|
||||
continue # Skip the current point itself - we only want to count the neighbours!
|
||||
if (x != self.N and y != self.N):
|
||||
s += self.old_grid[x][y]
|
||||
# The remaining branches handle the case where the neighbour is off the end of the grid.
|
||||
# In this case, we loop back round such that the grid becomes a "toroidal array".
|
||||
elif (x == self.N and y != self.N):
|
||||
s += self.old_grid[0][y]
|
||||
elif (x != self.N and y == self.N):
|
||||
s += self.old_grid[x][0]
|
||||
else:
|
||||
s += self.old_grid[0][0]
|
||||
return s
|
||||
|
||||
def play(self):
|
||||
""" Play Conway's Game of Life. """
|
||||
|
||||
# Write the initial configuration to file.
|
||||
|
||||
self.t = 1 # Current time level
|
||||
while self.t <= self.T: # Evolve!
|
||||
# print( "At time level %d" % t)
|
||||
|
||||
# Loop over each cell of the grid and apply Conway's rules.
|
||||
for i in range(self.N):
|
||||
for j in range(self.N):
|
||||
live = self.live_neighbours(i, j)
|
||||
if (self.old_grid[i][j] == 1 and live < 2):
|
||||
self.new_grid[i][j] = 0 # Dead from starvation.
|
||||
elif (self.old_grid[i][j] == 1 and (live == 2 or live == 3)):
|
||||
self.new_grid[i][j] = 1 # Continue living.
|
||||
elif (self.old_grid[i][j] == 1 and live > 3):
|
||||
self.new_grid[i][j] = 0 # Dead from overcrowding.
|
||||
elif (self.old_grid[i][j] == 0 and live == 3):
|
||||
self.new_grid[i][j] = 1 # Alive from reproduction.
|
||||
|
||||
# Output the new configuration.
|
||||
|
||||
# The new configuration becomes the old configuration for the next generation.
|
||||
self.old_grid = self.new_grid.copy()
|
||||
self.draw_board()
|
||||
# Move on to the next time level
|
||||
self.t += 1
|
||||
|
||||
def init_graphics(self):
|
||||
self.graph = sg.Graph((600, 600), (0, 0), (450, 450), key='_GRAPH_', change_submits=True, drag_submits=False, background_color='lightblue')
|
||||
layout = [
|
||||
[sg.Text('Game of Life ', font='ANY 15'), sg.Text('', key='_OUTPUT_', size=(30,1), font='ANY 15')],
|
||||
[self.graph],
|
||||
[sg.Button('Go!', key='_DONE_'),
|
||||
sg.Text(' Delay (ms)') , sg.Slider([0,800], orientation='h', key='_SLIDER_', enable_events=True, size=(15,15)), sg.T('', size=(3,1), key='_S1_OUT_'),
|
||||
sg.Text(' Num Generations'), sg.Slider([0, 20000],default_value=4000, orientation='h',size=(15,15),enable_events=True, key='_SLIDER2_'), sg.T('', size=(3,1), key='_S2_OUT_')]
|
||||
]
|
||||
|
||||
self.window = sg.Window('Window Title', ).Layout(layout).Finalize()
|
||||
event, values = self.window.Read(timeout=0)
|
||||
self.delay = values['_SLIDER_']
|
||||
self.window.Element('_S1_OUT_').Update(values['_SLIDER_'])
|
||||
self.window.Element('_S2_OUT_').Update(values['_SLIDER2_'])
|
||||
|
||||
|
||||
def draw_board(self):
|
||||
BOX_SIZE = 15
|
||||
self.graph.Erase()
|
||||
for i in range(self.N):
|
||||
for j in range(self.N):
|
||||
if self.old_grid[i][j]:
|
||||
self.graph.DrawRectangle((i * BOX_SIZE, j * BOX_SIZE),
|
||||
(i * BOX_SIZE + BOX_SIZE, j * (BOX_SIZE) + BOX_SIZE),
|
||||
line_color='black', fill_color='yellow')
|
||||
event, values = self.window.Read(timeout=self.delay)
|
||||
if event in (None, '_DONE_'):
|
||||
exit()
|
||||
self.delay = values['_SLIDER_']
|
||||
self.T = int(values['_SLIDER2_'])
|
||||
self.window.Element('_S1_OUT_').Update(values['_SLIDER_'])
|
||||
self.window.Element('_S2_OUT_').Update(values['_SLIDER2_'])
|
||||
self.window.Element('_OUTPUT_').Update('Generation {}'.format(self.t))
|
||||
|
||||
|
||||
def manual_board_setup(self):
|
||||
ids = []
|
||||
for i in range(self.N):
|
||||
ids.append([])
|
||||
for j in range(self.N):
|
||||
ids[i].append(0)
|
||||
while True: # Event Loop
|
||||
event, values = self.window.Read()
|
||||
if event is None or event == '_DONE_':
|
||||
break
|
||||
self.window.Element('_S1_OUT_').Update(values['_SLIDER_'])
|
||||
self.window.Element('_S2_OUT_').Update(values['_SLIDER2_'])
|
||||
mouse = values['_GRAPH_']
|
||||
|
||||
if event == '_GRAPH_':
|
||||
if mouse == (None, None):
|
||||
continue
|
||||
box_x = mouse[0] // BOX_SIZE
|
||||
box_y = mouse[1] // BOX_SIZE
|
||||
if self.old_grid[box_x][box_y] == 1:
|
||||
id = ids[box_x][box_y]
|
||||
self.graph.DeleteFigure(id)
|
||||
self.old_grid[box_x][box_y] = 0
|
||||
else:
|
||||
id = self.graph.DrawRectangle((box_x * BOX_SIZE, box_y * BOX_SIZE),
|
||||
(box_x * BOX_SIZE + BOX_SIZE, box_y * (BOX_SIZE) + BOX_SIZE),
|
||||
line_color='black', fill_color='yellow')
|
||||
ids[box_x][box_y] = id
|
||||
self.old_grid[box_x][box_y] = 1
|
||||
self.window.Element('_DONE_').Update(text='Exit')
|
||||
|
||||
if (__name__ == "__main__"):
|
||||
game = GameOfLife(N=35, T=200)
|
||||
game.play()
|
||||
game.window.Close()
|
|
@ -1,54 +0,0 @@
|
|||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
# import PySimpleGUIWeb as sg # take your pick of ports. Runs on both
|
||||
else:
|
||||
import PySimpleGUI27 as sg
|
||||
import random
|
||||
import string
|
||||
|
||||
"""
|
||||
Demo application to show how to draw rectangles and letters on a Graph Element
|
||||
This demo mocks up a crossword puzzle board
|
||||
It will place a letter where you click on the puzzle
|
||||
"""
|
||||
|
||||
|
||||
BOX_SIZE = 25
|
||||
|
||||
layout = [
|
||||
[sg.Text('Crossword Puzzle Using PySimpleGUI'), sg.Text('', key='_OUTPUT_')],
|
||||
[sg.Graph((800,800), (0,450), (450,0), key='_GRAPH_', change_submits=True, drag_submits=False)],
|
||||
[sg.Button('Show'), sg.Button('Exit')]
|
||||
]
|
||||
|
||||
window = sg.Window('Window Title', ).Layout(layout).Finalize()
|
||||
|
||||
g = window.FindElement('_GRAPH_')
|
||||
|
||||
for row in range(16):
|
||||
for col in range(16):
|
||||
if random.randint(0,100) > 10:
|
||||
g.DrawRectangle((col * BOX_SIZE + 5, row * BOX_SIZE + 3), (col * BOX_SIZE + BOX_SIZE + 5, row * BOX_SIZE + BOX_SIZE + 3), line_color='black')
|
||||
else:
|
||||
g.DrawRectangle((col * BOX_SIZE + 5, row * BOX_SIZE + 3), (col * BOX_SIZE + BOX_SIZE + 5, row * BOX_SIZE + BOX_SIZE + 3), line_color='black', fill_color='black')
|
||||
|
||||
g.DrawText('{}'.format(row * 6 + col + 1), (col * BOX_SIZE + 10, row * BOX_SIZE + 8))
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
print(event, values)
|
||||
if event is None or event == 'Exit':
|
||||
break
|
||||
mouse = values['_GRAPH_']
|
||||
|
||||
if event == '_GRAPH_':
|
||||
if mouse == (None, None):
|
||||
continue
|
||||
box_x = mouse[0]//BOX_SIZE
|
||||
box_y = mouse[1]//BOX_SIZE
|
||||
letter_location = (box_x * BOX_SIZE + 18, box_y * BOX_SIZE + 17)
|
||||
print(box_x, box_y)
|
||||
g.DrawText('{}'.format(random.choice(string.ascii_uppercase)), letter_location, font='Courier 25')
|
||||
|
||||
window.Close()
|
|
@ -1,242 +0,0 @@
|
|||
"""
|
||||
@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")
|
|
@ -1,55 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
# import imwatchingyou # Not needed because using the one inside PySimpleGUI.py code itself
|
||||
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
This particular program is a GUI based program simply to make it easier for you to interact and change
|
||||
things.
|
||||
|
||||
In this example, the debugger is not started initiallly. You click the "Debug" button to launch it
|
||||
There are THREE steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import imwatchingyou
|
||||
2. When you want to show a debug window, call one of two functions:
|
||||
imwatchingyou.show_debug_window()
|
||||
imwatchingyou.show_popout_window()
|
||||
3. You must find a location in your code to "refresh" the debugger. Some loop that's executed often.
|
||||
In this loop add this call:
|
||||
imwatchingyou.refresh()
|
||||
"""
|
||||
|
||||
layout = [
|
||||
[sg.T('A typical PSG application')],
|
||||
[sg.In(key='_IN_')],
|
||||
[sg.T(' ', key='_OUT_', size=(45,1))],
|
||||
[sg.CBox('Checkbox 1'), sg.CBox('Checkbox 2')],
|
||||
[sg.Radio('a',1, key='_R1_'), sg.Radio('b',1, key='_R2_'), sg.Radio('c',1, key='_R3_')],
|
||||
[sg.Combo(['c1', 'c2', 'c3'], size=(6,3), key='_COMBO_')],
|
||||
[sg.Output(size=(50,6))],
|
||||
[sg.Ok(), sg.Exit(), sg.Button('Enable'), sg.Button('Popout'), sg.Button('Debugger'), sg.Debug(key='Debug')],
|
||||
]
|
||||
|
||||
window = sg.Window('This is your Application Window', layout, debugger_enabled=False)
|
||||
|
||||
counter = 0
|
||||
timeout = 100
|
||||
|
||||
# Note that you can launch the debugger windows right away, without waiting for user input
|
||||
sg.show_debugger_popout_window()
|
||||
|
||||
while True: # Your Event Loop
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
elif event == 'Enable':
|
||||
window.EnableDebugger()
|
||||
elif event == 'Popout':
|
||||
sg.show_debugger_popout_window() # replaces old popout with a new one, possibly with new variables`
|
||||
elif event == 'Debugger':
|
||||
sg.show_debugger_window()
|
||||
counter += 1
|
||||
# to prove window is operating, show the input in another area in the window.
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
||||
|
||||
window.Close()
|
|
@ -1,48 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
# import imwatchingyou # STEP 1
|
||||
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
This particular program is a GUI based program simply to make it easier for you to interact and change
|
||||
things.
|
||||
|
||||
In this example, the debugger is not started initiallly. You click the "Debug" button to launch it
|
||||
There are THREE steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import imwatchingyou
|
||||
2. When you want to show a debug window, call one of two functions:
|
||||
imwatchingyou.show_debug_window()
|
||||
imwatchingyou.show_popout_window()
|
||||
3. You must find a location in your code to "refresh" the debugger. Some loop that's executed often.
|
||||
In this loop add this call:
|
||||
imwatchingyou.refresh()
|
||||
"""
|
||||
|
||||
layout = [
|
||||
[sg.T('A typical PSG application')],
|
||||
[sg.In(key='_IN_')],
|
||||
[sg.T(' ', key='_OUT_', size=(45,1))],
|
||||
[sg.CBox('Checkbox 1'), sg.CBox('Checkbox 2')],
|
||||
[sg.Radio('a',1, key='_R1_'), sg.Radio('b',1, key='_R2_'), sg.Radio('c',1, key='_R3_')],
|
||||
[sg.Combo(['c1', 'c2', 'c3'], size=(6,3), key='_COMBO_')],
|
||||
[sg.Output(size=(50,6))],
|
||||
[sg.Ok(), sg.Exit(), sg.Button('Enable'), sg.Debug(key='Debug')],
|
||||
]
|
||||
|
||||
window = sg.Window('This is your Application Window', layout, debugger_enabled=False)
|
||||
|
||||
counter = 0
|
||||
timeout = 100
|
||||
|
||||
while True: # Your Event Loop
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
elif event == 'Enable':
|
||||
window.EnableDebugger()
|
||||
counter += 1
|
||||
# to prove window is operating, show the input in another area in the window.
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
||||
|
||||
window.Close()
|
|
@ -1,54 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
import imwatchingyou # STEP 1
|
||||
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
This particular program is a GUI based program simply to make it easier for you to interact and change
|
||||
things.
|
||||
|
||||
In this example, the debugger is not started initiallly. You click the "Debug" button to launch it
|
||||
There are THREE steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import imwatchingyou
|
||||
2. When you want to show a debug window, call one of two functions:
|
||||
imwatchingyou.show_debug_window()
|
||||
imwatchingyou.show_popout_window()
|
||||
3. You must find a location in your code to "refresh" the debugger. Some loop that's executed often.
|
||||
In this loop add this call:
|
||||
imwatchingyou.refresh()
|
||||
"""
|
||||
|
||||
layout = [
|
||||
[sg.T('A typical PSG application')],
|
||||
[sg.In(key='_IN_')],
|
||||
[sg.T(' ', key='_OUT_', size=(30, 1))],
|
||||
[sg.Radio('a', 1, key='_R1_'), sg.Radio('b', 1, key='_R2_'), sg.Radio('c', 1, key='_R3_')],
|
||||
[sg.Combo(['c1', 'c2', 'c3'], size=(6, 3), key='_COMBO_')],
|
||||
[sg.Output(size=(50, 6))],
|
||||
[sg.Ok(), sg.Exit(), sg.Button('Debug'), sg.Button('Popout')],
|
||||
]
|
||||
|
||||
window = sg.Window('This is your Application Window', layout)
|
||||
|
||||
counter = 0
|
||||
timeout = 100
|
||||
|
||||
while True: # Your Event Loop
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
elif event == 'Ok':
|
||||
print('You clicked Ok.... this is where print output goes')
|
||||
elif event == 'Debug':
|
||||
imwatchingyou.show_debugger_window() # STEP 2
|
||||
elif event == 'Popout':
|
||||
imwatchingyou.show_debugger_popout_window() # STEP 2
|
||||
counter += 1
|
||||
# to prove window is operating, show the input in another area in the window.
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
||||
|
||||
# don't worry about the "state" of things, just call this function "frequently"
|
||||
imwatchingyou.refresh_debugger() # STEP 3 - refresh debugger
|
||||
|
||||
window.Close()
|
|
@ -1,59 +0,0 @@
|
|||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
else:
|
||||
import PySimpleGUI27 as sg
|
||||
|
||||
"""
|
||||
Demo - Running 2 windows with both being active at the same time
|
||||
Three important things to note about this design patter:
|
||||
1. The layout for window 2 is inside of the while loop, just before the call to window2=sg.Window
|
||||
2. The read calls have timeout values of 100 and 0. You can change the 100 to whatever interval you wish
|
||||
but must keep the second window's timeout at 0
|
||||
3. There is a safeguard to stop from launching multiple copies of window2. Only 1 window2 is visible at a time
|
||||
"""
|
||||
|
||||
# Window 1 layout
|
||||
layout = [
|
||||
[sg.Text('This is the FIRST WINDOW'), sg.Text(' ', key='_OUTPUT_')],
|
||||
[sg.Text('')],
|
||||
[sg.Button('Launch 2nd Window'),sg.Button('Popup'), sg.Button('Exit')]
|
||||
]
|
||||
|
||||
window = sg.Window('Window Title', location=(800,600)).Layout(layout)
|
||||
win2_active = False
|
||||
i=0
|
||||
while True: # Event Loop
|
||||
event, values = window.Read(timeout=100)
|
||||
if event != sg.TIMEOUT_KEY:
|
||||
print(i, event, values)
|
||||
|
||||
if event is None or event == 'Exit':
|
||||
break
|
||||
elif event == 'Popup':
|
||||
sg.Popup('This is a BLOCKING popup','all windows remain inactive while popup active')
|
||||
i+=1
|
||||
if event == 'Launch 2nd Window' and not win2_active: # only run if not already showing a window2
|
||||
win2_active = True
|
||||
# window 2 layout - note - must be "new" every time a window is created
|
||||
layout2 = [
|
||||
[sg.Text('The second window'), sg.Text('', key='_OUTPUT_')],
|
||||
[sg.Input(do_not_clear=True, key='_IN_')],
|
||||
[sg.Button('Show'), sg.Button('Exit')]
|
||||
]
|
||||
window2 = sg.Window('Second Window').Layout(layout2)
|
||||
# Read window 2's events. Must use timeout of 0
|
||||
if win2_active:
|
||||
# print("reading 2")
|
||||
event, values = window2.Read(timeout=100)
|
||||
# print("win2 ", event)
|
||||
if event != sg.TIMEOUT_KEY:
|
||||
print("win2 ", event)
|
||||
if event == 'Exit' or event is None:
|
||||
# print("Closing window 2", event)
|
||||
win2_active = False
|
||||
window2.Close()
|
||||
if event == 'Show':
|
||||
sg.Popup('You entered ', values['_IN_'])
|
||||
|
||||
window.Close()
|
|
@ -1,35 +0,0 @@
|
|||
"""
|
||||
PySimpleGUI The Complete Course
|
||||
Lesson 7 - Multiple Windows
|
||||
"""
|
||||
import PySimpleGUI as sg
|
||||
|
||||
# Design pattern 1 - First window does not remain active
|
||||
|
||||
layout = [[ sg.Text('Window 1'),],
|
||||
[sg.Input(do_not_clear=True)],
|
||||
[sg.Text('', size=(20,1), key='_OUTPUT_')],
|
||||
[sg.Button('Launch 2')]]
|
||||
|
||||
win1 = sg.Window('Window 1').Layout(layout)
|
||||
win2_active=False
|
||||
while True:
|
||||
ev1, vals1 = win1.Read(timeout=100)
|
||||
if ev1 is None:
|
||||
break
|
||||
win1.FindElement('_OUTPUT_').Update(vals1[0])
|
||||
|
||||
if ev1 == 'Launch 2' and not win2_active:
|
||||
win2_active = True
|
||||
win1.Hide()
|
||||
layout2 = [[sg.Text('Window 2')],
|
||||
[sg.Button('Exit')]]
|
||||
|
||||
win2 = sg.Window('Window 2').Layout(layout2)
|
||||
while True:
|
||||
ev2, vals2 = win2.Read()
|
||||
if ev2 is None or ev2 == 'Exit':
|
||||
win2.Close()
|
||||
win2_active = False
|
||||
win1.UnHide()
|
||||
break
|
|
@ -1,34 +0,0 @@
|
|||
"""
|
||||
PySimpleGUI The Complete Course
|
||||
Lesson 7 - Multiple Windows
|
||||
"""
|
||||
import PySimpleGUI as sg
|
||||
|
||||
# Design pattern 2 - First window remains active
|
||||
|
||||
layout = [[ sg.Text('Window 1'),],
|
||||
[sg.Input(do_not_clear=True)],
|
||||
[sg.Text('', size=(20,1), key='_OUTPUT_')],
|
||||
[sg.Button('Launch 2'), sg.Button('Exit')]]
|
||||
|
||||
win1 = sg.Window('Window 1').Layout(layout)
|
||||
|
||||
win2_active = False
|
||||
while True:
|
||||
ev1, vals1 = win1.Read(timeout=100)
|
||||
win1.FindElement('_OUTPUT_').Update(vals1[0])
|
||||
if ev1 is None or ev1 == 'Exit':
|
||||
break
|
||||
|
||||
if not win2_active and ev1 == 'Launch 2':
|
||||
win2_active = True
|
||||
layout2 = [[sg.Text('Window 2')],
|
||||
[sg.Button('Exit')]]
|
||||
|
||||
win2 = sg.Window('Window 2').Layout(layout2)
|
||||
|
||||
if win2_active:
|
||||
ev2, vals2 = win2.Read(timeout=100)
|
||||
if ev2 is None or ev2 == 'Exit':
|
||||
win2_active = False
|
||||
win2.Close()
|
|
@ -1,48 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
layout = [[sg.Text('Window 1'), ],
|
||||
[sg.Input(do_not_clear=True)],
|
||||
[sg.Text('',size=(20,1), key='_OUTPUT_')],
|
||||
[sg.Button('Next >'), sg.Button('Exit')]]
|
||||
|
||||
win1 = sg.Window('Window 1').Layout(layout)
|
||||
|
||||
win3_active = win2_active = False
|
||||
while True:
|
||||
if not win2_active:
|
||||
ev1, vals1 = win1.Read()
|
||||
if ev1 is None or ev1 == 'Exit':
|
||||
break
|
||||
win1.FindElement('_OUTPUT_').Update(vals1[0])
|
||||
|
||||
if not win2_active and ev1 == 'Next >':
|
||||
win2_active = True
|
||||
win1.Hide()
|
||||
layout2 = [[sg.Text('Window 2')],
|
||||
[sg.Button('< Prev'), sg.Button('Next >')]]
|
||||
|
||||
win2 = sg.Window('Window 2').Layout(layout2)
|
||||
|
||||
if win2_active:
|
||||
ev2, vals2 = win2.Read()
|
||||
if ev2 in (None, 'Exit', '< Prev'):
|
||||
win2_active = False
|
||||
win2.Close()
|
||||
win1.UnHide()
|
||||
elif ev2 == 'Next >':
|
||||
win3_active = True
|
||||
win2_active = False
|
||||
win2.Hide()
|
||||
layout3 = [[sg.Text('Window 3')],
|
||||
[sg.Button('< Prev'), sg.Button('Exit')]]
|
||||
win3 = sg.Window('Window 3').Layout(layout3)
|
||||
|
||||
if win3_active:
|
||||
ev3, vals3 = win3.Read()
|
||||
if ev3 == '< Prev':
|
||||
win3.Close()
|
||||
win3_active = False
|
||||
win2_active = True
|
||||
win2.UnHide()
|
||||
elif ev3 in (None, 'Exit'):
|
||||
break
|
|
@ -1,24 +0,0 @@
|
|||
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('', size=(20,1), 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()
|
|
@ -1,54 +0,0 @@
|
|||
"""
|
||||
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 "One Shot" window
|
||||
2. A persistent window that stays open after button clicks (uses an event loop)
|
||||
3. A persistent window that need to perform Update of an element before the window.read
|
||||
"""
|
||||
# ---------------------------------#
|
||||
# DESIGN PATTERN 1 - Simple Window #
|
||||
# ---------------------------------#
|
||||
import PySimpleGUI as sg
|
||||
|
||||
layout = [[ sg.Text('My Oneshot') ],
|
||||
[ sg.Button('OK') ]]
|
||||
|
||||
window = sg.Window('My Oneshot', layout)
|
||||
event, values = window.read()
|
||||
window.close()
|
||||
|
||||
|
||||
# -------------------------------------#
|
||||
# DESIGN PATTERN 2 - Persistent Window #
|
||||
# -------------------------------------#
|
||||
import PySimpleGUI as sg
|
||||
|
||||
layout = [[ sg.Text('My layout') ],
|
||||
[ sg.Button('OK'), sg.Button('Cancel') ]]
|
||||
|
||||
window = sg.Window('Design Pattern 2', layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.read()
|
||||
if event in (None, 'Cancel'):
|
||||
break
|
||||
window.close()
|
||||
|
||||
# ------------------------------------------------------------------#
|
||||
# DESIGN PATTERN 3 - Persistent Window with "early update" required #
|
||||
# ------------------------------------------------------------------#
|
||||
import PySimpleGUI as sg
|
||||
|
||||
layout = [[ sg.Text('My layout', key='-TEXT-KEY-') ],
|
||||
[ sg.Button('OK'), sg.Button('Cancel') ]]
|
||||
|
||||
window = sg.Window('Design Pattern 3', layout, finalize=True)
|
||||
|
||||
window['-TEXT-KEY-'].Update('NEW Text') # Change the text field. Finalize allows us to do this
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.read()
|
||||
if event in (None, 'Cancel'):
|
||||
break
|
||||
window.close()
|
|
@ -1,90 +0,0 @@
|
|||
#!/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.Button('Run', button_color=('white', '#00168B')),
|
||||
sg.Button('Program 1'),
|
||||
sg.Button('Program 2'),
|
||||
sg.Button('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()
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
#!/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)
|
|
@ -1,79 +0,0 @@
|
|||
#!/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()
|
|
@ -1,99 +0,0 @@
|
|||
#!/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()
|
|
@ -1,45 +0,0 @@
|
|||
#!/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()
|
|
@ -1,108 +0,0 @@
|
|||
#!/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()
|
|
@ -1,59 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import PySimpleGUI 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 PySimpleGUI
|
||||
Something like this 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!)
|
||||
|
||||
"""
|
||||
|
||||
def time_as_int():
|
||||
return int(round(time.time() * 100))
|
||||
|
||||
# ---------------- Create Form ----------------
|
||||
sg.ChangeLookAndFeel('Black')
|
||||
|
||||
layout = [[sg.Text('')],
|
||||
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
|
||||
[sg.Button('Pause', key='-RUN-PAUSE-', 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', layout, no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True, element_padding=(0,0))
|
||||
|
||||
# ---------------- main loop ----------------
|
||||
current_time, paused_time, paused = 0, 0, False
|
||||
start_time = time_as_int()
|
||||
while (True):
|
||||
# --------- Read and update window --------
|
||||
if not paused:
|
||||
event, values = window.Read(timeout=10)
|
||||
current_time = time_as_int() - start_time
|
||||
else:
|
||||
event, values = window.Read()
|
||||
# --------- Do Button Operations --------
|
||||
if event in (None, 'Exit'): # ALWAYS give a way out of program
|
||||
break
|
||||
if event == '-RESET-':
|
||||
paused_time = start_time = time_as_int()
|
||||
current_time = 0
|
||||
elif event == '-RUN-PAUSE-':
|
||||
paused = not paused
|
||||
if paused:
|
||||
paused_time = time_as_int()
|
||||
else:
|
||||
start_time = start_time + time_as_int() - paused_time
|
||||
window['-RUN-PAUSE-'].Update('Run' if paused else 'Pause') # Change button's text
|
||||
|
||||
# --------- Display timer in window --------
|
||||
window['text'].Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
|
||||
(current_time // 100) % 60,
|
||||
current_time % 100))
|
||||
window.close()
|
|
@ -1,138 +0,0 @@
|
|||
#!/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)
|
|
@ -1,45 +0,0 @@
|
|||
#!/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.Button('Enable', button_color=('white', 'black')),
|
||||
sg.Button('Disable', button_color=('white', 'black')),
|
||||
sg.Button('Reset', button_color=('white', '#9B0023'), key='reset'),
|
||||
sg.Button('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)
|
|
@ -1,62 +0,0 @@
|
|||
#!/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)
|
|
@ -1,75 +0,0 @@
|
|||
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 in ('Quit', 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 == '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()
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
# import PySimpleGUIQt as sg
|
||||
# import PySimpleGUIWx as sg
|
||||
|
||||
'''
|
||||
Event Callback Simulation
|
||||
|
||||
This design pattern simulates callbacks for events.
|
||||
This is NOT the "normal" way things work in PySimpleGUI and is an architecture that is actively discouraged
|
||||
Unlike tkinter, Qt, etc, PySimpleGUI does not utilize callback
|
||||
functions as a mechanism for communicating when button presses or other events happen.
|
||||
BUT, should you want to quickly convert some existing code that does use callback functions, then this
|
||||
is one way to do a "quick and dirty" port to PySimpleGUI.
|
||||
'''
|
||||
|
||||
# The callback functions
|
||||
# These callbacks all display a message in a non-blocking way and immediately return
|
||||
def button1(event, values):
|
||||
sg.popup_quick_message('Button 1 callback', background_color='red', text_color='white')
|
||||
|
||||
def button2(event, values):
|
||||
sg.popup_quick_message('Button 2 callback', background_color='green', text_color='white')
|
||||
|
||||
def catch_all(event, values):
|
||||
sg.popup_quick_message(f'An unplanned event = "{event}" happend', background_color='blue', text_color='white', auto_close_duration=6)
|
||||
|
||||
# Lookup dictionary that maps event to function to call. In this case, only 2 event have defined callbacks
|
||||
func_dict = {'1':button1, '2':button2}
|
||||
|
||||
# Layout the design of the GUI
|
||||
layout = [[sg.Text('Please click a button')],
|
||||
[sg.Button('1'), sg.Button('2'), sg.Button('Not defined', key='-MY-KEY-'), sg.Quit()]]
|
||||
|
||||
# Show the Window to the user
|
||||
window = sg.Window('Button callback example', layout)
|
||||
|
||||
# Event loop. Read buttons, make callbacks
|
||||
while True:
|
||||
# Read the Window
|
||||
event, values = window.read()
|
||||
# Lookup event in function dictionary and call the function, passing in the event and values variables
|
||||
try:
|
||||
func_dict[event](event, values) # Call function with event and values
|
||||
except:
|
||||
catch_all(event, values)
|
||||
# See if should close the window
|
||||
if event in ('Quit', None): # normally this is done IMMEDIATELY after the read
|
||||
break
|
||||
|
||||
window.close()
|
||||
|
||||
# All done!
|
||||
sg.popup_auto_close('Done... this window auto closes')
|
|
@ -1,66 +0,0 @@
|
|||
#!/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.Button('Exit'),
|
||||
sg.Text(' ' * 40), sg.Button('SaveSettings'), sg.Button('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 == 'SaveSettings':
|
||||
filename = sg.PopupGetFile('Save Settings', save_as=True, no_window=True)
|
||||
window.SaveToDisk(filename)
|
||||
# save(values)
|
||||
elif event == '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()
|
|
@ -1,44 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
import sys
|
||||
|
||||
button_names = ('close', 'cookbook', 'cpu', 'github', 'pysimplegui', 'run', 'storage', 'timer')
|
||||
|
||||
house64='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'
|
||||
|
||||
cpu64='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'
|
||||
|
||||
timer64='iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsSAAALEgHS3X78AAAJDUlEQVRYhbWWe2xT1x3Hv/fht6+feTiJm6TYCUnaLYUmJFmb0pWu0NKmYhtQxoaKCmKjRe1aVRVV/xh/dFPfj0mZNFUr3TSKIHQCOtYVSkehzCEkJORpJ8GJY8eO7Xvt2L7O9bV97/5Iy3iEdK3YT7rS0e/e8/t+zvmee84hcJOj/nu31zQ23LkxFAxaWC5WYC8rHQDgPXnq9Mcsx6Wu/Z66meLVTkfxbbU1O/oHBo8Mjbg/8IyNd9TW1g46nc5ilYJew3Kx/rm5OfFmal6OhoY7y3bt/OWftvx8s2qh9y++8PyD69c9+ti1+Zs2AzRFN1lMRu7SpK+nra3NVFuztH3z5s3y8RMn3ABQbLNFCFl+YGjEfeb/AsAw+mVT/oDIxWLee1pbf1dZWbHDarVuanv44erKysqp9/d+cMloND7lDwQ6ruxH3iwAAKlqp0N8+623msxm049NJhOCwWmc/OzEYw+uWf2Q1WKhrGbTzLWd6O+i1NzcTNlsNoYgCCkYDKZcLpfEMMxgZUXF1nSaf5Cm6dJ0mod7eBjfr7+j57U33txnLytd5qyqGsAnn343gBUrVuieeOKJlqmpqXV1dXXFhYWFhlwuJwUCgdnm5uaJlpbmI2Nu96X+vr4VdbffjlGPG/lcDhqt7o9yPjdV7XRs9YyNH7q2LvFNwi+//HLNpk2bfuL1el/geZ6RJAn5fB6iKCKTySCfz0MQBPA8D5VKFRi42FeaSiaIrCiivKIiqNNq3xgZGSnr6x94xTM2fp0FNwRoaWnB9u3b766pqWkXRbEmGo0q3G43RkaGQRIkjEYTQADpdBoAUFRUBJqmkckIYKNRtN5996sfffTRxe6enlEAg/7ANL+QzoIWNDc3EwcPHnxubGzsRY7jzF1dXfB4faioq8cjv9oNvbUIFEWDJAiQkJDmIvBccCE8OY5cLg/GYMSw27NBq2f+7Q9Mn1u+fLnh6NGPt3V1nXs2Fo+fevvtd54LBoPpG87Ae++9d7/D4TgkCIKho6MDKosNP3j0ZygvL4dBo4KSIiCkEpBlQM0wkGUgm81hOhDASOfn8I8OQxRF0DQ9abPZNhRYrVtEUdyq1Wi06TQf1OmZzY9v3fo5sMA+sGfPnhWNjY3vx+Pxko6DHVh61wO4b8PjsJs0QCaNnEKDQIRDmBeRysmIxpOQaQ1CAR90ahWqljWBYYwI+cbBp1KmSCT8kEatrpFlyTo40I+xMc9cU3OLd9++D88uCNDe3v5SIpH40cmTJwmF2YYf/nQLbEYtYpEIhse9CLGzyGQEMAYjFAoFkpEQ2JkAaJpGYVk5aJqCucgGiHOIBAPguJjB4x5h0nwqYbFYhpY3rHjqr/s+/JvH4xGvWwN79+6tmZiY2MGyLBHkEnhk+zYUqglEQ0F4QiwonRmEnEdBsQ0EAFKSYLulHEkuClKWQJEEKGLe2DJnLYRUEix7ApRCGdux86mWJ5/c6X/l9TfTV2petROGw+GHs9kscb6rC433rUFJUQF4ngcrypgYugiapmAtsgGShBQbQZINg5Ak6HU6lFXcCgoySFlCMsZBp2dQU78Mer0ekiRZ9u/fX9LTc+Eq8asA1q1bZ2hsbLw/l8shFo/DcUczrCYDxi55MdR9DnZHNb449Gec/fgg2MAkKBJgjAbMRkNQ0BQUJOBzD6LPdRpZgUdJaSnKKp24dckSGI1GHDt2bP1CC/6yBaIoWjKZjGVmZgaWIhsMJhNIALqSSlSZi8AYzSi7pQJ/efUluLvPYsuzL0GjVkNJkTCZzaBJAuVLHMhmSqHVaEAC0GjUsBYUQqVSIZFIFC0EQF4BYBRF0Tg7OwtjoQ1UXsR0cBoCn4Reb4BOq4W1sAjbdv8WZmshXvv1Npz/16cosFqh+Mp7vU4LlUKBcGAKQiqBdCIOlVoDmqahUCgW0v8vgCRJVDabpURRBK1UIptOYWygDzMTYxD5JCgCIAnAUlCAXzy9GzZ7Ob74+6HLeZokQBEEhHQKQZ8XoalJcJGZRcWvsoCiqKQkSUmappFJ82AshVh272qks/I1IvMQu1//w3yOIi/nSQKw2+2ovMUOigAokkBg3INMJgNBEBYHUCgUCVEUE2q1GlwwBDGbg0pBgyLkq8RJAlAQgNpguCr/9UNfAUsSgIKmkc/nIctyZlELWJYNC4LQTRAEUskEOL8XBGSwQR/YaR+EVAIUCShJYv5/J3HZ+/k2EGcjCAV8SHBRQMqDT8QxOuoBy7JobW39x6IALpdLDofDnyQSCej1elwavIBIYBKTwwOYGO5HPBKEgpgf1fxIv2qT821IEob6ejA+PIQ4x2JksB9cNAKWZeHz+fKrVq36bFELACAcDh93Op1fplKpuyaHL8K+pAqtq9eCJIAUF8WEZwhLnFVQKJUgya+mHTK4cAhSTkTrPfdCp9OAIoBYNILj//wEvb290tq1a9t37dp13V0AuOYscLlcMJlMPMMwD/B8SpWeZVFRVQutRouJ0WGEAz5YrQXQ63WQ81nQBAE5n0N351nkxQwMBgaMXoesIKD3Qg/OdXbC6/V68/n8bwYGBgLfCAAAarV6dOXKlfLk5OR9qUSCmOPCMJpMkHI53OpwoLi0FHPJWZw8dhjh6QBq6upQXV0NnVaLqYlL0Gk1GOzvx9GjR3D69Om59evX7zxz5sxxv9+/kP71ANPT0/lgMHhh5cqVt/n9/qUcGyWSbBgOhxOFJaXQqFRQ0hQyc2kweh3sdjtIAlAraOg0Gnx5+gucPfslTp06Ja5atar98OHDv+/s7JQXVMciV7L6+npm48aNT3d3d78gy7LeaDSiqqoKlY4qFJeUwlpgBUWSSM7OIjOXBhuNYGhoCL29vQiFQqG2trbnOzo69p8/fz53I41FAQCgoaFBuWfPng0HDhx4OhgMNuh0OhQXF8NgMMBisUCtVoPneYTDYfj9fvh8PixduvQIy7LtsVjsU5fLdcOR/08AX8czzzxDxmKxtmw2uyaXy92RyWQMgiAwkiTJSqVyVqVSxfR6vctkMh159913z3xzxW8J8HU0NTWRAOyJRMKQTCYZgiBko9E4azabY9lsNuRyub5NOQDAfwBU9w9d4+VBlQAAAABJRU5ErkJggg=='
|
||||
|
||||
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 main():
|
||||
|
||||
toolbar_buttons = [[sg.Button('', image_data=close64,button_color=('white', 'black'), pad=(0,0), key='-CLOSE-'),
|
||||
sg.Button('', image_data=timer64, button_color=('white', 'black'), pad=(0, 0), key='-TIMER-'),
|
||||
sg.Button('', image_data=house64, button_color=('white', 'black'), pad=(0, 0), key='-HOUSE-'),
|
||||
sg.Button('', image_data=cpu64, button_color=('white', 'black'), pad=(0,0), key='-CPU-'),]]
|
||||
|
||||
# layout = toolbar_buttons
|
||||
layout = [[sg.Column( toolbar_buttons, background_color='black')]]
|
||||
|
||||
window = sg.Window('Toolbar', layout, no_titlebar=True, grab_anywhere=True, background_color='black', margins=(0,0))
|
||||
|
||||
# ---===--- 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-':
|
||||
print('Timer Button') # add your call to launch a timer program
|
||||
elif button == '-CPU-':
|
||||
print('CPU Button') # add your call to launch a CPU measuring utility
|
||||
elif button == '-HOUSE-':
|
||||
print('Home Button')
|
||||
|
||||
window.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#!/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))
|
|
@ -1,30 +0,0 @@
|
|||
|
||||
# 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.")
|
|
@ -1,34 +0,0 @@
|
|||
#!/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)
|
|
@ -1,54 +0,0 @@
|
|||
#!/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()
|
|
@ -1,53 +0,0 @@
|
|||
#!/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
|
|
@ -1,97 +0,0 @@
|
|||
# import PySimpleGUIWeb as sg
|
||||
import PySimpleGUI as sg
|
||||
import pymunk
|
||||
import random
|
||||
import socket
|
||||
|
||||
"""
|
||||
Demo that shows integrating PySimpleGUI with the pymunk library. This combination
|
||||
of PySimpleGUI and pymunk could be used to build games.
|
||||
Note this exact same demo runs with PySimpleGUIWeb by changing the import statement
|
||||
"""
|
||||
|
||||
class Ball():
|
||||
def __init__(self, x, y, r, graph_elem, *args, **kwargs):
|
||||
mass = 10
|
||||
self.body = pymunk.Body(mass,
|
||||
pymunk.moment_for_circle(mass, 0, r, (0, 0))) # Create a Body with mass and moment
|
||||
self.body.position = x, y
|
||||
self.shape = pymunk.Circle(self.body, r, offset=(0, 0)) # Create a box shape and attach to body
|
||||
self.shape.elasticity = 0.99999
|
||||
self.shape.friction = 0.8
|
||||
self.gui_circle_figure = None
|
||||
self.graph_elem = graph_elem
|
||||
|
||||
def move(self):
|
||||
self.graph_elem.RelocateFigure(self.gui_circle_figure, self.body.position[0], ball.body.position[1])
|
||||
|
||||
|
||||
|
||||
class Playfield():
|
||||
def __init__(self, graph_elem):
|
||||
self.space = pymunk.Space()
|
||||
self.space.gravity = 0, 200
|
||||
self.add_wall((0, 400), (600, 400)) # ground
|
||||
self.add_wall((0, 0), (0, 600)) # Left side
|
||||
self.add_wall((600, 0), (600, 400)) # right side
|
||||
self.arena_balls = [] # type: [] Ball
|
||||
self.graph_elem = graph_elem # type: sg.Graph
|
||||
|
||||
|
||||
def add_wall(self, pt_from, pt_to):
|
||||
body = pymunk.Body(body_type=pymunk.Body.STATIC)
|
||||
ground_shape = pymunk.Segment(body, pt_from, pt_to, 0.0)
|
||||
ground_shape.friction = 0.8
|
||||
ground_shape.elasticity = .99
|
||||
self.space.add(ground_shape)
|
||||
|
||||
def add_random_balls(self):
|
||||
for i in range(1, 200):
|
||||
x = random.randint(0, 600)
|
||||
y = random.randint(0, 400)
|
||||
r = random.randint(1, 10)
|
||||
self.add_ball(x,y,r)
|
||||
|
||||
def add_ball(self, x, y, r, fill_color='black', line_color='red'):
|
||||
ball = Ball(x, y, r, self.graph_elem)
|
||||
self.arena_balls.append(ball)
|
||||
area.space.add(ball.body, ball.shape)
|
||||
ball.gui_circle_figure = self.graph_elem.DrawCircle((x, y), r, fill_color=fill_color, line_color=line_color)
|
||||
return ball
|
||||
|
||||
def shoot_a_ball(self, x, y, r, vector=(-10, 0), fill_color='black', line_color='red'):
|
||||
ball = self.add_ball(x,y,r, fill_color=fill_color, line_color=line_color )
|
||||
# ball.shape.surface_velocity=10
|
||||
ball.body.apply_impulse_at_local_point(100*pymunk.Vec2d(vector))
|
||||
|
||||
# ------------------- Build and show the GUI Window -------------------
|
||||
graph_elem = sg.Graph((600, 400), (0, 400), (600, 0), enable_events=True, key='_GRAPH_', background_color='lightblue')
|
||||
|
||||
layout = [[sg.Text('Ball Test'), sg.T('My IP {}'.format(socket.gethostbyname(socket.gethostname())))],
|
||||
[graph_elem],
|
||||
[sg.B('Kick'), sg.B('Player 1 Shoot', size=(15,2)),sg.B('Player 2 Shoot', size=(15,2)), sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Window Title', layout, disable_close=True)
|
||||
|
||||
area = Playfield(graph_elem)
|
||||
# area.add_random_balls()
|
||||
|
||||
# ------------------- GUI Event Loop -------------------
|
||||
while True: # Event Loop
|
||||
event, values = window.Read(timeout=10)
|
||||
# print(event, values)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
area.space.step(0.01)
|
||||
|
||||
if event == 'Player 2 Shoot':
|
||||
area.shoot_a_ball(555, 200, 5, (-10,0), fill_color='green', line_color='green')
|
||||
elif event == 'Player 1 Shoot':
|
||||
area.shoot_a_ball(10, 200, 5, (10,0))
|
||||
|
||||
for ball in area.arena_balls:
|
||||
if event == 'Kick':
|
||||
ball.body.position = ball.body.position[0], ball.body.position[1]-random.randint(1,200)
|
||||
ball.move()
|
||||
|
||||
window.Close()
|
|
@ -1,53 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
"""
|
||||
Demo - Drag a rectangle to draw it
|
||||
|
||||
This demo shows how to use a Graph Element to (optionally) display an image and then use the
|
||||
mouse to "drag a rectangle". This is sometimes called a rubber band and is an operation you
|
||||
see in things like editors
|
||||
"""
|
||||
|
||||
|
||||
# image_file = r'Color-names.png'
|
||||
image_file = None # image is optional
|
||||
|
||||
layout = [[sg.Graph(
|
||||
canvas_size=(400, 400),
|
||||
graph_bottom_left=(0, 400),
|
||||
graph_top_right=(400, 0),
|
||||
key="-GRAPH-",
|
||||
change_submits=True, # mouse click events
|
||||
drag_submits=True),],
|
||||
[sg.Text("", key="info", size=(60, 1))]]
|
||||
|
||||
window = sg.Window("draw rect on image", layout, finalize=True)
|
||||
# get the graph element for ease of use later
|
||||
graph = window["-GRAPH-"] # type: sg.Graph
|
||||
|
||||
graph.DrawImage(image_file, location=(0,0)) if image_file else None
|
||||
dragging = False
|
||||
start_point = end_point = prior_rect = None
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
if event is None:
|
||||
break # exit
|
||||
if event == "-GRAPH-": # if there's a "Graph" event, then it's a mouse
|
||||
x, y = values["-GRAPH-"]
|
||||
if not dragging:
|
||||
start_point = (x, y)
|
||||
dragging = True
|
||||
else:
|
||||
end_point = (x, y)
|
||||
if prior_rect:
|
||||
graph.DeleteFigure(prior_rect)
|
||||
if None not in (start_point, end_point):
|
||||
prior_rect = graph.DrawRectangle(start_point, end_point, line_color='red')
|
||||
elif event.endswith('+UP'): # The drawing has ended because mouse up
|
||||
info = window.Element("info")
|
||||
info.Update(value=f"grabbed rectangle from {start_point} to {end_point}")
|
||||
start_point, end_point = None, None # enable grabbing a new rect
|
||||
dragging = False
|
||||
else:
|
||||
print("unhandled event", event, values)
|
|
@ -1,31 +0,0 @@
|
|||
#!/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.Button('Red'), sg.Button('Blue'), sg.Button('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 in ('Blue', 'Red'):
|
||||
graph.TKCanvas.itemconfig(circle, fill=event)
|
||||
elif event == '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)
|
|
@ -1,67 +0,0 @@
|
|||
#!/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()
|
|
@ -1,29 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
import random
|
||||
|
||||
BAR_WIDTH = 50
|
||||
BAR_SPACING = 75
|
||||
EDGE_OFFSET = 3
|
||||
GRAPH_SIZE = (500,500)
|
||||
DATA_SIZE = (500,500)
|
||||
|
||||
graph = sg.Graph(GRAPH_SIZE, (0,0), DATA_SIZE)
|
||||
|
||||
layout = [[sg.Text('Bar graphs using PySimpleGUI')],
|
||||
[graph],
|
||||
[sg.Button('OK')]]
|
||||
|
||||
window = sg.Window('Window Title', layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
graph.Erase()
|
||||
if event is None:
|
||||
break
|
||||
|
||||
for i in range(7):
|
||||
graph_value = random.randint(0, 400)
|
||||
graph.DrawRectangle(top_left=(i * BAR_SPACING + EDGE_OFFSET, graph_value),
|
||||
bottom_right=(i * BAR_SPACING + EDGE_OFFSET + BAR_WIDTH, 0), fill_color='blue')
|
||||
graph.DrawText(text=graph_value, location=(i*BAR_SPACING+EDGE_OFFSET+25, graph_value+10))
|
||||
window.Close()
|
|
@ -1,51 +0,0 @@
|
|||
# import PySimpleGUIWeb as sg
|
||||
# import PySimpleGUIQt as sg
|
||||
import PySimpleGUI as sg
|
||||
import math
|
||||
|
||||
SIZE_X = 200
|
||||
SIZE_Y = 100
|
||||
NUMBER_MARKER_FREQUENCY = 25
|
||||
|
||||
def draw_axis():
|
||||
graph.draw_line((-SIZE_X,0), (SIZE_X, 0)) # axis lines
|
||||
graph.draw_line((0,-SIZE_Y), (0,SIZE_Y))
|
||||
|
||||
for x in range(-SIZE_X, SIZE_X+1, NUMBER_MARKER_FREQUENCY):
|
||||
graph.draw_line((x,-3), (x,3)) # tick marks
|
||||
if x != 0:
|
||||
graph.draw_text( str(x), (x,-10), color='green', font='Algerian 15') # numeric labels
|
||||
|
||||
for y in range(-SIZE_Y, SIZE_Y+1, NUMBER_MARKER_FREQUENCY):
|
||||
graph.draw_line((-3,y), (3,y))
|
||||
if y != 0:
|
||||
graph.draw_text( str(y), (-10,y), color='blue')
|
||||
|
||||
# Create the graph that will be put into the window
|
||||
graph = sg.Graph(canvas_size=(400, 400),
|
||||
graph_bottom_left=(-(SIZE_X+5), -(SIZE_Y+5)),
|
||||
graph_top_right=(SIZE_X+5, SIZE_Y+5),
|
||||
background_color='white',
|
||||
key='graph')
|
||||
# Window layout
|
||||
layout = [[sg.Text('Example of Using Math with a Graph', justification='center', size=(50,1), relief=sg.RELIEF_SUNKEN)],
|
||||
[graph],
|
||||
[sg.Text('y = sin(x / x2 * x1)', font='Algerian 18')],
|
||||
[sg.Text('x1'),sg.Slider((0,200), orientation='h', enable_events=True,key='_SLIDER_')],
|
||||
[sg.Text('x2'),sg.Slider((1,200), orientation='h', enable_events=True,key='_SLIDER2_')]]
|
||||
|
||||
window = sg.Window('Graph of Sine Function', layout)
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
if event is None:
|
||||
break
|
||||
graph.erase()
|
||||
draw_axis()
|
||||
prev_x = prev_y = None
|
||||
for x in range(-SIZE_X,SIZE_X):
|
||||
y = math.sin(x/int(values['_SLIDER2_']))*int(values['_SLIDER_'])
|
||||
if prev_x is not None:
|
||||
graph.draw_line((prev_x, prev_y), (x,y), color='red')
|
||||
prev_x, prev_y = x, y
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
#!/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()
|
|
@ -1,66 +0,0 @@
|
|||
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)
|
|
@ -1,77 +0,0 @@
|
|||
import PySimpleGUIWeb as sg
|
||||
# import PySimpleGUI as sg
|
||||
import pymunk
|
||||
import random
|
||||
import socket
|
||||
|
||||
"""
|
||||
Demo that shows integrating PySimpleGUI with the pymunk library. This combination
|
||||
of PySimpleGUI and pymunk could be used to build games.
|
||||
Note this exact same demo runs with PySimpleGUIWeb by changing the import statement
|
||||
"""
|
||||
|
||||
class Ball():
|
||||
def __init__(self, x, y, r, *args, **kwargs):
|
||||
mass = 10
|
||||
self.body = pymunk.Body(mass,
|
||||
pymunk.moment_for_circle(mass, 0, r, (0, 0))) # Create a Body with mass and moment
|
||||
self.body.position = x, y
|
||||
self.shape = pymunk.Circle(self.body, r, offset=(0, 0)) # Create a box shape and attach to body
|
||||
self.shape.elasticity = 0.99999
|
||||
self.shape.friction = 0.8
|
||||
self.gui_circle_figure = None
|
||||
|
||||
class Playfield():
|
||||
def __init__(self):
|
||||
self.space = pymunk.Space()
|
||||
self.space.gravity = 0, 200
|
||||
self.add_wall((0, 400), (600, 400)) # ground
|
||||
self.add_wall((0, 0), (0, 600)) # Left side
|
||||
self.add_wall((600, 0), (600, 400)) # right side
|
||||
|
||||
def add_wall(self, pt_from, pt_to):
|
||||
body = pymunk.Body(body_type=pymunk.Body.STATIC)
|
||||
ground_shape = pymunk.Segment(body, pt_from, pt_to, 0.0)
|
||||
ground_shape.friction = 0.8
|
||||
ground_shape.elasticity = .99
|
||||
self.space.add(ground_shape)
|
||||
|
||||
def add_balls(self):
|
||||
self.arena_balls = []
|
||||
for i in range(1, 200):
|
||||
x = random.randint(0, 600)
|
||||
y = random.randint(0, 400)
|
||||
r = random.randint(1, 10)
|
||||
ball = Ball(x, y, r)
|
||||
self.arena_balls.append(ball)
|
||||
area.space.add(ball.body, ball.shape)
|
||||
ball.gui_circle_figure = graph_elem.DrawCircle((x, y), r, fill_color='black', line_color='red')
|
||||
|
||||
|
||||
# ------------------- Build and show the GUI Window -------------------
|
||||
graph_elem = sg.Graph((600, 400), (0, 400), (600, 0), enable_events=True, key='_GRAPH_', background_color='lightblue')
|
||||
|
||||
layout = [[sg.Text('Ball Test'), sg.T('My IP {}'.format(socket.gethostbyname(socket.gethostname())))],
|
||||
[graph_elem],
|
||||
# [sg.Up(), sg.Down()],
|
||||
[sg.B('Kick'), sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Window Title', layout, ).Finalize()
|
||||
|
||||
area = Playfield()
|
||||
area.add_balls()
|
||||
|
||||
# ------------------- GUI Event Loop -------------------
|
||||
while True: # Event Loop
|
||||
event, values = window.Read(timeout=0)
|
||||
# print(event, values)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
area.space.step(0.02)
|
||||
|
||||
for ball in area.arena_balls:
|
||||
if event == 'Kick':
|
||||
ball.body.position = ball.body.position[0], ball.body.position[1]-random.randint(1,200)
|
||||
graph_elem.RelocateFigure(ball.gui_circle_figure, ball.body.position[0], ball.body.position[1])
|
||||
|
||||
window.Close()
|
|
@ -1,15 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
"""
|
||||
Oh yes, the classic "Hello World". The problem is that you
|
||||
can do it so many ways using PySimpleGUI
|
||||
"""
|
||||
|
||||
sg.PopupNoButtons('Hello World') # the single line
|
||||
|
||||
sg.Window('Hello world', [[sg.Text('Hello World')]]).Read() # single line using a real window
|
||||
|
||||
# This is a "Traditional" PySimpleGUI window code. First make a layout, then a window, the read it
|
||||
layout = [[sg.Text('Hello World')]]
|
||||
window = sg.Window('Hello world', layout)
|
||||
event, values = window.Read()
|
|
@ -1,85 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUIQt 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=(120, 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.Button('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()
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
'''
|
||||
IP Address entry window with digit validation and auto advance
|
||||
If not a digit or ., the ignored
|
||||
. will advance the focus to the next entry
|
||||
On the last input, once it's complete the focus moves to the OK button
|
||||
Pressing spacebar with focus on OK generates an _OK_ event
|
||||
'''
|
||||
|
||||
# create a short-cut element so don't have to type this in over and over
|
||||
InIp = lambda key: sg.Input(do_not_clear=True, size=(3, 1), key=key, pad=(0, 2))
|
||||
|
||||
layout = [[sg.Text('Your typed chars appear here:'), sg.Text('', key='_OUTPUT_')],
|
||||
[InIp(0), sg.T('.'), InIp(1), sg.T('.'), InIp(2), sg.T('.'), InIp(3)],
|
||||
[sg.Button('Ok', key='_OK_', bind_return_key=True), sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Window Title', return_keyboard_events=True).Layout(layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
print(event)
|
||||
if event is None or event == 'Exit':
|
||||
break
|
||||
elem = window.FindElementWithFocus()
|
||||
if elem is not None:
|
||||
key = elem.Key
|
||||
value = values[key] # get value of input field that has focus
|
||||
if event == '.' and key!= '_OK_': # if a ., then advance to next field
|
||||
elem.Update(value[:-1])
|
||||
value = value[:-1]
|
||||
next_elem = window.Element(key+1)
|
||||
next_elem.SetFocus()
|
||||
elif event not in '0123456789':
|
||||
elem.Update(value[:-1])
|
||||
elif len(value) > 2 and key < 3: # if 2 digits typed in, move on to next input
|
||||
next_elem = window.Element(key+1)
|
||||
next_elem.SetFocus()
|
||||
elif len(value)> 2 and key == 3:
|
||||
window.Element('_OK_').SetFocus()
|
||||
print('You entered IP Address {}.{}.{}.{}'.format(*values.values()))
|
||||
|
||||
window.Close()
|
|
@ -1,120 +0,0 @@
|
|||
#!/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.Button('Next', size=(8,2)), sg.Button('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))
|
||||
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
import sys
|
||||
import re
|
||||
QT = True
|
||||
if QT:
|
||||
import PySimpleGUIQt as sg
|
||||
else:
|
||||
import PySimpleGUI as sg
|
||||
|
||||
def autocomplete_popup_show(text_list ):
|
||||
autocomplete_popup_layout = [[sg.Listbox(values=text_list,
|
||||
size=(100,20*len(text_list)) if QT else (15, len(text_list)),
|
||||
change_submits=True,
|
||||
bind_return_key=True,
|
||||
auto_size_text=True,
|
||||
key='_FLOATING_LISTBOX_', enable_events=True)]]
|
||||
|
||||
autocomplete_popup = sg.Window("Borderless Window",
|
||||
default_element_size=(12, 1),
|
||||
auto_size_text=False,
|
||||
auto_size_buttons=False,
|
||||
no_titlebar=True,
|
||||
grab_anywhere=True,
|
||||
return_keyboard_events=True,
|
||||
keep_on_top=True,
|
||||
background_color='black',
|
||||
location=(1320,622),
|
||||
default_button_element_size=(12, 1))
|
||||
|
||||
window = autocomplete_popup.Layout(autocomplete_popup_layout).Finalize()
|
||||
return window
|
||||
|
||||
|
||||
def predict_text(input, lista):
|
||||
pattern = re.compile('.*' + input + '.*')
|
||||
return [w for w in lista if re.match(pattern, w)]
|
||||
|
||||
choices = ['ABC' + str(i) for i in range(30)] # dummy data
|
||||
|
||||
layout = [ [sg.Text('Your typed chars appear here:')],
|
||||
[sg.In(key='_INPUT_', size=(10,1), do_not_clear=True)],
|
||||
[sg.Button('Show'), sg.Button('Exit')],]
|
||||
|
||||
window = sg.Window('Window Title', return_keyboard_events=True).Layout(layout)
|
||||
|
||||
sel_item = -1
|
||||
skip_event = False
|
||||
while True: # Event Loop
|
||||
event, values = window.Read(timeout=500)
|
||||
if event is None or event == 'Exit':
|
||||
break
|
||||
if event != sg.TIMEOUT_KEY:
|
||||
# print(f'ev1 {event}')
|
||||
in_val = values['_INPUT_']
|
||||
prediction_list = predict_text(str(in_val), choices)
|
||||
if prediction_list:
|
||||
try:
|
||||
fwindow.Close()
|
||||
except: pass
|
||||
fwindow = autocomplete_popup_show(prediction_list)
|
||||
list_elem = fwindow.Element('_FLOATING_LISTBOX_')
|
||||
if event == '_COMBO_':
|
||||
sg.Popup('Chose', values['_COMBO_'])
|
||||
if event.startswith('Down') or event.startswith('special 16777237'):
|
||||
sel_item = sel_item + (sel_item<len(prediction_list))
|
||||
list_elem.Update(set_to_index=sel_item)
|
||||
skip_event = True
|
||||
elif event.startswith('Up') or event.startswith('special 16777235'):
|
||||
sel_item = sel_item - (sel_item>0)
|
||||
list_elem.Update(set_to_index=sel_item)
|
||||
skip_event = True
|
||||
if event == '\r' or event.startswith('special 16777220'):
|
||||
chosen = vals2['_FLOATING_LISTBOX_']
|
||||
window.Element('_INPUT_').Update(vals2['_FLOATING_LISTBOX_'][0], select=True)
|
||||
fwindow.Close()
|
||||
sel_item = -1
|
||||
if event.startswith('Escape') or event.startswith('special 16777216'):
|
||||
window.Element('_INPUT_').Update('')
|
||||
|
||||
try:
|
||||
ev2, vals2 = fwindow.Read(timeout=10)
|
||||
if ev2 == '_FLOATING_LISTBOX_' and skip_event and QT:
|
||||
skip_event = False
|
||||
elif ev2 != sg.TIMEOUT_KEY and ev2 is not None:
|
||||
# print(f'ev2 {ev2}')
|
||||
fwindow.Close()
|
||||
window.Element('_INPUT_').Update(vals2['_FLOATING_LISTBOX_'][0], select=True)
|
||||
sel_item = -1
|
||||
fwindow = None
|
||||
except: pass
|
||||
window.Close()
|
|
@ -1,25 +0,0 @@
|
|||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
else:
|
||||
import PySimpleGUI27 as sg
|
||||
|
||||
"""
|
||||
Simple field validation
|
||||
Input field should only accept digits.
|
||||
If non-digit entered, it is deleted from the field
|
||||
"""
|
||||
|
||||
layout = [[sg.Text('Enter digits:')],
|
||||
[sg.Input(do_not_clear=True, enable_events=True, key='_INPUT_')],
|
||||
[sg.Button('Ok', key='_OK_'),sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Window Title').Layout(layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
if len(values['_INPUT_']) and values['_INPUT_'][-1] not in ('0123456789'): # if last char entered not a digit
|
||||
window.Element('_INPUT_').Update(values['_INPUT_'][:-1]) # delete last char from input
|
||||
window.Close()
|
|
@ -1,27 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
"""
|
||||
Demonstrates that using a Column Element to make groups of Elements appear and disappear
|
||||
will cause the layout of the elements in the column to remain as they were. If each individual element
|
||||
were made invisible and then visible, then tkinter puts EACH ELEMENT on a separate row when it is made
|
||||
visible again. This means a row of 6 elements will become a column of 6 elements if you make each of them
|
||||
visible one at a time.
|
||||
|
||||
"""
|
||||
|
||||
layout = [[sg.Column([[sg.Text('My Window')],[sg.Input(key='_IN_'), sg.B('My button', key='_OUT_')]], key='_COL_')],
|
||||
[sg.Button('Invisible'), sg.B('Visible'), sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Window Title', layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
print(event, values)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
if event == 'Invisible':
|
||||
window.Elem('_COL_').Update(visible=False)
|
||||
elif event == 'Visible':
|
||||
window.Elem('_COL_').Update(visible=True)
|
||||
|
||||
window.Close()
|
|
@ -1,29 +0,0 @@
|
|||
#!/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)
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
# import PySimpleGUIQt as sg
|
||||
|
||||
"""
|
||||
tkinter and Qt do not "activate" buttons by pressing the ENTER key with the button highlighted / in focus
|
||||
This demo will enable the application to click on a button if the button has focus (is highlighted) and the
|
||||
user presses the ENTER key.
|
||||
NOTE that the SPACE BAR works correctly out of the box with both tkinter and Qt. If a button has focus and
|
||||
you press the space bar, then tkinter and Qt will both consider that a button click. But not so with the ENTER
|
||||
key.
|
||||
|
||||
The solution is for your program to read the keyboard presses and act upon those directly. It's trivial logic
|
||||
in the end:
|
||||
1. Get a key press
|
||||
2. See if the key is the ENTER key
|
||||
3. Find the Element that currently has focus
|
||||
4. Click the Button if the Element with focus is a button
|
||||
|
||||
"""
|
||||
|
||||
QT_ENTER_KEY1 = 'special 16777220'
|
||||
QT_ENTER_KEY2 = 'special 16777221'
|
||||
|
||||
layout = [ [sg.T('Test of Enter Key use')],
|
||||
[sg.In(key='_IN_')],
|
||||
[sg.Button('Button 1', key='_1_')],
|
||||
[sg.Button('Button 2', key='_2_')],
|
||||
[sg.Button('Button 3', key='_3_')], ]
|
||||
|
||||
window = sg.Window('My new window', layout,
|
||||
return_keyboard_events=True)
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
if event is None:
|
||||
break
|
||||
if event in ('\r', QT_ENTER_KEY1, QT_ENTER_KEY2): # Check for ENTER key
|
||||
elem = window.FindElementWithFocus() # go find element with Focus
|
||||
if elem is not None and elem.Type == sg.ELEM_TYPE_BUTTON: # if it's a button element, click it
|
||||
elem.Click()
|
||||
# check for buttons that have been clicked
|
||||
elif event == '_1_':
|
||||
print('Button 1 clicked')
|
||||
elif event == '_2_':
|
||||
print('Button 2 clicked')
|
||||
elif event == '_3_':
|
||||
print('Button 3 clicked')
|
|
@ -1,25 +0,0 @@
|
|||
#!/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
|
|
@ -1,44 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
else:
|
||||
import PySimpleGUI27 as sg
|
||||
|
||||
# Demonstrates a number of PySimpleGUI features including:
|
||||
# Default element size
|
||||
# auto_size_buttons
|
||||
# Button
|
||||
# Dictionary return values
|
||||
# Update of elements in form (Text, Input)
|
||||
# do_not_clear of Input elements
|
||||
|
||||
|
||||
|
||||
layout = [[sg.Text('Enter Your Passcode')],
|
||||
[sg.Input(size=(10, 1), do_not_clear=True, key='input')],
|
||||
[sg.Button('1'), sg.Button('2'), sg.Button('3')],
|
||||
[sg.Button('4'), sg.Button('5'), sg.Button('6')],
|
||||
[sg.Button('7'), sg.Button('8'), sg.Button('9')],
|
||||
[sg.Button('Submit'), sg.Button('0'), sg.Button('Clear')],
|
||||
[sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')],
|
||||
]
|
||||
|
||||
window = sg.Window('Keypad', default_button_element_size=(5, 2), auto_size_buttons=False, grab_anywhere=False).Layout(layout)
|
||||
|
||||
# Loop forever reading the form's values, updating the Input field
|
||||
keys_entered = ''
|
||||
while True:
|
||||
event, values = window.Read() # read the form
|
||||
if event is None: # if the X button clicked, just exit
|
||||
break
|
||||
if event == 'Clear': # clear keys if clear button
|
||||
keys_entered = ''
|
||||
elif event in '1234567890':
|
||||
keys_entered = values['input'] # get what's been entered so far
|
||||
keys_entered += event # add the new digit
|
||||
elif event == 'Submit':
|
||||
keys_entered = values['input']
|
||||
window.FindElement('out').Update(keys_entered) # output the final string
|
||||
|
||||
window.FindElement('input').Update(keys_entered) # change the form to reflect current key string
|
File diff suppressed because one or more lines are too long
|
@ -1,51 +0,0 @@
|
|||
#!/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')
|
|
@ -1,289 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
"""
|
||||
PySimpleGUI is designed & authored in Python to take full advantage the awesome Python constructs & capabilities.
|
||||
Layouts are represented as lists to PySimpleGUI. Lists are fundamental in Python and have a number of powerful
|
||||
capabilities that PySimpleGUI exploits.
|
||||
|
||||
Many times PySimpleGUI programs can benefit from using CODE to GENERATE your layouts. This Demo illustrates
|
||||
a number of ways of "building" a layout. Some work on 3.5 and up. Some are basic and show concatenation of rows
|
||||
to build up a layout. Some utilize generators.
|
||||
|
||||
These 8 "Constructs" or Design Patterns demonstrate numerous ways of "generating" or building your layouts
|
||||
0 - A simple list comprehension to build a row of buttons
|
||||
1 - A simple list comprehension to build a column of buttons
|
||||
2 - Concatenation of rows within a layout
|
||||
3 - Concatenation of 2 complete layouts [[ ]] + [[ ]] = [[ ]]
|
||||
4 - Concatenation of elements to form a single row [ [] + [] + [] ] = [[ ]]
|
||||
5 - Questionnaire - Using a double list comprehension to build both rows and columns in a single line of code
|
||||
6 - Questionnaire - Unwinding the comprehensions into 2 for loops instead
|
||||
7 - Using the * operator to unpack generated items onto a single row
|
||||
8 - Multiple Choice Test - a practical use showing list comprehension and concatenated layout
|
||||
"""
|
||||
|
||||
"""
|
||||
Construct #0 - List comprehension to generate a row of Buttons
|
||||
|
||||
Comprehensions are super-powers of Python. In this example we're using a comprehension to create 4 buttons that
|
||||
are all on the same row.
|
||||
"""
|
||||
|
||||
|
||||
def layout0():
|
||||
layout = [[sg.Button(i) for i in range(4)]] # A list of buttons is created
|
||||
|
||||
window = sg.Window('Generated Layouts', layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
"""
|
||||
Construct #1 - List comprehension to generate a Column of Buttons
|
||||
|
||||
More list super-power, this time used to build a series of buttons doing DOWN the window instead of across
|
||||
|
||||
"""
|
||||
|
||||
def layout1():
|
||||
layout = [[sg.Button(i)] for i in range(4)] # a List of lists of buttons. Notice the ] after Button
|
||||
|
||||
window = sg.Window('Generated Layouts', layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
|
||||
"""
|
||||
Construct #2 - List comprehension to generate a row of Buttons and concatenation of more lines of elements
|
||||
|
||||
Comprehensions are super-powers of Python. In this example we're using a comprehension to create 4 buttons that
|
||||
are all on the same row, just like the previous example.
|
||||
However, here, we want to not just have a row of buttons, we want have an OK button at the bottom.
|
||||
To do this, you "add" the rest of the GUI layout onto the end of the generated part.
|
||||
|
||||
Note - you can't end the layout line after the +. If you wanted to put the OK button on the next line, you need
|
||||
to add a \ at the end of the first line.
|
||||
See next Construct on how to not use a \ that also results in a VISUALLY similar to a norma layout
|
||||
"""
|
||||
|
||||
def layout2():
|
||||
layout = [[sg.Button(i) for i in range(4)]] + [[sg.OK()]] # if want to split, can't add newline after + to do it
|
||||
|
||||
window = sg.Window('Generated Layouts', layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
|
||||
"""
|
||||
Construct # 3 - Adding together what appears to be 2 layouts
|
||||
|
||||
Same as layout2, except that the OK button is put on another line without using a \ so that the layout appears to
|
||||
look like a normal, multiline layout without a \ at the end
|
||||
|
||||
Also shown is the OLD tried and true way, using layout.append. You will see the append technique in many of the
|
||||
Demo programs and probably elsewhere. Hoping to remove these and instead use this more explicit method of +=.
|
||||
|
||||
Using the + operator, as you've already seen, can be used in the middle of the layout. A call to append you cannot
|
||||
use this way because it modifies the layout list directly.
|
||||
"""
|
||||
|
||||
def layout3():
|
||||
# in terms of formatting, the layout to the RIGHT of the = sign looks like a 2-line GUI (ignore the layout +=
|
||||
layout = [[sg.Button(i) for i in range(4)]]
|
||||
layout += [[sg.OK()]] # this row is better than, but is the same as
|
||||
layout.append([sg.Cancel()]) # .. this row in that they both add a new ROW with a button on it
|
||||
|
||||
window = sg.Window('Generated Layouts', layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
|
||||
"""
|
||||
Construct 4 - Using + to place Elements on the same row
|
||||
|
||||
If you want to put elements on the same row, you can simply add them together. All that is happening is that the
|
||||
items in one list are added to the items in another. That's true for all these contructs using +
|
||||
"""
|
||||
|
||||
def layout4():
|
||||
layout = [[sg.Text('Enter some info')] + [sg.Input()] + [sg.Exit()]]
|
||||
|
||||
window = sg.Window('Generated Layouts', layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
|
||||
"""
|
||||
Construct #5 - Simple "concatenation" of layouts
|
||||
Layouts are lists of lists. Some of the examples and demo programs use a .append method to add rows to layouts.
|
||||
These will soono be replaced with this new technique. It's so simple that I don't know why it took so long to
|
||||
find it.
|
||||
This layout uses list comprehensions heavily, and even uses 2 of them. So, if you find them confusing, skip down
|
||||
to the next Construct and you'll see the same layout built except for loops are used rather than comprehensions
|
||||
|
||||
The next 3 examples all use this same window that is layed out like this:
|
||||
Each row is:
|
||||
Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
|
||||
Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
|
||||
...
|
||||
|
||||
It shows, in particular, this handy bit of layout building, a += to add on additional rows.
|
||||
layout = [[stuff on row 1], [stuff on row 2]]
|
||||
layout += [[stuff on row 3]]
|
||||
|
||||
Works as long as the things you are adding together look like this [[ ]] (the famous double bracket layouts of PSG)
|
||||
"""
|
||||
|
||||
def layout5():
|
||||
questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?',
|
||||
'Get along with people in your family?', 'Get along with people outside your family?',
|
||||
'Get along well in social situations?', 'Feel close to another person',
|
||||
'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?')
|
||||
|
||||
layout = [[sg.T(qnum + 1, size=(2, 2)), sg.T(q, size=(30, 2))] + [sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, col)) for col in range(5)] for qnum, q in enumerate(questions)]
|
||||
layout += [[sg.OK()]]
|
||||
|
||||
window = sg.Window('Computed Layout Questionnaire', layout)
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
|
||||
"""
|
||||
Construct #6 - Computed layout without using list comprehensions
|
||||
This layout is identical to Contruct #5. The difference is that rather than use list comprehensions, this code
|
||||
uses for loops. Perhaps if you're a beginner this version makes more sense?
|
||||
|
||||
In this example we start with a "blank layout" [[ ]] and add onto it.
|
||||
|
||||
Works as long as the things you are adding together look like this [[ ]] (the famous double bracket layouts of PSG)
|
||||
"""
|
||||
|
||||
|
||||
def layout6():
|
||||
questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?',
|
||||
'Get along with people in your family?', 'Get along with people outside your family?',
|
||||
'Get along well in social situations?', 'Feel close to another person',
|
||||
'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?')
|
||||
|
||||
layout = [[]]
|
||||
for qnum, question in enumerate(questions): # loop through questions
|
||||
row_layout = [sg.T(qnum + 1, size=(2, 2)), sg.T(question, size=(30, 2))] # rows start with # and question
|
||||
for radio_num in range(5): # loop through 5 radio buttons and add to row
|
||||
row_layout += [sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, radio_num))]
|
||||
layout += [row_layout] # after row is completed layout, tack it onto the end of final layout
|
||||
|
||||
layout += [[sg.OK()]] # and finally, add a row to the bottom that has an OK button
|
||||
|
||||
window = sg.Window('Computed Layout Questionnaire', layout)
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Construct #7 - * operator and list comprehensions
|
||||
Using the * operator from inside the layout
|
||||
List comprehension inside the layout
|
||||
Addition of rows to layouts
|
||||
All in a single variable assignment
|
||||
|
||||
NOTE - this particular code, using the * operator, will not work on Python 2 and think it was added in Python 3.5
|
||||
|
||||
This code shows a bunch of questions with Radio Button choices
|
||||
|
||||
There is a double-loop comprehension used. One that loops through the questions (rows) and the other loops through
|
||||
the Radio Button choices.
|
||||
Thus each row is:
|
||||
Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
|
||||
Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
|
||||
Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
|
||||
|
||||
What the * operator is doing in these cases is expanding the list they are in front of into a SERIES of items
|
||||
from the list... one after another, as if they are separated with comma. It's a way of "unpacking" from within
|
||||
a statement.
|
||||
|
||||
The result is a beautifully compact way to make a layout, still using a layout variable, that consists of a
|
||||
variable number of rows and a variable number of columns in each row.
|
||||
"""
|
||||
|
||||
def layout7():
|
||||
questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?',
|
||||
'Get along with people in your family?', 'Get along with people outside your family?',
|
||||
'Get along well in social situations?', 'Feel close to another person',
|
||||
'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?')
|
||||
|
||||
layout = [[*[sg.T(qnum + 1, size=(2, 2)), sg.T(q, size=(30, 2))], # These are the question # and the question text
|
||||
*[sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, col)) for col in range(5)]] for qnum, q in enumerate(questions)] + [[sg.OK()]] # finally add an OK button at the very bottom by using the '+' operator
|
||||
|
||||
window = sg.Window('Questionnaire', layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
print(event, values)
|
||||
window.Close()
|
||||
|
||||
|
||||
"""
|
||||
Construct #8 - Computed layout using list comprehension and concatenation
|
||||
This layout shows one practical use, a multiple choice test. It's been left parse as to focus on the generation
|
||||
part of the program. For example, default keys are used on the Radio elements. In reality you would likely
|
||||
use a tuple of the question number and the answer number.
|
||||
|
||||
In this example we start with a "Header" Text element and build from there.
|
||||
"""
|
||||
|
||||
def layout8():
|
||||
# The questions and answers
|
||||
q_and_a = [
|
||||
['1. What is the thing that makes light in our solar system', ['A. The Moon', 'B. Jupiter', 'C. I dunno']],
|
||||
['2. What is Pluto', ['A. The 9th planet', 'B. A dwarf-planet', 'C. The 8th planet', 'D. Goofies pet dog']],
|
||||
['3. When did man step foot on the moon', ['A. 1969', 'B. 1960', 'C. 1970', 'D. 1869']], ]
|
||||
|
||||
layout = [[sg.Text('Astronomy Quiz #1', font='ANY 15', size=(30, 2))]] # make Header larger
|
||||
|
||||
# "generate" the layout for the window based on the Question and Answer information
|
||||
for qa in q_and_a:
|
||||
q = qa[0]
|
||||
a_list = qa[1]
|
||||
layout += [[sg.Text(q)]] + [[sg.Radio(a, group_id=q)] for a in a_list] + [[sg.Text('_' * 50)]]
|
||||
|
||||
layout += [[sg.Button('Submit Answers', key='SUBMIT')]]
|
||||
|
||||
window = sg.Window('Multiple Choice Test', layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.read()
|
||||
if event in (None, 'SUBMIT'):
|
||||
break
|
||||
sg.popup('The answers submitted were', values)
|
||||
window.close()
|
||||
|
||||
|
||||
# ------------------------- Call each of the Constructs -------------------------
|
||||
|
||||
layout0()
|
||||
layout1()
|
||||
layout2()
|
||||
layout3()
|
||||
layout4()
|
||||
layout5()
|
||||
layout6()
|
||||
layout7()
|
||||
layout8()
|
|
@ -1,27 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
|
||||
names = ['Roberta', 'Kylie', 'Jenny', 'Helen',
|
||||
'Andrea', 'Meredith','Deborah','Pauline',
|
||||
'Belinda', 'Wendy']
|
||||
|
||||
layout = [ [sg.Text('Listbox with search')],
|
||||
[sg.Input(do_not_clear=True, size=(20,1),enable_events=True, key='_INPUT_')],
|
||||
[sg.Listbox(names, size=(20,4), enable_events=True, key='_LIST_')],
|
||||
[sg.Button('Chrome'), sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Listbox with Search').Layout(layout)
|
||||
# Event Loop
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
if event is None or event == 'Exit': # always check for closed window
|
||||
break
|
||||
if values['_INPUT_'] != '': # if a keystroke entered in search field
|
||||
search = values['_INPUT_']
|
||||
new_values = [x for x in names if search in x] # do the filtering
|
||||
window.Element('_LIST_').Update(new_values) # display in the listbox
|
||||
else:
|
||||
window.Element('_LIST_').Update(names) # display original unfiltered list
|
||||
if event == '_LIST_' and len(values['_LIST_']): # if a list item is chosen
|
||||
sg.Popup('Selected ', values['_LIST_'])
|
||||
|
||||
window.Close()
|
|
@ -1,227 +0,0 @@
|
|||
#!/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()
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
#!/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()
|
|
@ -1,101 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('TkAgg')
|
||||
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
Based on information from: https://matplotlib.org/3.1.0/gallery/user_interfaces/embedding_in_tk_sgskip.html
|
||||
(Thank you Em-Bo & dirck)
|
||||
"""
|
||||
|
||||
|
||||
#------------------------------- 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)):
|
||||
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
|
||||
figure_canvas_agg.draw()
|
||||
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
|
||||
return figure_canvas_agg
|
||||
#------------------------------- 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', layout, finalize=True)
|
||||
|
||||
# add the plot to the window
|
||||
fig_canvas_agg = draw_figure(window['canvas'].TKCanvas, fig)
|
||||
|
||||
event, values = window.read()
|
|
@ -1,56 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
from random import randint
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
def draw_figure(canvas, figure, loc=(0, 0)):
|
||||
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
|
||||
figure_canvas_agg.draw()
|
||||
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
|
||||
return figure_canvas_agg
|
||||
|
||||
def main():
|
||||
|
||||
NUM_DATAPOINTS = 10000
|
||||
# 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.Text('Progress through the data')],
|
||||
[sg.Slider(range=(0, NUM_DATAPOINTS), size=(60, 10), orientation='h', key='-SLIDER-')],
|
||||
[sg.Text('Number of data points to display on screen')],
|
||||
[sg.Slider(range=(10, 500), default_value=40, size=(40, 10), orientation='h', key='-SLIDER-DATAPOINTS-')],
|
||||
[sg.Button('Exit', size=(10, 1), 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, finalize=True)
|
||||
|
||||
canvas_elem = window.FindElement('-CANVAS-')
|
||||
slider_elem = window.FindElement('-SLIDER-')
|
||||
canvas = canvas_elem.TKCanvas
|
||||
|
||||
# draw the initial plot in the window
|
||||
fig = Figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.set_xlabel("X axis")
|
||||
ax.set_ylabel("Y axis")
|
||||
ax.grid()
|
||||
fig_agg = draw_figure(canvas, fig)
|
||||
# make a bunch of random data points
|
||||
dpts = [randint(0, 10) for x in range(NUM_DATAPOINTS)]
|
||||
|
||||
for i in range(len(dpts)):
|
||||
event, values = window.Read(timeout=10)
|
||||
if event in ('Exit', None):
|
||||
exit(69)
|
||||
slider_elem.Update(i) # slider shows "progress" through the data points
|
||||
ax.cla() # clear the subplot
|
||||
ax.grid() # draw the grid
|
||||
data_points = int(values['-SLIDER-DATAPOINTS-']) # draw this many data points (on next line)
|
||||
ax.plot(range(data_points), dpts[i:i+data_points], color='purple')
|
||||
fig_agg.draw()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,51 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
import PySimpleGUI as sg
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
import matplotlib.pyplot as plt
|
||||
from numpy.random import rand
|
||||
|
||||
def draw_figure(canvas, figure):
|
||||
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
|
||||
figure_canvas_agg.draw()
|
||||
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
|
||||
return figure_canvas_agg
|
||||
|
||||
|
||||
|
||||
|
||||
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.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, finalize=True)
|
||||
|
||||
canvas_elem = window.FindElement('-CANVAS-')
|
||||
canvas = canvas_elem.TKCanvas
|
||||
# draw the intitial scatter plot
|
||||
fig, ax = plt.subplots()
|
||||
ax.grid(True)
|
||||
fig_agg = draw_figure(canvas, fig)
|
||||
|
||||
while True:
|
||||
event, values = window.Read(timeout=10)
|
||||
if event in ('Exit', None):
|
||||
exit(69)
|
||||
|
||||
ax.cla()
|
||||
ax.grid(True)
|
||||
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()
|
||||
fig_agg.draw()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,878 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import PySimpleGUI as sg
|
||||
import matplotlib
|
||||
matplotlib.use('TkAgg')
|
||||
import inspect
|
||||
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
Each plotting function, complete with imports, was copied directly from Matplot examples page
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def PyplotSimple():
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# evenly sampled time .2 intervals
|
||||
t = np.arange(0., 5., 0.2) # go from 0 to 5 using .2 intervals
|
||||
|
||||
# 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):
|
||||
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
|
||||
figure_canvas_agg.draw()
|
||||
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
|
||||
return figure_canvas_agg
|
||||
|
||||
def delete_figure_agg(figure_agg):
|
||||
figure_agg.get_tk_widget().forget()
|
||||
plt.close('all')
|
||||
|
||||
# -------------------------------- GUI Starts Here -------------------------------#
|
||||
# fig = your figure you want to display. Assumption is that 'fig' holds the #
|
||||
# information to display. #
|
||||
# --------------------------------------------------------------------------------#
|
||||
|
||||
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 = list(fig_dict)
|
||||
col_listbox = [[sg.Listbox(values=listbox_values, enable_events=True, size=(28, len(listbox_values)), key='-LISTBOX-')],
|
||||
[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', layout, grab_anywhere=False, finalize=True)
|
||||
figure_agg = None
|
||||
# The GUI Event Loop
|
||||
while True:
|
||||
event, values = window.read()
|
||||
# print(event, values) # helps greatly when debugging
|
||||
if event in (None, 'Exit'): # if user closed window or clicked Exit button
|
||||
break
|
||||
if figure_agg:
|
||||
# ** IMPORTANT ** Clean up previous drawing before drawing again
|
||||
delete_figure_agg(figure_agg)
|
||||
choice = values['-LISTBOX-'][0] # get first listbox item chosen (returned as a list)
|
||||
func = fig_dict[choice] # get function to call from the dictionary
|
||||
window['-MULTILINE-'].Update(inspect.getsource(func)) # show source code to function in multiline
|
||||
fig = func() # call function to get the figure
|
||||
figure_agg = draw_figure(window['-CANVAS-'].TKCanvas, fig) # draw the figure
|
||||
window.close()
|
|
@ -1,889 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python
|
||||
import PySimpleGUI as sg
|
||||
import matplotlib
|
||||
matplotlib.use('TkAgg')
|
||||
import inspect
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
"""
|
||||
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):
|
||||
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
|
||||
figure_canvas_agg.draw()
|
||||
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
|
||||
return figure_canvas_agg
|
||||
|
||||
def delete_figure_agg(figure_agg):
|
||||
figure_agg.get_tk_widget().forget()
|
||||
plt.close('all')
|
||||
|
||||
|
||||
# -------------------------------- 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 = list(fig_dict)
|
||||
col_listbox = [[sg.Listbox(values=listbox_values, change_submits=True, size=(28, len(listbox_values)), key='-LISTBOX-')],
|
||||
[sg.T(' ' * 12), sg.Exit(size=(5, 2))]]
|
||||
|
||||
col_multiline = sg.Column([[sg.Multiline(size=(70, 35), key='-MULTILINE-')]])
|
||||
col_canvas = sg.Column([[ sg.Canvas(size=(figure_w, figure_h), key='-CANVAS-')]])
|
||||
col_instructions = sg.Column([[sg.Pane([col_canvas, col_multiline], size=(800,600))],
|
||||
[sg.Text('Grab square above and slide upwards to view source code for graph')]])
|
||||
|
||||
layout = [[sg.Text('Matplotlib Plot Test', font=('ANY 18'))],
|
||||
[sg.Column(col_listbox), col_instructions],]
|
||||
|
||||
# create the form and show it without the plot
|
||||
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI',layout, resizable=True, finalize=True)
|
||||
|
||||
canvas_elem = window.FindElement('-CANVAS-')
|
||||
multiline_elem= window.FindElement('-MULTILINE-')
|
||||
figure_agg = None
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
|
||||
if figure_agg:
|
||||
# ** IMPORTANT ** Clean up previous drawing before drawing again
|
||||
delete_figure_agg(figure_agg)
|
||||
choice = values['-LISTBOX-'][0] # get first listbox item chosen (returned as a list)
|
||||
func = fig_dict[choice] # get function to call from the dictionary
|
||||
window['-MULTILINE-'].Update(inspect.getsource(func)) # show source code to function in multiline
|
||||
fig = func() # call function to get the figure
|
||||
figure_agg = draw_figure(window['-CANVAS-'].TKCanvas, fig) # draw the figure
|
||||
|
|
@ -1,674 +0,0 @@
|
|||
#!/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 in ('Exit', None):
|
||||
exit(0)
|
||||
|
||||
run_a_ping_and_graph()
|
||||
photo = draw(fig, canvas)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,108 +0,0 @@
|
|||
#!/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 in ('Exit', None):
|
||||
break
|
||||
|
||||
run_a_ping_and_graph()
|
||||
photo = draw(fig, canvas)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,53 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
# import PySimpleGUIQt as sg # portable to QT
|
||||
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
|
||||
|
||||
ImageButton = lambda image_filename, key:sg.Button('', button_color=(background,background), image_filename=image_filename, image_size=(50, 50), image_subsample=2, border_width=0, key=key)
|
||||
|
||||
# define layout of the rows
|
||||
layout= [[sg.Text('Media File Player', font=("Helvetica", 25))],
|
||||
[sg.Text('', size=(15, 2), font=("Helvetica", 14), key='output')],
|
||||
[ImageButton(image_restart, key='Restart Song'), sg.Text(' ' * 2),
|
||||
ImageButton(image_pause, key='Pause'),
|
||||
sg.Text(' ' * 2),
|
||||
ImageButton(image_next, key='Next'),
|
||||
sg.Text(' ' * 2),
|
||||
sg.Text(' ' * 2),ImageButton(image_exit, key='Exit')],
|
||||
]
|
||||
|
||||
# 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()
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUI as sg
|
||||
else:
|
||||
import PySimpleGUI27 as sg
|
||||
|
||||
|
||||
def ShowMeTheButtons():
|
||||
# ------ Menu Definition ------ #
|
||||
menu_def = [['&File', ['&Open', '&Save', '&Properties', 'E&xit' ]],
|
||||
['&Edit', ['&Paste', ['Special', 'Normal',], 'Undo'],],
|
||||
['&Toolbar', ['---', 'Command &1', 'Command &2', '---', 'Command &3', 'Command &4']],
|
||||
['&Help', '&About...'],]
|
||||
|
||||
sg.SetOptions(auto_size_buttons=True, margins=(0,0), button_color=sg.COLOR_SYSTEM_DEFAULT)
|
||||
|
||||
toolbar_buttons = [[sg.Button('', image_data=close64[22:],button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0,0), key='_close_'),
|
||||
sg.Button('', image_data=timer64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_timer_'),
|
||||
sg.Button('', image_data=house64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_house_'),
|
||||
sg.Button('', image_data=cpu64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cpu_'),
|
||||
sg.Button('', image_data=camera64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_camera_'),
|
||||
sg.Button('', image_data=checkmark64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_checkmark_'),
|
||||
sg.Button('', image_data=cookbook64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cookbook_'),
|
||||
sg.Button('', image_data=download64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_download_'),
|
||||
sg.Button('', image_data=github64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_github_'),
|
||||
sg.Button('', image_data=psg64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_psg_'),
|
||||
sg.Button('', image_data=run64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_run_'),
|
||||
sg.Button('', image_data=storage64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_storage_'),
|
||||
]]
|
||||
|
||||
# layout = toolbar_buttons
|
||||
# ------ GUI Defintion ------ #
|
||||
layout = [ [sg.Menu(menu_def, )],
|
||||
[sg.Frame('', toolbar_buttons,title_color='white', background_color=sg.COLOR_SYSTEM_DEFAULT, pad=(0,0))],
|
||||
[sg.Text('', size=(20,8))],
|
||||
[sg.Text('Status Bar', relief=sg.RELIEF_SUNKEN, size=(55, 1), pad=(0, 3), key='_status_')]
|
||||
]
|
||||
|
||||
window = sg.Window('Toolbar').Layout(layout)
|
||||
|
||||
# ---===--- Loop taking in user input --- #
|
||||
while True:
|
||||
button, value = window.Read()
|
||||
print(button)
|
||||
if button in ('_close_', 'Exit') or button is None:
|
||||
break # exit button clicked
|
||||
elif button == '_timer_':
|
||||
pass # add your call to launch a timer program
|
||||
elif button == '_cpu_':
|
||||
pass # add your call to launch a CPU measuring utility
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
house64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAF50lEQVRIiYWVX2wc1RWHf+ece+/szu7a47Vjx+s42CRA/hASAQFCEgcTgkjAjVryQFNXtJUqFfJQqe0DbZ+KKvEcVU1VpAYa+idSq1IKFFTVgUBccKAJSYkViC2TxCZZx2uv7V3Wu56Z24fZNU4aykhXGmnune9+v3N0L/AlDzEDAC/JZPDS/v1bsod++7M9u3cnAUCJ0Jetl//3kYnIWiuu54W/ePKJrV3DIwcnXnn1a11bu+KX6+r6Bs+eDYmIAFw7EIvFKJlM8hcCmBnWWhZjwj88/fS9D50bfqH/9ZfaBsq5ibaPPtmx6/7ulmE38erQuXOWKRJREv3fAojH45xKpei6ACKCtZabMpnw+R8/1dV95Ohf33y7LzW8LTWf2FTvDQ5dydW9eaqrZ3v30nwm8974TPHb8VjdrkKhsEk75sEg8I+JSCAi/wtYiCWdDn/5rccf2nni5AvH3u93L25vDNdvu8Fb1d7K0/WhPjdemHTfOrl16+13ZG7rufv+W9p574ab0tuD0PJYNv9cMpm0nufJVYCFWOLx8I8//MEDO//17sHj/Ucbzj/aMX/nfcu9zuYMnHgSbU0xKTSTHhotzKijH9x6g5nVD3x9nfPIfTerDz8afea9wcvvl8tlmpqaCtXiWMIw5KZly8Jf9e7d0f27w38ZmPrUXnx8bXn5inpv5FIdLs1YGH8KFeXZ1kTFyGNO6sIrF/P5F4+3FGdLvPknXwVMLA0ATU1N3NLSEhV5IZbGxvDArp27H/7HPw+dmByT7N5bg7VbOrxsVuF5vxctG7+BN05fwgdrfk7rVRY3t8xJsDQu2aLvF45+rFS+RBdSDX9/++TQO77vU6EwGwozk7WWxHXDw729PY/0HXn2dPZC4tPvbvRX3NPhtTUtQ25iBqpcwio3j/riEO5p9XFj+RQSDR7S6ZSybUpPTPnFXN+gWellMNnZ+efzo6NBZmmrklq3HNqz5ys7f3/4T/+hEmef3OyvvKvDW+K1QZTG5VwJL8tuxFd349hYgA+XPIq73AtI6RmIU2/TqQTplQmaKFGucuTf63esXr1uMpPpGzhxYla8pia7/95Nj+3pe+PgGVWxk9/bHLRv7PAaU60gHYMii9x0gPrOTdiyKgFz5WPcvmYV1pcHAKqAdIy0E0d9IiZ6uauuVChXev2dO+7u7Owotbe/RU/19Gx4ZnTsxbPDg61jP314rvW2ZfUNiWYQKwAWREC5UIQjAsfRoPIsyCSB8gxKbhrWAhYAgTA3N4Wx8fHKmd8M5KXvTPPaffsOSEtb21wq5mSGNjevuGXHusYGt4XYuCCSCEIKM8U55D+bQ75YQd5nTBXnkPcVtIlBm1h1LkPrpHUNK789Redn1fFxN31IvdzfP/038PefaNsg23R8nziuZRICRa3r+wGe/fVhTI1nobWCDUMABD+0+OZ3enHnxnWoVCogEIjFBkWhlTfeVHxtNf1o/4Hn3lVB4HMQhEEIzivtQMSAWQOwYCIEoY+gOINEZRocEmAtCEChAlT8EErFEAQEIgKRgJWGk6ifDwOaBAAFWzsiWEQ0SEw1/8iAQkY8ZsBJBZKoLgwAcxaiTDRf7OcAMWBisgglAtQIQAhisDgQqRowQUKBUQw3rhYKL2QRIASzgigHEmABQJ/fALYKWHSKgqIdiAEQgplBwnCMQrMxoGp0IMK8nQexBosDFiwyuPr8VFfhiEDVmCIhBgnBKIWkdgBWMBzik4KDXOUzKJFFEQFECqAvANQcWAxYG8BWDXyCoxW8pAFV76c1MYsEEcAGrAw4iADMGrQAoGsBkbqIA2GnGpFAhGG0IOkQQARrAaMY0yUBiQJLDCKIDLjWIMH1DagWkXIAG4JYQAI4WuC5GiCBBaAZSDgqqolyQP4iA2ZY68Pa8HoRMZgNRMwCgNlCaY2GlAsihrWAVoRUwYJZAWwgEkYGYmqFtlqbawC1biWORu2dGT40ZoK4BTMsABUQKmGZ3Gjb1TVR7o4Tw8jISHDy1OkyAPwXWfQkSWcWg6cAAAAASUVORK5CYII='
|
||||
|
||||
timer64 = 'data:image/png;base64,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'
|
||||
|
||||
close64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAE30lEQVRIiZ2VXYgdRRqGn6+quvucM/85iRoTNevMBJFEWY0GFQTBC1HBlaz/jMpoFFfXBdmFvdiLvRIEFRHFGBXMjUQhF/6Bol6sSNaIruCNir/R/Dlx5iRzck736e6qby/6JDlx9CIWFN10Ue/7vW+9X7XcDn8bryWPL2vERkNQQPj9Q72K7F3s7Hxb9bZ98L0bj91jt1y23kxNTxIEGUQ/aTYR6WW9cud/Prx01zf7/7FP5EHXHG7Y6bVTpBPLMSegCWKEEMKvkihgjEWDP+FbEjxTa1bjv9l/CsIKF3ypHhUDSFGACCKC956iKKjV6/hfkCjgUNK0TW1oCA3h+EJk8UUBYFCsQaSyRajArUWLnEONcTrT68nTLtZaEKmmMTiUlsREGy9HO0dgcL1y6lgtZrAsEYFexhwxq2buYfru+1mcOo+828UYg4rgUH7OSkY3zbDq1lkaV1yFP9TqEyy18jiBCMF7DjYmOOu+hxifnCSKItZuvp/F6fPJ05TEwE+dHhN33MfpGy4iFAVjf7qF8etvBV9y1IilBApGIMt6TExOM372JKqKqhLFMdOz93Jk6jx+bHVoztzLyj9eiHqP2Gq7O3UlGAuq1RwYDlUwhoChMdSAz3ZxaEeD8T/fBggaAnGtxpqZWdKFBSbOPLMCCQGJItJPdrHw4lOYRgNsBM6dSCDGErIuodtGkhoyPEr68U5svcbI1ZsQY0CV2vAw9ZGRKjEiSBTR/fQjDm9/AddcjqoSul182kYHVDhJauRffUH7wD7ilatxzVOwI6PM7XiJLO2x4rob0CgGVTSEKigidD94j/ltW9Dg0b0/4BfmyQ8ewKUdWLZ6wCIB9SXFXJvQ+hLkc6QeEznHf199jY1rpjh1w0ZUFTGm7z18/tSj2Hffor5shKLdhhJCADMcw7IlKRIkAqkJRIa4LPl6d5c/PPJkBd5vpArcArD+ue101l1Md08bFxuIBUlOyOUggUIAVIl94Kv5wKqtz7L+7r/0bRHEmApcFbwnHhljw6tv0b3kEtK5gDWmj/GbfQAWZbdaztjyPOfP3oN6D8GDCO133uDAvx9CyxKsRX1JMjbBBa+8Rnbl5RSpR35RfXUGfVLnYGFBcTfdwLo77yLkPYy14CLa773JngfuoNy7QOh2WPnw09WVkufUm8s598G/s+eT9wmBJZ1m+sVTFNBc4Wi8vJ3v//kAJk7AOhbf3MGezTfjWwuYCcv8s1s58K+/okWOxDGdjz5g7+YZtKRSoL+igCp5FKVntGk48sTTzDWb1C+4mB833wgETD2CELBjEfNbtyAjo4xdcz27N11L6B5GGoZQhN+26KiSoII9LebnJx9BkggzNIQkyfEdItiRQGvbM7S2bQHJMGN1NO8ds2dQhBORYBCjAFEE1kFSw0QxuAiTJCAGce64vz4gviTkOTJcErIMMRbyDIxg7bHTFnc47clcmpdj43VkeBRJEkytgdTqSL2OiRMkSRDroH9t4EtCUaBZhmYpIUurZ9pFfVnuX+w62xfjeq3D3/6vbifXrT1XkzgWdREmipA4RlwMUYRY21cg/X+lJ5gSbIHGOVovCHmOCSX7DrbMx599icIhVI2cA5c5mC1gbGnITm4oqAOr0PoOXs9g51HAGiITyCDByXDp4KuiaoESmP8/YC0Y5GajmEsAAAAASUVORK5CYII='
|
||||
|
||||
psg64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAABlbkiVhkyZikyFjmShjkyhlly1mli5nlylnmS5olyppnC5qmi5rmzBpmDBpmTFqmDFqmTFqmjNrmzFrnDNsmzJsnDJtnTFunjNvnzRsmTRtmzVunTVunjVvnzZwnjlxny5toDFvozJwoTNzpjVwoDRzpDRzqDd4rThyojp0ozl1pTt2pjt3pz11ozt4qDl4qTp6rTl6rzx4qD55qTx5qj97qz17rT58rD98rT9+rz5+sEB3pEF9rkB+r0F+sEF/sT2AtD6AtT6Btk+Aqk2CrEOBs0GBtEKCtEGCtUSBskWBs0SCtEWDtUaEtkWFt0aGt0KFuUCEukOGu0eFuEWHu0eJvEiGuUiHukiIuUiIukmJu0uKvEyKvFCAp1CCqlODrVKCrl2Kr1OEs1KGsFWGsFaIsVaPvFqKsl6NsmKNsWeTuG6YvHadvlySwV6UxF+YxW+bxW6dxnKewHGex3SdwHSfx3egwXGizHmgwHqkxnyjxHio0f/RMf7QMv/TMf/SMv3SNf7UOv7UO//UPP/UPf/UPv/VP//WPP/XPv/VQ//WQv7XQ//WRP/XRf/WR//YRv/YSP/YSf/YSv/ZS//aS//ZTf/aTP7aTf7aTv7bT//cT//bUP/cUP7cUv/cU/7cVf/eVf/fVv/eV/7dWP/eWP/eWf/fWv/fW//fYvzcaf/hW//gXP/gXv/gX//hYP/hYf/hY//iYP/jY//gZf/iZP7iZf/jZv/iZ//lZv/jaf/kav7ka//maf/ma//kbf/lbv7mbP/mbv/mb//id//mcP/ncv/nc//ld//ndv/meP/ocf/ocv/oc//odP/odf/odv/peP/pff/qfY+11ZSzz5G41qC81aW/1P/jgf/qiv/qjv7qoMnZ5szb587d6eDm2+fo1+7v3e/x3vXw1fHx3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJblQd8AAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAABcQAAAXEAEYYRHbAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjFjKpxLAAABzUlEQVQoU2P4jwNAJZIEuLJA9M2u/iNgAaiEELOgIFPc//+r6puaalvAQmAJdg4pPj4h5oT/2+raWtqaGmAS/PxC/IJAGdbEB7saW5pb20AyQAkFNiFhUSEgkGZNfjizua29u70XJJHr8j+eV0RGVkRMTJb56u2mvt7eSR0gCT2gPsbMGzbi8hJyPDl3OidPnDRlwhagRHbG/zTXe5WSqqqqmpzXb/VMmz5jztSVIDtSWFLvl3Jrampq8ZY8WThj1tx586ZCXFV9t1xRR1tbR6Lw0f6ZC+YvWDAb6tz/xUom+rrGymWPD8xaunjZ0oUgMZBEsYqZqampWsnTY/PWLF+xZhFIHCRRpW5raWFhUPT/3IJ1a9euW/H//5oTYAlDezs7Kwvv//+XbN6wcev6//+3/z8FltDwcrC3N8/7v3rHtu07Nv3/vxVo0CWQhJGPm5ubdf7/TXt279699//JnTA70j38fH19wv//33b00OGj+w6fPXz5KMRVTiH+/gHuFf//7zl+5szZs2fO7YPo+H/FOSIyPMqz5v//g+dAMocvQCX+XwsMjYmNdgSy9p0/d/bgRZAYWOL//4LgoDAwY+++02AaJoEJcEj8/w8A4UqG4COjF7gAAAAASUVORK5CYII='
|
||||
|
||||
cpu64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAFi0lEQVRIiZ1WS2wbRRj+/5l92O6u7WycTW03xLEd6uBUjVO1hfbSByLQEwhxoIhbCwcuvApUiEOFhAQVEiAOvK4gqoojSEhtgUNApaJKG5qkdWpa1JfcZL3teu3sZmeGQ9ZVGhIJ8Z/+2Vn93//45psBWMMqlcoXxWLxEACAaZpju3btOkkIoZRSsnv37hOmaY4BABSLxUOVSuXzteJIq32klMqyLBuqqhoAAKqqGpTSpKIoSUQESZK6OnuKohiKohiUUpkxtvivWCvWBABEOp1+sr+//5V169ZtnJub+6FUKh3Rdf3hVqv1l6Zp5Ww2ezASifQ0Go3fhoaGjsZisYdardaM4zjTiEgAQHQC4j0HkQghAAC4oiiJRCKxBQBIs9m8oOt6iRASa7VaVwEAYrFYP+e85TjOpXg8PiyE4LZtn/F93wYAgogghOD3AYS+UFW1q1AovGoYxp4wGxIEgS2EEIQQCQCAcx7gkslCCB8AwLbt07Va7SPP8xqdWPdmIElSxDTNfZZlncrn828MDg6+VavVPvF9fy4Wi/X19fUdWJHMfSaEYJlMZgwRpVqtdtQwjD31ev2HIAgWpJGRkS8VRTEMw9g9OTm5v7u7+9GpqamXq9XqxwAAmzZt+oBzjpzzYC0QIQRDRJpIJLanUqmdw8PDX1mW9ZPv+5bkOM5FVVVTiURia1i24rruDQCAUqn09sDAwCHGGEdEadnwlgOJZT5BRMIYc5rN5iXP8+ax0y9N04qc84Vt27aduHjx4uuEED46Ovo95xxEOH1ExKWEhQh9DPe4JEl0fn7+14mJiecQUWo2m7MAgNQ0zb3d3d3bhoaGjrTb7Wld1x/p6uoa2bBhw4uyLGsAEFBKKSIi51xQSjFcIiICIQRDAhDXdWue502Vy+X3hRALqqr2SoODg2/KsmzE4/GNlNJ1nPOF9evXPxYEAbiue7lWq72rKIphmub+GzdufBeNRg1d14cZYx4hhBJClFQqNRbOQlBKo8lkcms+n48vLi5a0vj4+OOKoiTT6fQzjuNcJYRIQRCALMswOzv7LSEk0tPT85TjOBeCIKi12+1rtm3/ruv6FgDgAMB7e3vHgiAAQgh1HOfquXPnXr958+Zx3/dtshopltp7nyEiUtd1rxuG8URfX99B13Un2+32rKIo3ZzztRgMdOfOnT/mcrkX+vv79zcajVOapm3XNC3HGINoNNpnWdZJz/P+TiQSOzRNK6bT6WcjkUh/q9WaQUTIZrMHEFEjhECz2fzL9/2ZkZGRz0zT3JfNZp+WqtXq+5FIJJXL5V5kjLVDdgDnnMVisYFyufxVSFHgnO9gjDFElIvF4jth34ExxgCAIiIyxtq2bZ+5cuXK5wsLC3NSvV4/BQDCsqw/hBBBLpeTO+WF/KdhC0TIHAoAIggCjogYMnjpEBAi27Z96ezZsy90aCoVCoXXVFVNZbPZ/TMzMy9xzr1ljSdhYLHicN0DCkFYWKFnGMamUqn06fXr17/xPG9e0nV9Y6jnWqiAPCydrTm5laxY+pcCABdCcEqprmnag4qiWNLExMTBZWI3Ho/Hd2Qymb1CCBpm+V8AQJZluHPnzum5ubnx8+fPH+iI3apync/nX04mk9vDXihCiMX/K9drXTjJZDK5FRHJ3bt3/9R1/cH/e+Esb0FnkKK3t3ff5s2bv+7p6Rm7devWsXK5/GGhUDjsOM5kNBp9oFKpfKNp2kC9Xv9xdHT0eCaTed513fPhlYmd4CsBOiDQarVmu7q6KpZl/XLt2rVjQggvHo8PTE9PH242m1PpdPrRy5cvf3L79u2fo9GoyRi7U61W3wsDL5fv1V8VjLFF3/ct3/ctAADP86wgCBq+7zcAABljtud5FgCA7/uWLMvWai8KAIB/ACsf4Gh+DNwbAAAAAElFTkSuQmCC'
|
||||
|
||||
camera64 = 'data:image/png;base64,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'
|
||||
|
||||
checkmark64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAD2ElEQVRIibWVe4hUVRzHP+feuTOz82gfrqwu5gM0zTQpS1yfoGQPyc3IjOwlYRElPcDICEP6Q6i/IvojCCIpTA0tSlSqJSvbTLZE0dbWxz4nXWdnxrk7M/feOY/+sHyEa7pt57/z45zP53x/B84R/B/jzeNLgeXADDHE4IeBV2Ihpo6LK1rzNqEhAW9oGw18UhelYeWUGHFL0tx5lqPZyBAI3mi9o8YRm16cWTlsVFLQVwjY2+4SSI2W+j8KXj+ybmwytO69xjo7Wyqzr8sldbaE60ksIdBlhTVo+KuHXppY5azftKzeNsbQkurntOuTKQQobYhFbCgPNsGaA5NDWm94ZlYV7fmAX3pcenIlTucDAqlJRENUxcJQLgwiwfMtYcpq4503JMJjq8M0d+XpyBRJnfUpBpJwyKYqFqbCcSCQg0gQyCeq4qHp90yr5Pd0kY6+ImnXJ1CaeDhEdSJCTSJKzLEHLXhu4oQEuWKZ79uzZAoX2hKPhOn+I6DtuEdfLriC4NE9L4CYhzEP8dH84Hz9kT0NBHLqvMlJmo5nyBQDylITj4RwM5rmw70orcEA0AL8Q/DgN8OBr/DltL8q64G1F52+obomwr6US7boE0hNhRPiVIdHx7H+EvA2sJ0tC3/+e8uFS27c/SS+7ElGrGkbnp5EfV0UArmGxt0Lzq/x5YzKWocz/T4FXyGEINvj0XE410QgJ7Fl4dqL4ecS3PVlJYgdllKzx04ZxqolY8h4mkm315JPl+z+nP8Bd++4hZ2LM/hyuokLCr7Eti28TJnOA5ndGLOUnYtLl+u2YMHnJ4BxY2bWsWj2SA72eoBBG4PnBvy2qwvpq81gVjhJp1Q7q9axLIFVMqSaz3ytfLWEpsbLwgFs6pc1o/R9+e7+eK9joSMWvjR4gSLA4FSGKLS7UyirUmRkbJFTG0VI6N17+oR0/bl8d/+A8HMJAG7bPB7BTmGL8TVz64mMiKGNQSuN0hqvq59CS59Kzq2zo8MrcH/s1V6qMIf9y5uvBL8gALj54xpgG5aYH589klB9BdoYjDY0XJ9k9HURPj2aRZ/ycL/tfouDK17+N/ilAoAbP6wAsRGLB8INI7BGJUAYLGEhLAtLCApfnDymc95NtD4eDMC8ZNiXzNKfSdLbt5K8N6o68nNMwoHqKCAwlkVwKI06ln2MtpWtVwMHBnjspHyNQO1Xe7pRbTmUEchCGbk/laKsdl0tfGBB51OKQM0hUD/ppk7kkTTy11NQku/TuUpdi+DKn/7wdyuAHzDcii0Uykwg/ezJoRMAVL9TCWwFjpJdvfpa4AB/Akx4zQw8GDagAAAAAElFTkSuQmCC'
|
||||
|
||||
cookbook64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAIAAAB1KUohAAAACXBIWXMAAAsSAAALEgHS3X78AAADMUlEQVQ4jY1UQUgCWxS9782M5hQzle3NTRGEQdBipCKKECoEg4JaJAqCmyBsYauEaNOqjQtXLSKINuFiKMiwIMtF1iLK2kWRZLUoDRxo5s37ixfSt4+/u7xzz51z7r3nwc7Ojt1uBwCEEPwtWKXL5eIvLy+HhoYIIYIgCIJAKa0Do5RyHPfy8nJycnJ1dcUjhJxOZygUOj09zefzFoulDp5SihCKRqPLy8vJZBI4jgOAo6Mjj8cDABjj/6WdTqdDoRAAfJeyFn8MQohhGADAY4xFUSyVSpIkAYBpmgih+soRQmxm2GazbW5u7u7ujoyMKIrCmP+ePMdxv9nhSqXi8/lmZmb29vay2Syrs1gs8EM/QogQQgipBWOMOzs7397eWlpabDYbAMiyHAwGu7u7mQTWzu/3R6PRxsZG+HERvNVqjcVix8fHfX19Nzc3T09PHo+HUjo1NVUulx8fHwFgbW0tEolQSguFwtbWVpU/rlQqs7Ozc3NzqqrmcjmXy9Xe3m61WgcGBubn5wGgo6NjYWEBAEql0t3dHQBUx8ljjNva2orFYnNzM8/zBwcHFoslGo329/cXCgUA6OnpwRh/fHwsLS3lcjm2qm9wQ0NDPB7f398fHBx8eHjIZrOqqhaLRUmSwuFwPB53OBw+ny+dTn9+ftYujed5AEilUhMTE9U9saTX66WUJhKJmv0dHh4Gg0FgF4YxJoQwANNjGIaiKLFYbHp62ul0Li4umqb5H5crSVIymQwEAolEwu12s6SiKNfX15OTkwDgcDguLi4ikUgVUv0zCIJgs9lUVWWlrP3q6qrf72dfAaCrq2tjY0OW5RowTynVNM1qteq6XqW9srJiGAZCSNd1hNDt7W04HGZm+NeFiaKYTCa3t7fHx8fdbjez+9fXV7UR87Cu66Zp1oI1TQsEAl6vN51Os9smhCCEfpbWmMw0TZbBpmm+v7+3traWy2VKKdP825I/M7Isi6IIAFxTU9P6+nomk+nt7X19fX1+fsYY1/ez0+k8Pz+/v7/nMMblcnl4eDifz5+dnWmaVgfGolQq2e32sbGx7wcok8mMjo7C396wVCpFKSWE/ANWXYLwO0+V8wAAAABJRU5ErkJggg=='
|
||||
|
||||
download64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAAEl0lEQVRIia2WS2hdZRDHf/Odc+7NvbZpYky1Gk2rEbRa26pt8UWLitYHIqigCD426kIQxCfWhYu68U0RBBERXCq+WrXoorYIFWpbEY0lpk0NTWKtuUlzn+d834yLm5smqQspzurMcM785j+c+eYTTtUcESAzvhIAm/+azHacA4FodszAVNFTrUPm+Q6iecmUyJkEna5OiDCCXCtvJ2cnV+Ep+9R7/VIfQhmeryKeySywok+SSMMKMwqAihDXPIcPDDMURUQCgiPWjJCck6x87ZXXV3cXu3XTO5tkYOvAHbnIfZipTpghLdAMIEngi1cXvtlzwfrHpG0x5ismzsvo0E9D9z7z++M799s2EcSm67OUkAs5cpbzkkoMtPtAAzdXQ9zqjHkt1Ol5SHofx0KWYRUxrdiS3FlLtzz51wd7+v2OQl7qHnPtorUXS3ZxPRUKUT5x4mTDWu559LbCNS+9X9v025Duc4KoYdMAA7A4Mk92EMp/JFIZwR/rx9dL1teVdC2/Qe8yzQg+pS0JvLUzx3hjioPVQamGGlcu47KNq6qrPj+fsd+GeAEYA2SmRQiCNSJKP1Ad3IVaG0nnlWRxKqkkVlYxJxGZwhmFIo34U/fh0Hv4v6YYrY+ihYtkorDUNj+298GPvzv6ZRrkMzA/oyCXh9rEMOOHfiLfcx+5zhXkOnppswxEpJHVxdTjs0CycDHy9XcMlwc5a0E3EoTconOls/dyBsb6lYRLY4m/9T6blDgi8oHw3rPx83fesubl4oVPWFvXBUKoQzqB92Xitpite77n/k/epaN7AZO1CTIROtZ14fJC6ccS9ndGUhRLK0Eum1h2YGpH5eFfD47sjluzcFo+f+vp655F03alNhZhASMjloA1qtzedzab125kiw2QLhHaQ0zIFM2MztUdkBcqx1Lp+0o59NGRP49OVQs0Z3d6nEyMUMP8OGgVtAJaA19CagP4xn4e6DPuPhox1V9HTRFr/h9mRmWkwbJtGSsHK4xXq4cQGQDCDABM0ClEy6DlJiA9DLV90BgktirFzhrPXX0mT6Y9lAaqkAhRItRKGT3bjetTYd2aYM7JYcwm5wwaAP44hDyQYukokg5jliICZoFIoNjZ4Ol1HdhueOPgCLlFjt7twvo63HwztGuipml20lEBBlrGfBXzR5BsDGjOPBrAAkJKRKBwuuepNUXyP5/HN7tKXFGvcuMGY/3qhAO/NLCTJ7kFmIT0OPgjmAhiYKYIASFgGoCUyAILu+o8ckng0jSwsF1YuzxP0hYwm3tizwIIpKPQOIY4BXUYCiiYYWSIKYYHMoRAV1fKTddFxJKQOA/mmW9zFWRjoCmYw6R1lrcg2kxgAfCIeRxKMa+YBSw0Vc7fOScAZuAnMXWYE8yaIUFBDFSbS8sCgscsayZWD3jMAmhT7b8CnDPIeZw6RGTOLmwWFRALMA3BZvkamoBcwM3Zh7MA9Yb5I3v/YKoKTlr9sROKZVrlTGDWsylmkMTGxCQ4h0ObGaT1aRJzHsbtwJJmWSet0/9kIpB69gPbgersJA4oMm/pn6JlQI1/uWX87/YP06p9rkZQnAYAAAAASUVORK5CYII='
|
||||
|
||||
github64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAADAwMDQ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhyjGAAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADdUAAA3VAT3WWPEAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAABzSURBVChTbYxRFoAgDMPQ+98Z1zbIeJqPbU3RMRfDECqyGpjMg6ivT6NBbKTw5WySq0jKt/sHrXiJ8PwpAAVIgQGkwABSYAApMIAUGEAalFmK9UJ24dC1i7qdj6IO5F+xnxfLu0jS0c7kqxd3Dk+JY8/5AKFrLuM7mfCAAAAAAElFTkSuQmCC'
|
||||
|
||||
|
||||
run64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAszD0iAAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADdUAAA3VAT3WWPEAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAABqSURBVChTpY5JDsAwCMTy/09TMGvFpVF9aAZPRHpkcXC7OIodPg0uCjPq+MwCrWRGKkiIvLyTqzw3aqoI73eqUNAoXBXlg4zudxF+NONfPIVvbSZPgww5oW0Vz8T4Lgbt/xbjia+rahR5AEYEg4vdzh2JAAAAAElFTkSuQmCC'
|
||||
|
||||
storage64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAABwcHDQ0NDg4ODw8PFxcXGRkZGhoaGxsbHh4eIyMjJSUlJiYmJycnKCgoMTExMjIyNTU1NjY2Nzc3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAouNksgAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADdQAAA3UAe+RuhUAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAAC5SURBVChTfZLbDsMgDEPpbb3TDv7/W7PYuAztYUeqhO2QAGowkXIMIeYkaSU4QsNBi4GcyhNINpTglmq4GWSphvy/ldkuLXZ4HmAxy3NmFJaA4guKGCwsjClfV05+fWdhYBtFw+amB292aygW3M7fsPTwjmadZkCvHEtWaAYTViBqVwgTA3tJVnB6D/xhaimItDhjMBvlhtFsaIafnEtOaAY/twAw/eslK70CbX8obUvgJNw9Jv0+Zh8D4s5+VAm/LwAAAABJRU5ErkJggg=='
|
||||
|
||||
ShowMeTheButtons()
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import PySimpleGUI 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 second_window():
|
||||
|
||||
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)
|
||||
event, values = window.read()
|
||||
window.close()
|
||||
|
||||
def test_menus():
|
||||
|
||||
|
||||
sg.change_look_and_feel('LightGreen')
|
||||
sg.set_options(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))],
|
||||
]
|
||||
|
||||
window = sg.Window("Windows-like program",
|
||||
layout,
|
||||
default_element_size=(12, 1),
|
||||
auto_size_text=False,
|
||||
auto_size_buttons=False,
|
||||
default_button_element_size=(12, 1))
|
||||
|
||||
# ------ 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.popup_get_file('file to open', no_window=True)
|
||||
print(filename)
|
||||
elif event == 'Properties':
|
||||
second_window()
|
||||
|
||||
test_menus()
|
|
@ -1,46 +0,0 @@
|
|||
#!/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)
|
|
@ -1,98 +0,0 @@
|
|||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import PySimpleGUIQt 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.Layout(layout).Read(timeout=0)
|
||||
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()
|
|
@ -1,106 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import queue
|
||||
import threading
|
||||
import time
|
||||
|
||||
# This program has been tested on all flavors of PySimpleGUI and it works with no problems at all
|
||||
# To try something other than tkinter version, just comment out the first import and uncomment the one you want
|
||||
import PySimpleGUI as sg
|
||||
# import PySimpleGUIQt as sg
|
||||
# import PySimpleGUIWx as sg
|
||||
# import PySimpleGUIWeb as sg
|
||||
|
||||
"""
|
||||
DESIGN PATTERN - Multithreaded Long Tasks GUI
|
||||
Presents one method for running long-running operations in a PySimpleGUI environment.
|
||||
The PySimpleGUI code, and thus the underlying GUI framework, runs as the primary, main thread
|
||||
The "long work" is contained in the thread that is being started.
|
||||
|
||||
A queue.Queue is used by the threads to communicate with main GUI code
|
||||
The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined,
|
||||
a Window is created, and an event loop is executed.
|
||||
What's different is that within this otherwise normal PySimpleGUI Event Loop, there is a check for items
|
||||
in the Queue. If there are items found, process them by making GUI changes, and continue.
|
||||
|
||||
This design pattern works for all of the flavors of PySimpleGUI including the Web and also repl.it
|
||||
You'll find a repl.it version here: https://repl.it/@PySimpleGUI/Async-With-Queue-Communicationspy
|
||||
"""
|
||||
|
||||
|
||||
def long_operation_thread(seconds, gui_queue):
|
||||
"""
|
||||
A worker thread that communicates with the GUI through a queue
|
||||
This thread can block for as long as it wants and the GUI will not be affected
|
||||
:param seconds: (int) How long to sleep, the ultimate blocking call
|
||||
:param gui_queue: (queue.Queue) Queue to communicate back to GUI that task is completed
|
||||
:return:
|
||||
"""
|
||||
print('Starting thread - will sleep for {} seconds'.format(seconds))
|
||||
time.sleep(seconds) # sleep for a while
|
||||
gui_queue.put('** Done **') # put a message into queue for GUI
|
||||
|
||||
|
||||
###### ## ## ####
|
||||
## ## ## ## ##
|
||||
## ## ## ##
|
||||
## #### ## ## ##
|
||||
## ## ## ## ##
|
||||
## ## ## ## ##
|
||||
###### ####### ####
|
||||
|
||||
def the_gui():
|
||||
"""
|
||||
Starts and executes the GUI
|
||||
Reads data from a Queue and displays the data to the window
|
||||
Returns when the user exits / closes the window
|
||||
"""
|
||||
|
||||
gui_queue = queue.Queue() # queue used to communicate between the gui and the threads
|
||||
|
||||
layout = [[sg.Text('Long task to perform example')],
|
||||
[sg.Output(size=(70, 12))],
|
||||
[sg.Text('Number of seconds your task will take'),sg.Input(key='_SECONDS_', size=(5,1)), sg.Button('Do Long Task', bind_return_key=True)],
|
||||
[sg.Button('Click Me'), sg.Button('Exit')], ]
|
||||
|
||||
window = sg.Window('Multithreaded Window').Layout(layout)
|
||||
|
||||
# --------------------- EVENT LOOP ---------------------
|
||||
while True:
|
||||
event, values = window.Read(timeout=100) # wait for up to 100 ms for a GUI event
|
||||
if event is None or event == 'Exit':
|
||||
break
|
||||
elif event.startswith('Do'):
|
||||
try:
|
||||
seconds = int(values['_SECONDS_'])
|
||||
print('Starting thread to do long work....sending value of {} seconds'.format(seconds))
|
||||
threading.Thread(target=long_operation_thread, args=(seconds , gui_queue,), daemon=True).start()
|
||||
except Exception as e:
|
||||
print('Error starting work thread. Did you input a valid # of seconds? You entered: %s' % values['_SECONDS_'])
|
||||
elif event == 'Click Me':
|
||||
print('Your GUI is alive and well')
|
||||
# --------------- Check for incoming messages from threads ---------------
|
||||
try:
|
||||
message = gui_queue.get_nowait()
|
||||
except queue.Empty: # get_nowait() will get exception when Queue is empty
|
||||
message = None # break from the loop if no more messages are queued up
|
||||
|
||||
# if message received from queue, display the message in the Window
|
||||
if message:
|
||||
print('Got a message back from the thread: ', message)
|
||||
|
||||
# if user exits the window, then close the window and exit the GUI func
|
||||
window.Close()
|
||||
|
||||
|
||||
## ## ### #### ## ##
|
||||
### ### ## ## ## ### ##
|
||||
#### #### ## ## ## #### ##
|
||||
## ### ## ## ## ## ## ## ##
|
||||
## ## ######### ## ## ####
|
||||
## ## ## ## ## ## ###
|
||||
## ## ## ## #### ## ##
|
||||
|
||||
if __name__ == '__main__':
|
||||
the_gui()
|
||||
print('Exiting Program')
|
|
@ -1,119 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Rather than importing individual classes such as threading.Thread or queue.Queue, this
|
||||
# program is doing a simple import and then indicating the package name when the functions
|
||||
# are called. This seemed like a great way for the reader of the code to get an understanding
|
||||
# as to exactly which package is being used. It's purely for educational and explicitness purposes
|
||||
import queue
|
||||
import threading
|
||||
import time
|
||||
import itertools
|
||||
|
||||
# This program has been tested on all flavors of PySimpleGUI and it works with no problems at all
|
||||
# To try something other than tkinter version, just comment out the first import and uncomment the one you want
|
||||
import PySimpleGUI as sg
|
||||
# import PySimpleGUIQt as sg
|
||||
# import PySimpleGUIWx as sg
|
||||
# import PySimpleGUIWeb as sg
|
||||
|
||||
"""
|
||||
DESIGN PATTERN - Multithreaded GUI
|
||||
One method for running multiple threads in a PySimpleGUI environment.
|
||||
The PySimpleGUI code, and thus the underlying GUI framework, runs as the primary, main thread
|
||||
Other parts of the software are implemented as threads
|
||||
|
||||
A queue.Queue is used by the worker threads to communicate with code that calls PySimpleGUI directly.
|
||||
The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined,
|
||||
a Window is created, and an event loop is executed.
|
||||
What's different is that within this otherwise normal PySimpleGUI Event Loop, there is a check for items
|
||||
in the Queue. If there are items found, process them by making GUI changes, and continue.
|
||||
|
||||
This design pattern works for all of the flavors of PySimpleGUI including the Web and also repl.it
|
||||
You'll find a repl.it version here: https://repl.it/@PySimpleGUI/Async-With-Queue-Communicationspy
|
||||
"""
|
||||
|
||||
|
||||
######## ## ## ######## ######## ### ########
|
||||
## ## ## ## ## ## ## ## ## ##
|
||||
## ## ## ## ## ## ## ## ## ##
|
||||
## ######### ######## ###### ## ## ## ##
|
||||
## ## ## ## ## ## ######### ## ##
|
||||
## ## ## ## ## ## ## ## ## ##
|
||||
## ## ## ## ## ######## ## ## ########
|
||||
|
||||
def worker_thread(thread_name, run_freq, gui_queue):
|
||||
"""
|
||||
A worker thrread that communicates with the GUI
|
||||
These threads can call functions that block withouth affecting the GUI (a good thing)
|
||||
Note that this function is the code started as each thread. All threads are identical in this way
|
||||
:param thread_name: Text name used for displaying info
|
||||
:param run_freq: How often the thread should run in milliseconds
|
||||
:param gui_queue: Queue used to communicate with the GUI
|
||||
:return:
|
||||
"""
|
||||
print('Starting thread - {} that runs every {} ms'.format(thread_name, run_freq))
|
||||
for i in itertools.count(): # loop forever, keeping count in i as it loops
|
||||
time.sleep(run_freq/1000) # sleep for a while
|
||||
gui_queue.put('{} - {}'.format(thread_name, i)) # put a message into queue for GUI
|
||||
|
||||
###### ## ## ####
|
||||
## ## ## ## ##
|
||||
## ## ## ##
|
||||
## #### ## ## ##
|
||||
## ## ## ## ##
|
||||
## ## ## ## ##
|
||||
###### ####### ####
|
||||
|
||||
def the_gui(gui_queue):
|
||||
"""
|
||||
Starts and executes the GUI
|
||||
Reads data from a Queue and displays the data to the window
|
||||
Returns when the user exits / closes the window
|
||||
(that means it does NOT return until the user exits the window)
|
||||
:param gui_queue: Queue the GUI should read from
|
||||
:return:
|
||||
"""
|
||||
layout = [ [sg.Text('Multithreaded Window Example')],
|
||||
[sg.Text('', size=(15,1), key='_OUTPUT_')],
|
||||
[sg.Output(size=(40,6))],
|
||||
[sg.Button('Exit')],]
|
||||
|
||||
window = sg.Window('Multithreaded Window').Layout(layout)
|
||||
# --------------------- EVENT LOOP ---------------------
|
||||
while True:
|
||||
event, values = window.Read(timeout=100) # wait for up to 100 ms for a GUI event
|
||||
if event is None or event == 'Exit':
|
||||
break
|
||||
#--------------- Loop through all messages coming in from threads ---------------
|
||||
while True: # loop executes until runs out of messages in Queue
|
||||
try: # see if something has been posted to Queue
|
||||
message = gui_queue.get_nowait()
|
||||
except queue.Empty: # get_nowait() will get exception when Queue is empty
|
||||
break # break from the loop if no more messages are queued up
|
||||
# if message received from queue, display the message in the Window
|
||||
if message:
|
||||
window.Element('_OUTPUT_').Update(message)
|
||||
window.Refresh() # do a refresh because could be showing multiple messages before next Read
|
||||
print(message)
|
||||
# if user exits the window, then close the window and exit the GUI func
|
||||
window.Close()
|
||||
|
||||
|
||||
## ## ### #### ## ##
|
||||
### ### ## ## ## ### ##
|
||||
#### #### ## ## ## #### ##
|
||||
## ### ## ## ## ## ## ## ##
|
||||
## ## ######### ## ## ####
|
||||
## ## ## ## ## ## ###
|
||||
## ## ## ## #### ## ##
|
||||
|
||||
if __name__ == '__main__':
|
||||
#-- Create a Queue to communicate with GUI --
|
||||
gui_queue = queue.Queue() # queue used to communicate between the gui and the threads
|
||||
#-- Start worker threads, one runs twice as often as the other
|
||||
threading.Thread(target=worker_thread, args=('Thread 1', 500, gui_queue,), daemon=True).start()
|
||||
threading.Thread(target=worker_thread, args=('Thread 2', 200, gui_queue,), daemon=True).start()
|
||||
threading.Thread(target=worker_thread, args=('Thread 3', 1000, gui_queue,), daemon=True).start()
|
||||
#-- Start the GUI passing in the Queue --
|
||||
the_gui(gui_queue)
|
||||
print('Exiting Program')
|
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