Metadata property added to call ref. Checkin of the latest readme_creator files

This commit is contained in:
PySimpleGUI 2021-01-17 13:04:15 -05:00
parent faa701e3de
commit fcb3cc7bde
17 changed files with 19766 additions and 8351 deletions

View File

@ -160,6 +160,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -289,7 +299,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -491,6 +500,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -605,7 +624,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -756,6 +774,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -851,7 +879,6 @@ unhide_row()
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -1010,6 +1037,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -1120,7 +1157,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -1325,6 +1361,16 @@ Parameter Descriptions:
| List[List[Element]] | rows | The rows of Elements |
| (Column) | **RETURN** | Used for chaining
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -1438,7 +1484,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -1633,6 +1678,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -1747,7 +1802,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -1959,6 +2013,16 @@ Parameter Descriptions:
| List[List[Element]] | rows | The rows of Elements |
| (Frame) | **RETURN** | Used for chaining
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -2059,7 +2123,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -2615,6 +2678,16 @@ Parameter Descriptions:
|--|--|--|
| int | figure | value returned by tkinter when creating the figure / drawing |
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -2725,7 +2798,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -3184,6 +3256,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -3269,7 +3351,6 @@ unhide_row()
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -3400,6 +3481,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -3539,7 +3630,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -3742,6 +3832,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -3867,7 +3967,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -4082,6 +4181,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -4219,7 +4328,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -4421,6 +4529,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -4521,7 +4639,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -4767,6 +4884,16 @@ Restore a previously re-reouted stdout back to the original destination
restore_stdout()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -4900,7 +5027,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -5080,6 +5206,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -5199,7 +5335,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -5349,6 +5484,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -5473,7 +5618,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -5641,6 +5785,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -5740,7 +5894,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -5887,6 +6040,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -6008,7 +6171,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -6204,6 +6366,16 @@ Sets all Radio Buttons in the group to not selected
reset_group()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -6313,7 +6485,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -6504,6 +6675,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -6609,7 +6790,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -6782,6 +6962,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -6892,7 +7082,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -7070,6 +7259,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -7177,7 +7376,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -7373,6 +7571,16 @@ Create a tkinter event that mimics user clicking on a tab. Must have called wind
select()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -7476,7 +7684,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -7701,6 +7908,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -7800,7 +8017,6 @@ unhide_row()
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -8013,6 +8229,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -8122,7 +8348,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -8313,6 +8538,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -8420,7 +8655,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -8681,6 +8915,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -8804,7 +9048,6 @@ Parameter Descriptions:
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -9016,6 +9259,16 @@ Hide the entire row an Element is located on.
hide_row()
```
### metadata
#### property: metadata
Metadata is an Element property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### set_cursor
Sets the cursor for the current Element.
@ -9101,7 +9354,6 @@ unhide_row()
#### property: visible
Returns visibility state for the element. This is a READONLY property
To control visibility, use the element's update method
|Type|Name|Meaning|
|---|---|---|
@ -9815,6 +10067,16 @@ Maximize the window. This is done differently on a windows system versus a linux
maximize()
```
### metadata
#### property: metadata
Metadata is available for all windows. You can set to any value.
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### minimize
Minimize this window to the task bar
@ -10627,6 +10889,16 @@ Hides the icon
hide()
```
### metadata
#### property: metadata
Metadata is an SystemTray property that you can use at any time to hold any value
|Type|Name|Meaning|
|---|---|---|
|(Any)| **return** | the current metadata value |
### notify
Displays a "notification window", usually in the bottom right corner of your display. Has an icon, a title, and a message

View File

@ -0,0 +1,692 @@
import time
import subprocess,re,datetime,time,os,platform,json,PySimpleGUI as sg; from subprocess import Popen; from make_real_readme import main
# mkdir
import os
cd = CD = os.path.dirname(os.path.abspath(__file__))
dir_name = os.path.join(cd, 'output')
if not os.path.exists(dir_name): os.mkdir(dir_name)
else: print(f'Такая папка уже есть: "{dir_name}"')
sg.theme('Dark2')
cd = os.path.dirname(os.path.abspath(__file__))
def readfile(filename):
with open(filename, 'r', encoding='utf-8') as ff: return ff.read()
def writefile(fpath, content):
with open(fpath, 'w', encoding='utf-8') as ff: ff.write(content)
def writejson(a_path:str, a_dict:dict) -> None:
with open(a_path, 'w', encoding='utf-8') as output_file: json.dump(a_dict, output_file, ensure_ascii=False, indent=2)
def readjson(a_path:str) -> dict:
with open(a_path, 'r', encoding='utf-8') as f: return json.load(f)
def openfile(a_path):
# File exists?
if not os.path.exists(a_path): return sg.Popup(f"Error! This file doesn't exists: {a_path}")
# check: OS
if 'Windows' in platform.system():
os.startfile(a_path)
elif 'Linux' in platform.system():
Popen(f'exo-open "{a_path}"', shell=True)
def opendir(a_path):
# Folder exists?
if not os.path.exists(a_path): return sg.Popup(f"Error! This directory doesn't exists: {a_path}")
try:
# check: OS
if 'Windows' in platform.system():
os.startfile(a_path)
elif 'Linux' in platform.system():
Popen(f'exo-open --launch FileManager --working-directory "{a_path}"', shell=True)
except Exception as e:
sg.Popen(f"Error, can't open a file: '{e}'")
########################################################################
# __ _ _ #
# / _(_) | | #
# __ __ ___ ___ _ __ | |_ _ __ _ | |__ ___ _ __ ___ #
# \ \ / / / __/ _ \| '_ \| _| |/ _` | | '_ \ / _ \ '__/ _ \ #
# \ V / | (_| (_) | | | | | | | (_| | | | | | __/ | | __/ #
# \_/ \___\___/|_| |_|_| |_|\__, | |_| |_|\___|_| \___| #
# __/ | #
# |___/ #
########################################################################
def load_configs(): return readjson(os.path.join(cd, 'app_configs.json'))
def save_configs(a_config:dict): writejson(os.path.join(cd, 'app_configs.json'), a_config)
APP_CONFIGS = load_configs()
README_OFILENAME = APP_CONFIGS['README_OFILE']
CALL_REFERENCE_OFILENAME = APP_CONFIGS['CALL_REF_OFILE']
##-#-#-# ##-#-#-#
# Post-process logic
##-#-#-# ##-#-#-#
insert_md_section_for__class_methods = False
remove_repeated_sections_classmethods = False
import time
def timeit(f):
def wrapper(*args, **kwargs):
start = time.time()
res = f(*args, **kwargs)
end = time.time()
# print('\nНачало в : ', start)
# print('\n ({}) Начало в : '.format(f.__name__, start))
# print('Окончено в : ', end)
# print('Длительность: ', end - start)
# print('')
return res
return wrapper
class BESTLOG(object):
def __init__(self, filename):
# my_file = logging.FileHandler(filename, mode='w')
# my_file.setLevel(logging.DEBUG)
# my_file.setFormatter(logging.Formatter('%(asctime)s>%(levelname)s: %(message)s'))
# logger = logging.getLogger(__name__)
# logger.setLevel(logging.DEBUG)
# logger.addHandler(my_file)
self.filename = filename
self.json_name = filename + '.json'
self.error_list = []
self.warning_list = []
self.info_list = []
self.debug_list = []
self.tick_amount=1
self.names = self.messages_names = 'error warning info debug'.split(' ')
def tick(self):
self.tick_amount+=1
return self.tick_amount
#######################################################################
# __ _ _ _ #
# / _| | | (_) | #
# | |_ ___ _ __ | |_ _ __ __ _ _ __ ___ _ __ _| | ___ _ __ #
# | _/ _ \| '__| | __| '__/ _` | '_ \/ __| '_ \| | |/ _ \ '__| #
# | || (_) | | | |_| | | (_| | | | \__ \ |_) | | | __/ | #
# |_| \___/|_| \__|_| \__,_|_| |_|___/ .__/|_|_|\___|_| #
# | | #
# |_| #
#######################################################################
def error(self, m, metadata={}):
self.error_list.append([self.tick(), m, metadata])
def warning(self, m, metadata={}):
self.warning_list.append([self.tick(), m, metadata])
def info(self, m, metadata={}):
self.info_list.append([self.tick(), m, metadata])
def debug(self, m, metadata={}):
self.debug_list.append([self.tick(), m, metadata])
##########################################
# __ #
# / _| #
# | |_ ___ _ __ _ __ ___ ___ #
# | _/ _ \| '__| | '_ ` _ \ / _ \ #
# | || (_) | | | | | | | | __/ #
# |_| \___/|_| |_| |_| |_|\___| #
# #
# #
##########################################
def tolist(self): return zip([self.error_list, self.warning_list, self.info_list, self.debug_list], self.names)
def todict(self): return {'error' : self.error_list, 'warning' : self.warning_list, 'info' : self.info_list, 'debug' : self.debug_list}
@timeit
def save(self):
'''
{
'message_type' : message_type,
'message_text' : m_text,
'message_time' : m_time,
'message_metadata' : m_metadata
}
'''
all_messages_list = []
for messages, message_type in self.tolist():
results_ = [{'message_type' : message_type,
'message_text' : m_text,
'message_time' : m_time,
'message_metadata' : m_metadata}
for m_time, m_text, m_metadata in messages]
all_messages_list.extend(results_)
# sort messages on time
all_messages_list = sorted(all_messages_list,
key=lambda x: x['message_time'])
# convert time
# for i in all_messages_list: i['message_time'] = i['message_time'].strftime('%Y-%m-%d %H:%M:%S.%f')
writejson(self.json_name, all_messages_list)
@timeit
def load(self, **kw):
'''
return dict with messages
kw = {
use_psg_color : bool
show_time : bool
}
'''
# plan:
# read json, convert time
# read
all_messages_list = readjson(self.json_name)
# convert time
# for i in all_messages_list: i['message_time'] = datetime.datetime.strptime(i['message_time'], '%Y-%m-%d %H:%M:%S.%f')
def format_message(message):
if kw['show_time']:
return str(message['message_time']) + ':' + message['message_text']
else:
return message['message_text']
#=========#
# 4 lists #
#=========#
error_list = [i for i in all_messages_list if i['message_type'] == 'error']
warning_list = [i for i in all_messages_list if i['message_type'] == 'warning']
info_list = [i for i in all_messages_list if i['message_type'] == 'info']
debug_list = [i for i in all_messages_list if i['message_type'] == 'debug']
#=================#
# and 1 more list #
#=================#
# colors = {'warning' : 'magenta', 'info' : 'black'}
colors = {'warning' : 'blue', 'info' : 'black'}
warning_info_ = []
for message in sorted(warning_list + info_list, key=lambda x: x['message_time']):
if kw['use_psg_color']:
warning_info_.append([ format_message(message),
colors.get(message['message_type']) ])
else:
warning_info_.append(format_message(message))
error_list = [format_message(i) for i in error_list]
warning_list = [format_message(i) for i in warning_list]
info_list = [format_message(i) for i in info_list]
debug_list = [format_message(i) for i in debug_list]
return error_list, warning_list, info_list, debug_list, warning_info_
@timeit
def load_to_listbox(self):
'''
read .json
'''
return sorted(readjson(self.json_name),
key=lambda x: x['message_time'])
@timeit
def compile_call_ref(output_filename='LoG_call_ref', **kw):
''' Compile a "5_call_reference.md" file'''
log_obj = BESTLOG(os.path.join(cd, output_filename))
main(logger=log_obj,
main_md_file='markdown input files/5_call_reference.md',
insert_md_section_for__class_methods=insert_md_section_for__class_methods,
remove_repeated_sections_classmethods=remove_repeated_sections_classmethods,
files_to_include=[],
output_name=CALL_REFERENCE_OFILENAME,
delete_html_comments=True)
log_obj.save()
return log_obj.load(**kw), log_obj.load_to_listbox()
@timeit
def compile_readme(output_filename='LoG', **kw):
''' Compile a "2_readme.md" file'''
log_obj = BESTLOG(os.path.join(cd, output_filename))
main(logger=log_obj,
insert_md_section_for__class_methods=insert_md_section_for__class_methods,
remove_repeated_sections_classmethods=remove_repeated_sections_classmethods,
files_to_include=[0, 1, 2, 3],
output_name=README_OFILENAME,
delete_html_comments=True)
log_obj.save()
return log_obj.load(**kw), log_obj.load_to_listbox()
def compile_all_stuff(**kw):
'''
Compile a "2_ and 5_" .md filess
return output from them
'''
return compile_readme(**kw), compile_call_ref(**kw)
########################################
# _____ #
# | __ \ #
# | |__) |__ _ __ _ _ _ __ #
# | ___/ _ \| '_ \| | | | '_ \ #
# | | | (_) | |_) | |_| | |_) | #
# |_| \___/| .__/ \__,_| .__/ #
# | | | | #
# |_| |_| #
########################################
def md2psg(target_text):
r'''
ib<space>color
i italic
b bold
color = can be word can be color
red #ff00111
green
blue
i?b?\s?\w+?
usage
*i*a** italic
*b*a** bold
*ib*a** italic bold
*ib red*a** italic bold red
*b green*a** bold green
'This was *I*special** message from *B*him**. And from *Igreen*this** to *Ired*this**'
'''
# format
# ======
font_norm = ('Mono 12 ') # (*sg.DEFAULT_FONT, 'italic')
font_bold = ('Mono 12 italic') # (*sg.DEFAULT_FONT, 'italic')
font_italic = ('Mono 12 bold') # (*sg.DEFAULT_FONT, 'bold')
list_of_Ts = []
parts = [i for i in re.compile(r'(\*I?B?[a-z]*?\*[\d\D]*?\*\*)', flags=re.M|re.DOTALL).split(target_text) if i is not None]
for index, text in enumerate(parts):
if index % 2 == 0:
# Normal text
T_text = text
T = sg.T(T_text, size=(len(T_text), 1), pad=(0,0), font=font_norm)
else:
# SPECIAL format
T_parameters = {
'font': font_norm
}
my_format = text[1:].split('*')[0]
# ::: italic
if 'I' in my_format: T_parameters['font'] = font_italic
# ::: bold
if 'B' in my_format: T_parameters['font'] = font_bold
# ::: colors
color_left = my_format.replace('I', '').replace('B', '')
if color_left: T_parameters['text_color'] = color_left
# making psg element
T_text = '*'.join(text.split('*')[2:-2])
T = sg.T(T_text, size=(len(T_text), 1), pad=(0,0), **T_parameters)
list_of_Ts.append(T)
return list_of_Ts
def mini_GUI():
my_font = ("Helvetica", 12)
my_font2 = ("Helvetica", 12, "bold")
my_font3 = ("Helvetica", 15, "bold")
my_font4 = ("Mono", 18, "bold")
def make_tab(word):
def tabs(*layouts):
return sg.TabGroup(
[[ sg.Tab(title, lay, key=f'-tab-{word_}-{index}-')
for index, (title, word_, lay) in enumerate(layouts)
]]
)
return [[
sg.Column(layout=[
[sg.T('debug', font=my_font, text_color='grey')],
[sg.ML(size=(50-15, 15), key=f'-{word}-debug-')],
[sg.T('error', font=my_font, text_color='red')],
[sg.ML(size=(50-15, 15), key=f'-{word}-error-')],
], pad=(0, 0)),
sg.T(' '),
sg.Column(layout=[
[sg.T('warning', font=my_font2)],
[sg.ML(size=(70-12, 15), key=f'-{word}-warning-')],
[sg.T('info', font=my_font2)],
[sg.ML(size=(70-12, 15), key=f'-{word}-info-')],
], pad=(0, 0)),
tabs(
('Text', word, [
[sg.T('warning info', font=my_font3)]
,[sg.ML(size=(110, 30), key=f'-{word}-warning_info-')]
]),
('Listbox', word, [
[sg.T('warning info listbox', font=my_font3)]
,[sg.Listbox([], size=(110, 30-1), key=f'-{word}-listbox-', enable_events=True, background_color='#ffccaa')]
])
)
]]
settings_layout = [
[sg.CB('Toggle progressbar', False, enable_events=True, key='toggle_progressbar')],
[
sg.Frame('Text editor', [[ sg.Combo(['pycharm', 'subl'], default_value='subl', enable_events=True, key='_text_editor_combo_') ]] ),
sg.Frame('Pycharm path:', [[ sg.I('', size=(40, 1), enable_events=True, key='_PyCharm_path_') ]] )
],
[
sg.Frame('⅀∉ Filter "empty tables"', [
[sg.T('''This is for filtering stirng, like:''')],
[sg.T('''Warning ======= We got empty md_table for "EasyPrintClose"''', font='Mono 8')],
[sg.CB('enable', True, key='checkbox_enable_empty_tables_filter', enable_events=True)],
[sg.ML('PrintClose\nEasyPrintClose\nmain\ntheme\nRead',
size=(30,10), enable_events=True, key='_filter_empty_tables_ml_')]]),
sg.Frame('⅀∉ Filter "tkinter class methods"', [
[sg.T('''This is for filtering stirng, like:''')],
[sg.T('''Please, fix ':return:' in 'SetFocus' IF you want to see 'return' row in 'signature table' ''', font='Mono 8')],
[sg.CB('enable', True, enable_events=True, key='checkbox_enable_filter_tkinter_class_methods')],
[sg.ML('SetFocus\nSetTooltip\nUpdate\n__init__\nbind\nexpand\nset_cursor\nset_size',
size=(30,10), enable_events=True, key='_filter_tkinter_class_methods_')]], visible=not True)
]
]
layout = [[sg.TabGroup([[
sg.Tab('readme logs', make_tab('README')),
sg.Tab('Call reference logs', make_tab('CALL_REF')),
sg.Tab('General settings', settings_layout)
]])]]
# ░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░
# ░▒▒▓▓▓ progress bar ▓▓▓▒▒░
# ░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░
from time import sleep; from math import pi, sin; from itertools import count
def next_star():
middle = 100/2
for i in (int(sin(i*pi/middle)*middle + middle) for i in count()): yield i
psg_module_path = str(sg).split("' from '")[1][:-2]
star_bar = sg.Col([
[sg.ProgressBar(max_value=100, orientation='h',
key='_star_bar1_', size=(50,5), bar_color=('blue', 'yellow'))],
[sg.ProgressBar(max_value=100, orientation='h',
key='_star_bar2_', size=(50,5), bar_color=('yellow', 'blue'))],
])
# guia
def empty_line(fontsize=12): return [sg.T('', font=('Mono '+str(fontsize)))]
window = sg.Window('We are live! Again! --- ' + 'Completed making {}, {}'.format(os.path.basename(README_OFILENAME), os.path.basename(CALL_REFERENCE_OFILENAME)), [
[sg.T(size=(30,1), key='-compile-time-'), star_bar],
empty_line(),
[*md2psg(f'The *Bmagenta*PySimpleGUI** module being processed is *Imagenta*"{psg_module_path}"**'), sg.B('< open (__init__.py)', key='open_init_file'), sg.B('< open (psg.py)', key='open_psg_file')],
# [sg.T(f'The **PySimpleGUI** module being processed is *"{psg_module_path}"*')],
empty_line(),
[
sg.B('Run again (F1)', key='-run-')
,sg.Col([
[sg.CB('show time in logs (F2)', False, enable_events=True, key='show_time')],
[sg.CB('Logs with Color (F3)', True, enable_events=True, key='use_psg_color')],
])
,sg.Col([
empty_line(5),
[sg.B('open "db folder"', key='-open_db_folder-')],
])
,sg.Frame('', [[
sg.Col([
[*md2psg('markdown outputFileName *I*FOR** *B*readme **: ')
,sg.I(README_OFILENAME, key='README_OFILE', size=(25, 1))
,sg.B('open in explorer', key='open in explorer_readme')
,sg.B('open in text editor', key='open file - readme')
]
,[*md2psg('markdown outputFileName *I*FOR** *B*call ref**: ')
,sg.I(CALL_REFERENCE_OFILENAME, key='CALL_REF_OFILE', size=(25, 1))
,sg.B('open in explorer', key='open in explorer_calref')
,sg.B('open in text editor', key='open file - calref')
]
])
]], relief=sg.RELIEF_SUNKEN, border_width=4)
]
,*layout
], resizable=True, finalize=True, location=(0,0), return_keyboard_events = True)
def update_time_in_GUI():
window['-compile-time-'](datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S.%f'))
def update_compilation_in_psg(values):
#
# ░▒▒▓▓▓▓▓◘ compile ◘▓▓▓▓▓▒▒░
#
result_readme__for_txt_n_listbox, result_call_ref__for_txt_n_listbox = compile_all_stuff(
use_psg_color=values['use_psg_color'],
show_time=values['show_time'])
result_readme_txt, result_readme_listbox_items = result_readme__for_txt_n_listbox
result_call_ref_txt, result_call_ref_listbox_items = result_call_ref__for_txt_n_listbox
#
# ░▒▒▓▓▓▓▓◘ define FILTER functions ◘▓▓▓▓▓▒▒░
#
badNames = [ i.strip() for i in values['_filter_tkinter_class_methods_'].split('\n') if i.strip()]
badNames = '|'.join(badNames)
regex_str1 = rf"fix .:return:. in .({badNames})."
badNames = [ i for i in values['_filter_empty_tables_ml_'].split('\n') if i.strip()]
badNames = '|'.join(badNames)
regex_str2 = rf'empty md_table for .({badNames}).'
def is_valid_regex_LogMessage(msg: str):
nonlocal regex_str1, regex_str2
# test 1 - filter tkinter class methods
error1_found = False
if values['checkbox_enable_filter_tkinter_class_methods'] and ':return:' in msg:
error1_found = bool(re.search(regex_str1, msg, flags=re.M|re.DOTALL))
# test 2 - filter "special empty tables"
error2_found = False
if values['checkbox_enable_empty_tables_filter'] and 'empty md_table for' in msg:
error2_found = bool(re.search(regex_str2, msg, flags=re.M|re.DOTALL))
return not error1_found and not error2_found
def filter_log_messages(messages):
if type(messages) is str:
return '\n'.join([msg for msg in messages.split('\n') if is_valid_regex_LogMessage(msg)])
raise TypeError
#
# ▓▓▓ Update GUI ▓▓▓
#
# =========== listbox's
class ParsingError(object):
def __init__(self, log_obj):
self.log_obj = log_obj
self.text = log_obj['message_text']
def __str__(self): return self.__repr__()
def __repr__(self):
'''qwe'''
# {
# 'message_type': 'info',
# 'message_text': 'STARTING',
# 'message_time': 2,
# 'message_metadata': {}
# }
text = self.log_obj['message_text']
metadata = self.log_obj['message_metadata']
lineno = ''
if 'lineno' in metadata.keys(): lineno = "(line:" + str(metadata['lineno']) + ') '
return f'{lineno} {text}'
items1 = [i for i in result_readme_listbox_items if is_valid_regex_LogMessage(i['message_text']) ]
items2 = [i for i in result_call_ref_listbox_items if is_valid_regex_LogMessage(i['message_text']) ]
window['-README-listbox-']([ ParsingError(i) for i in items1])
window['-CALL_REF-listbox-']([ ParsingError(i) for i in items2])
# =========== multitext's
def set_it(prefix = 'CALL_REF', messages_obj = result_call_ref_txt):
t_error, t_warning, t_info, t_debug = ['\n'.join(i) for i in messages_obj[:4]]
t_error = filter_log_messages(t_error)
t_warning = filter_log_messages(t_warning)
t_info = filter_log_messages(t_info)
t_debug = filter_log_messages(t_debug)
window[f'-{prefix}-error-'](t_error)
window[f'-{prefix}-warning-'](t_warning)
window[f'-{prefix}-info-'](t_info)
window[f'-{prefix}-debug-'](t_debug)
# /// colors warning_info
window[f'-{prefix}-warning_info-'].update('')
t_warning_info_obj = messages_obj[-1]
if values['use_psg_color']:
for text, color in t_warning_info_obj:
if not is_valid_regex_LogMessage(text): continue
window[f'-{prefix}-warning_info-'].print(text, text_color=color)
else:
window[f'-{prefix}-warning_info-'](t_warning_info_obj)
# two calls
set_it('README', result_readme_txt)
set_it('CALL_REF', result_call_ref_txt)
# ~~~~~~~~~~~~
# GUI updating
# ~~~~~~~~~~~~
update_time_in_GUI()
values = window.read(timeout=0)[1]
update_compilation_in_psg(values)
p_values = values
window['_PyCharm_path_'](APP_CONFIGS['_PyCharm_path_'])
window['_text_editor_combo_'].update(set_to_index=APP_CONFIGS['_text_editor_combo_']) # index
window['toggle_progressbar'](APP_CONFIGS['toggle_progressbar'])
window['checkbox_enable_empty_tables_filter'](APP_CONFIGS['checkbox_enable_empty_tables_filter'])
window['_filter_empty_tables_ml_'](APP_CONFIGS['_filter_empty_tables_ml_'])
window['checkbox_enable_filter_tkinter_class_methods'](APP_CONFIGS['checkbox_enable_filter_tkinter_class_methods'])
window['_filter_tkinter_class_methods_'](APP_CONFIGS['_filter_tkinter_class_methods_'])
window['show_time'](APP_CONFIGS['show_time'])
window['use_psg_color'](APP_CONFIGS['use_psg_color'])
window['README_OFILE'](APP_CONFIGS['README_OFILE'])
window['CALL_REF_OFILE'](APP_CONFIGS['CALL_REF_OFILE'])
next_val_gen = next_star()
my_timeout = None
while True:
event, values = window(timeout=my_timeout)
if event in ('Exit', None):
# save to disk
# APP_CONFIGS['_PyCharm_path_'] = p_values['_PyCharm_path_']
APP_CONFIGS['_text_editor_combo_'] = 1 if window['_text_editor_combo_'].get() == 'subl' else 0
APP_CONFIGS['toggle_progressbar'] = p_values['toggle_progressbar']
APP_CONFIGS['checkbox_enable_empty_tables_filter'] = p_values['checkbox_enable_empty_tables_filter']
APP_CONFIGS['_filter_empty_tables_ml_'] = p_values['_filter_empty_tables_ml_']
APP_CONFIGS['checkbox_enable_filter_tkinter_class_methods'] = p_values['checkbox_enable_filter_tkinter_class_methods']
APP_CONFIGS['_filter_tkinter_class_methods_'] = p_values['_filter_tkinter_class_methods_']
APP_CONFIGS['show_time'] = p_values['show_time']
APP_CONFIGS['use_psg_color'] = p_values['use_psg_color']
APP_CONFIGS['README_OFILE'] = p_values['README_OFILE']
APP_CONFIGS['CALL_REF_OFILE'] = p_values['CALL_REF_OFILE']
save_configs(APP_CONFIGS)
break
p_values = values
if '__TIMEOUT__' in event:
if values['toggle_progressbar']:
window['_star_bar1_'].UpdateBar(next(next_val_gen))
window['_star_bar2_'].UpdateBar(next(next_val_gen))
if '__TIMEOUT__' not in event:
print('PSG event>', event)
if event == 'toggle_progressbar':
my_timeout = None if not values['toggle_progressbar'] else 100
if event == '-README-listbox-':
metadata = values['-README-listbox-'][0].log_obj['message_metadata']
print(f'metadata = {metadata}')
if event == '-CALL_REF-listbox-':
ParsingError_obj = values['-CALL_REF-listbox-'][0]
metadata = ParsingError_obj.log_obj['message_metadata']
if 'lineno' in metadata.keys():
lineno = metadata['lineno']
texteditor = values['_text_editor_combo_']
psg_module_path_SDK = psg_module_path.replace('__init__.py', 'PySimpleGUI.py')
if 'pycharm' == texteditor:
texteditor = values['_PyCharm_path_']
subprocess.Popen(f'"{texteditor}" --line {lineno} "{psg_module_path_SDK}"', shell=True)
elif 'subl' == texteditor:
subprocess.Popen(f'{texteditor} "{psg_module_path_SDK}:{lineno}"', shell=True)
# if event == '-CALL_REF-listbox-':
# res = values['-CALL_REF-listbox-'][0]
# print(f'res = {res}')
if event == '-run-' or 'F1' in event: update_compilation_in_psg(values)
# folder
if event == '-open_db_folder-': opendir(cd)
# folder
if event == 'open in explorer_readme': opendir(os.path.dirname(os.path.join(cd, values['README_OFILE'])))
if event == 'open in explorer_calref': opendir(os.path.dirname(os.path.join(cd, values['CALL_REF_OFILE'])))
# file
if event == 'open file - readme': openfile(os.path.join(cd, values['README_OFILE']))
if event == 'open file - calref': openfile(os.path.join(cd, values['CALL_REF_OFILE']))
# file
if event == 'open_init_file': openfile(psg_module_path)
if event == 'open_psg_file': openfile(psg_module_path.replace('__init__.py', 'PySimpleGUI.py'))
# hotkeys
if 'F2' in event: window['show_time'](not values['show_time'])
if 'F3' in event: window['use_psg_color'](not values['use_psg_color'])
window.close()
if __name__ == '__main__':
mini_GUI()

View File

@ -0,0 +1,6 @@
readfile = lambda fpath: open(fpath, 'r', encoding='utf-8').read()
writefile = lambda fpath, x: open(fpath, 'w', encoding='utf-8').write(x)
from collections import Counter
asd = Counter(readfile('LoG_call_ref.json').split('\n'))
import pdb; pdb.set_trace();

View File

@ -0,0 +1,61 @@
import PySimpleGUIQt as sg
print(sg)
dicta1 = {
"a": "hellgdfgo world",
4: 5,
"qwerty" : "ytjyhrewq"
}
dicta2 = {
"a": "helldasdo world",
4: 5,
"qwerty" : "ytrewq"
}
dicta3 = {
"a": "hello world",
4: 5,
"qwerty" : "ytwqddqwrewq"
}
class ParsingError(object):
def __init__(self, psg_object, num):
self.num = num
self.psg_object = psg_object
def __str__(self):
return self.__repr__()
def __repr__(self):
return f'{self.num} {self.psg_object}'
@staticmethod
def headers():
return 'num,psg_object'.split(',')
items = [
ParsingError(dicta1, 45),
ParsingError(dicta2, 42),
ParsingError(dicta3, 12),
]
window = sg.Window('Test', [
[sg.Listbox(items, key='qwe', enable_events=True)],
[sg.B('q1'), sg.B('q2'), sg.B('q3')],
],return_keyboard_events=True)
while True:
event, values = window()
if event in ('Exit', None): break
print(event, values)
if event == 'q1':
gui = values['qwe'][0]
print(gui.num)
print(gui.psg_object[4])
window.close()

View File

@ -0,0 +1,60 @@
import inspect
import PySimpleGUI
"""
Create All Possible Tags
Will output to STDOUT all of the different tags for classes, members and functions for a given PySimpleGUI.py
file. Functions that begin with _ are filtered out from the list.
Displays the results in a PySimpleGUI window which can be used to copy and paste into other places.
"""
def new_name(name):
name = name.replace("OK", "*1")
name = name.replace("TK", "*2")
name = name.replace("RGB", "*3")
new = name[0].lower()
for c in name[1:]:
new += '_' + c.lower() if (c.isupper() or c == "*") else c
new=new.replace("*1", "ok")
new = new.replace("*2", "tk")
new = new.replace("*3", "rgb")
return new
layout = [[PySimpleGUI.Output(size=(600,300))]]
window = PySimpleGUI.Window('Dump of tags', layout, resizable=True).Finalize()
psg_members = inspect.getmembers(PySimpleGUI)
psg_funcs = [o for o in psg_members if inspect.isfunction(o[1])]
psg_classes = [o for o in psg_members if inspect.isclass(o[1])]
# I don't know how this magic filtering works, I just know it works. "Private" stuff (begins with _) are somehow
# excluded from the list with the following 2 lines of code. Very nicely done Kol-ee-ya!
psg_classes_ = list(set([i[1] for i in psg_classes])) # filtering of anything that starts with _ (methods, classes, etc)
psg_classes = list(zip([i.__name__ for i in psg_classes_], psg_classes_))
for pclass in sorted(psg_classes):
if 'Tk' in pclass[0] or 'TK' in pclass[0] or 'Element' == pclass[0]: # or 'Window' == i[0]:
continue
# print(f'### {pclass[0]} Element')
# print('')
# print(f'<!-- <+{pclass[0]}.doc+> -->')
# print(f'<!-- <+{pclass[0]}.__init__+> -->')
print('')
print(f'{pclass[0]} methods in PEP8 format --------------------------------------')
for funcs in inspect.getmembers(pclass[1]):
if '_' not in funcs[0]:
# print(f'{pclass[0]}.{new_name(funcs[0])} = {pclass[0]}.{funcs[0]}') # version that has class on front
print(f'{new_name(funcs[0])} = {funcs[0]}') # version without class on front (use for most)
# print('\n'.join([f"#### {j[0]}\n\n<!-- <+{pclass[0]}.{j[0]}+> -->\n" for j in inspect.getmembers(pclass[1]) if '_' not in j[0]]))
# print('\n------------------------- Functions start here -------------------------\n')
#
for f in psg_funcs:
if f[0][0] == '_':
continue
print(f'{new_name(f[0])} = {f[0]}')
# print(f"<!-- <+func.{f[0]}+> -->")
window.Read()

View File

@ -0,0 +1,83 @@
import make_real_readme as mk_readme
import PySimpleGUI as sg
import logging, os
enable_logs = False
def readfile(filename):
with open(filename, 'r', encoding='utf-8') as ff:
return ff.read()
def writefile(fpath, content):
with open(fpath, 'w', encoding='utf-8') as ff:
ff.write(content)
window = sg.Window('Test', [
[sg.CB('include all .md files', True, key='all-checkbox', enable_events=True)],
[sg.CB('1', True, key='file1', disabled=True, enable_events=True)],
[sg.CB('2', False,key='file2', disabled=True, enable_events=True)],
[sg.CB('3', False,key='file3', disabled=True, enable_events=True)],
[sg.CB('4', False,key='file4', disabled=True, enable_events=True)],
[sg.T('well, this is you output name:'), sg.I('readme.md', key='output_name'), sg.B('aaaand, hope for the best... Compile.', key='comp')],
[sg.T('-- -- -- -- -- -- --\nlogs\n-- -- -- -- -- -- --', justification='center')],
# [sg.ML('', key='logs', size=(120,25))]
[sg.Column([
[sg.T('COMPLETE')],
[sg.Listbox([], size=(40, 10), key='COMPLETE')]
]), sg.Column([
[sg.T('NOTCOMPLETE')],
[sg.Listbox([], size=(40, 10), key='NOTCOMPLETE')]
])],
],element_justification='center')
while True:
event, values = window()
if event in ('Exit', None): break
print(event, values)
if event == 'all-checkbox':
window['file1'](disabled=values['all-checkbox'])
window['file2'](disabled=values['all-checkbox'])
window['file3'](disabled=values['all-checkbox'])
window['file4'](disabled=values['all-checkbox'])
if event == 'comp':
if enable_logs: window['logs'](values['logs'] + 'start')
# MAIN WORK - START
# 1### logging module
logger = logging.getLogger(__name__); logger.setLevel(logging.DEBUG); a_log_file = logging.FileHandler('_logs.txt', mode='w'); a_log_file.setLevel(logging.DEBUG); formatter = logging.Formatter('%(asctime)s>%(levelname)s: %(message)s'); a_log_file.setFormatter(formatter); logger.addHandler(a_log_file);
# 2### files to compile
files = [0, 1, 2, 3] if values['all-checkbox'] else []
if not values['all-checkbox']:
if values['file1']: files.append(1)
if values['file2']: files.append(2)
if values['file3']: files.append(3)
if values['file4']: files.append(4)
# 3### REAL work:
mk_readme.main(logger=logger,
files_to_include=files,
output_name=values['output_name'],
delete_html_comments=True)
# MAIN WORK - END
for i in readfile('_logs.txt').split('\n'):
if i.endswith('--> - COMPLETE'):
window['COMPLETE'](values['COMPLETE'] + [i])
else:
window['NOTCOMPLETE'](values['COMPLETE'] + [i])
if enable_logs: window['logs'](values['logs'] + output_readme)
window.close()

View File

@ -1,11 +1,9 @@
import inspect
from inspect import getmembers, isfunction, isclass, getsource, signature, _empty, isdatadescriptor
from datetime import datetime
import click, textwrap, logging, json, re, os
import PySimpleGUI, click, textwrap, logging, json, re, os
import os
cd = CD = os.path.dirname(os.path.abspath(__file__))
import PySimpleGUI as sg
module_to_process = sg
from collections import namedtuple
triplet = namedtuple('triplet', 'name value atype'.split(' '))
@ -39,6 +37,9 @@ TABLE_Only_table_RETURN_TEMPLATE = '''|Type|Name|Meaning|\n|---|---|---|\n|<type
from collections import namedtuple
special_case = namedtuple('special_case', 'ok sig table just_text'.split(' '))
def get_line_number(python_obj):
return inspect.getsourcelines(python_obj)[1]
"""
injection_points:
@ -83,22 +84,26 @@ CLASS
}
"""
def get_return_part(code: str, line_break=None) -> str:
def get_return_part(code: str, line_break=None):
""" Find ":return:" part in given "doc string"."""
if not line_break:
# line_break = ' <br> '
line_break = ''
if ':return:' not in code:
return ''
return '', ''
only_return = code[code.index(':return:')+len(':return:'):].strip().replace('\n', line_break)
if ':rtype' in only_return:
only_return = only_return.split(':rtype')[0]
return only_return
only_return = only_return.split(':rtype')[0].strip()
return_TYPE = ''
if ':rtype' in code:
rcode = code.strip()
return_TYPE = rcode[rcode.index(':rtype:')+len(':rtype:'):].strip()
return only_return, return_TYPE
def special_cases(function_name, function_obj, sig, doc_string, line_break=None):
@ -144,10 +149,17 @@ def special_cases(function_name, function_obj, sig, doc_string, line_break=None)
return special_case(ok=True, just_text=f'\n\n#### property: {function_name}\n{get_doc_desc(doca, function_obj)}\n\n', sig='', table='')
# TEMPLATE3
elif only_self and doca and ':param' not in doca and ':return:' in doca:
return_part, desc = get_return_part(doca, line_break=line_break), get_doc_desc(doca, function_obj)
return special_case(ok=True, just_text='',
return_part, return_part_type = get_return_part(doca, line_break=line_break)
# print(return_part, return_part_type)
desc = get_doc_desc(doca, function_obj)
a_table = TABLE_Only_table_RETURN_TEMPLATE.replace('$', return_part) + '\n\n'
if return_part_type:
a_table = a_table.replace('<type>', return_part_type)
return special_case(ok=True, just_text='',
sig=f'\n\n#### property: {function_name}\n{desc}\n\n',
table=TABLE_Only_table_RETURN_TEMPLATE.replace('$', return_part) + '\n\n')
table=a_table)
################################################################################################################
# _ _ _ _ _ #
@ -161,27 +173,27 @@ def special_cases(function_name, function_obj, sig, doc_string, line_break=None)
################################################################################################################
"""
# TEMPLATE1
# TEMPLATE1
def Get(self):
''' '''
# TEMPLATE2 -return -param
def Get(self):
'''
blah blah blah
'''
# TEMPLATE3 +return -param
def Get(self):
'''
blah blah blah
:return: blah-blah
'''
# TEMPLATE4 -return +param
def SetFocus(self, elem):
'''
blah blah blah
:param elem: qwerty
'''
def Get(self):
''' '''
# TEMPLATE2 -return -param
def Get(self):
'''
blah blah blah
'''
# TEMPLATE3 +return -param
def Get(self):
'''
blah blah blah
:return: blah-blah
'''
# TEMPLATE4 -return +param
def SetFocus(self, elem):
'''
blah blah blah
:param elem: qwerty
'''
"""
# TEMPLATE1
@ -192,9 +204,14 @@ def special_cases(function_name, function_obj, sig, doc_string, line_break=None)
return special_case(ok=True, just_text=f'\n\n{doca}\n\n```python\n{function_name}()\n```\n\n', sig='', table='')
# TEMPLATE3
elif only_self and doca and ':param' not in doca and ':return:' in doca:
return_part, desc = get_return_part(doca, line_break=line_break), get_doc_desc(doca, function_obj)
return_part, return_part_type = get_return_part(doca, line_break=line_break)
desc = get_doc_desc(doca, function_obj)
a_table = TABLE_Only_table_RETURN_TEMPLATE.replace('$', return_part) + '\n\n'
if return_part_type:
a_table = a_table.replace('<type>', return_part_type)
return special_case(ok=True, just_text='', sig=f'\n\n{desc}\n\n`{function_name}()`\n\n',
table=TABLE_Only_table_RETURN_TEMPLATE.replace('$', return_part) + '\n\n')
table=a_table)
# TEMPLATE4
elif only_self and doca and ':param' not in doca and ':return:' in doca:
return special_case(ok=False, just_text='', sig='', table='')
@ -227,7 +244,8 @@ def is_propery(func):
def get_sig_table_parts(function_obj, function_name, doc_string,
logger=None, is_method=False, line_break=None,
insert_md_section_for__class_methods=False):
insert_md_section_for__class_methods=False,
replace_pipe_bar_in_TYPE_TEXT_char=''):
"""
Convert python object "function + __doc__"
to
@ -245,6 +263,7 @@ def get_sig_table_parts(function_obj, function_name, doc_string,
if logger: logger.error(f'PROBLEM WITH "{function_obj}" "{function_name}":\nit\'s signature is BS. Ok, I will just return \'\' for \'signature\' and \'param_table\'\nOR BETTER - delete it from the 2_readme.md.\n======')
return '', ''
# if 'The text currently displayed on the butto' in
if not is_propery(function_obj):
for key in sig:
@ -252,22 +271,23 @@ def get_sig_table_parts(function_obj, function_name, doc_string,
if 'self' == str(key): continue
elif key == 'args': rows.append('args=*<1 or N object>')
elif val == _empty: rows.append(key)
elif val == None: rows.append(f'{key}=None')
elif type(val) in (int, float): rows.append(f'{key}={val}')
elif type(val) is str: rows.append(f'{key}="{val}"')
elif type(val) is tuple: rows.append(f'{key}={val}')
elif type(val) is bool: rows.append(f'{key}={val}')
elif type(val) is bytes: rows.append(f'{key}=...')
elif val == None: rows.append(f'{key} = None')
elif type(val) in (int, float): rows.append(f'{key} = {val}')
elif type(val) is str: rows.append(f'{key} = "{val}"')
elif type(val) is tuple: rows.append(f'{key} = {val}')
elif type(val) is bool: rows.append(f'{key} = {val}')
elif type(val) is bytes: rows.append(f'{key} = ...')
else:
raise Exception(f'IDK this type -> {key, val}')
# if 'update' in function_name.lower(): breakpoint();
sig_content = f',\n{TAB_char}'.join(rows) if len(rows) > 2 else f', '.join(rows) if rows else ''
sign = "\n\n{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string, function_obj), function_name, sig_content)
if is_method:
if insert_md_section_for__class_methods:
sign = "\n\n{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string, function_obj), function_name, sig_content)
@ -287,7 +307,7 @@ def get_sig_table_parts(function_obj, function_name, doc_string,
# qpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqp
# 1
return_guy = get_return_part(doc_string, line_break=line_break)
return_guy, return_guy_type = get_return_part(doc_string, line_break=line_break)
if not return_guy:
md_return = return_guy = ''
else:
@ -311,35 +331,56 @@ def get_sig_table_parts(function_obj, function_name, doc_string,
(str) -> str
Union[str, Tuple[str, int]] -> Union[str, Tuple[str, int]]
'''
final_txt = ''
if re.compile(r'\(\s?\w+\s?\)', flags=re.M|re.DOTALL).match(txt):
return txt.rstrip(')').lstrip('(')
final_txt = txt.rstrip(')').lstrip('(')
else:
return txt
final_txt = txt
if ') or (' in final_txt:
final_txt = final_txt.replace(') or (', ' OR ')
if replace_pipe_bar_in_TYPE_TEXT_char and '|' in final_txt:
final_txt = final_txt.replace('|', replace_pipe_bar_in_TYPE_TEXT_char)
return final_txt
# if 'led by application to change the tooltip text for an Element. Normally invoked using ' in docstring:
# pass
# print(123)
# |> find PARAM, PARAM_TYPE, PARAM_DESCRIPTIONe
# |> find PARAM, PARAM_TYPE, PARAM_DESCRIPTION
trips = [triplet( i.group(1), replace_re(i.group(2), r'\s{2,}', ' '), process_type(i.group(3).strip()))
for index, i in enumerate(re.finditer(row_n_type_regex, docstring + ' \n'))]
if not trips:
if not trips and ':return:' not in docstring: # no :param in doc
raise Exception('no _TRIPs found!')
# ===|> format markdown table
# ---------------------------
# ROW template:
max_type_width, max_name_width = 20, 20
max_type_width, max_name_width = 40, 40
try:
max_type_width, max_name_width = max([len(i.atype) for i in trips]), max([len(i.name) for i in trips])
if trips:
max_type_width, max_name_width = max([len(i.atype) for i in trips]), max([len(i.name) for i in trips])
except Exception as e:
logger.warning(f"ALERT ------ bug with max_type_width, max_name_width variables")
logger.debug(f"just ALERT ------ bug with max_type_width, max_name_width variables: {a_original_obj.__name__}")
# print(f"ALERT ------ bug with max_type_width, max_name_width variables")
# import pdb; pdb.set_trace();
row_template = f'| {{: ^{max_type_width}}} | {{: ^{max_name_width}}} | {{}} |'
# rows, and finally table.
rows = [row_template.format(i.atype, i.name, i.value) for i in trips]
rows = []
for some_triplet in trips:
if '|' in some_triplet.atype:
good_atype = some_triplet.atype.replace('|', 'or')
else:
good_atype = some_triplet.atype
good_atype = good_atype.replace(' OR ', ' or ').replace('\\or', 'or')
rows.append(row_template.format(good_atype, some_triplet.name, some_triplet.value))
row_n_type_regex = re.compile(r':param ([\d\w\*\s]+):([\d\D]*?):type [\w\d]+:([\d\D].*?)\n', flags=re.M|re.DOTALL)
@ -350,15 +391,22 @@ def get_sig_table_parts(function_obj, function_name, doc_string,
a_doc = docstring + ' \n'
aa = list(re.finditer(regex_pattern, a_doc))[0]
text, atype = aa.group(1).strip(), aa.group(2).strip()
rows.append(f'| {atype} | **RETURN** | {text}')
if text.strip():
if '|' in atype:
atype_no_pipes = atype.replace('|', 'or')
rows.append(f'| {atype_no_pipes} | **RETURN** | {text}')
else:
rows.append(f'| {atype} | **RETURN** | {text}')
except Exception as e:
padded_name = "{: <25}".format(f"'{a_original_obj.__name__}'")
# TODO - Mike changed this!
# logger.warning(f"ALERT ------ Hi, Mike! Please, fix ':return:' in {padded_name}"
# " \tIF you want to see 'return' row in 'signature table'")
# print(a_original_obj)
# import pdb; pdb.set_trace();
pass
# func_or_method_name = a_original_obj.__name__.lower()
# if True or func_or_method_name not in ['__init__', 'setfocus', 'settooltip', 'update', 'unbind', 'setfocus', 'bind', 'unbind', 'set_size', 'expand', 'set_cursor']:
# padded_name = "{: <25}".format(f"'{a_original_obj.__name__}'")
# logger.warning(f"ALERT ------ Warning=== Please, fix ':return:' in {padded_name}" +
# " \tIF you want to see 'return' row in 'signature table'", metadata={'lineno' : get_line_number(a_original_obj)})
header = '\nParameter Descriptions:\n\n|Type|Name|Meaning|\n|--|--|--|\n'
@ -371,17 +419,24 @@ def get_sig_table_parts(function_obj, function_name, doc_string,
# 3
try:
# if 'list of valid string' in doc_string:
# import pdb; pdb.set_trace();
params_TABLE = md_table = make_md_table_from_docstring(doc_string, function_obj)
except Exception as e:
logger.warning(f'Boy======= We got empty md_table for "{function_obj.__name__}"')
func_name_ = function_obj.__name__
if func_name_ not in ['unbind', 'theme_'] and not func_name_.startswith('theme_'):
logger.warning(f'Warning======= We got empty md_table for "{func_name_}"',
metadata={'lineno' : get_line_number(function_obj)})
params_TABLE = md_table = ''
if not md_table.strip():
params_TABLE = ''
if return_guy:
# import pdb; pdb.set_trace();
sign = sign[:-4] + f' -> {return_guy}\n```\n'
return sign, params_TABLE
@ -389,7 +444,7 @@ def pad_n(text):
return f'\n{text}\n'
def render(injection, logger=None, line_break=None, insert_md_section_for__class_methods=False):
def render(injection, logger=None, line_break=None, insert_md_section_for__class_methods=False, replace_pipe_bar_in_TYPE_TEXT_char=''):
try:
if 'skip readme' in injection['function_object'].__doc__:
@ -401,14 +456,15 @@ def render(injection, logger=None, line_break=None, insert_md_section_for__class
sig, table = get_sig_table_parts(function_obj=injection['function_object'],
function_name=injection['part2'],
insert_md_section_for__class_methods=insert_md_section_for__class_methods,
doc_string=injection['function_object'].__doc__, logger=logger, line_break=line_break)
doc_string=injection['function_object'].__doc__, logger=logger, line_break=line_break,
replace_pipe_bar_in_TYPE_TEXT_char=replace_pipe_bar_in_TYPE_TEXT_char)
else: # class method
function_name = injection['parent_class'].__name__ if injection['part2'] == '__init__' else injection['part2']
sig, table = get_sig_table_parts(function_obj=injection['function_object'],
function_name=function_name, is_method=True,
insert_md_section_for__class_methods=insert_md_section_for__class_methods,
doc_string=injection['function_object'].__doc__, logger=logger, line_break=line_break)
doc_string=injection['function_object'].__doc__, logger=logger, line_break=line_break,
replace_pipe_bar_in_TYPE_TEXT_char=replace_pipe_bar_in_TYPE_TEXT_char)
if injection['number'] == '':
return pad_n(sig) + pad_n(table)
@ -425,6 +481,7 @@ def readfile(fname):
return ff.read()
def main(do_full_readme=False,
files_to_include: list = [],
logger:object=None,
@ -437,7 +494,8 @@ def main(do_full_readme=False,
remove_repeated_sections_classmethods:bool=False,
output_repeated_tags:bool=False,
main_md_file='markdown input files/2_readme.md',
skip_dunder_method:bool=True):
skip_dunder_method:bool=True, verbose = False,
replace_pipe_bar_in_TYPE_TEXT_char=''):
"""
Goal is:
1) load 1_.md 2_.md 3_.md 4_.md
@ -483,8 +541,9 @@ def main(do_full_readme=False,
return True
psg_members = [i for i in getmembers(module_to_process) if valid_field(i)] # variables, functions, classes
# psg_members = getmembers(PySimpleGUIlib) # variables, functions, classes
if verbose: timee(''' psg_members ''')
psg_members = [i for i in getmembers(PySimpleGUI) if valid_field(i)] # variables, functions, classes
# psg_members = getmembers(PySimpleGUI) # variables, functions, classes
psg_funcs = [o for o in psg_members if isfunction(o[1])] # only functions
psg_classes = [o for o in psg_members if isclass(o[1])] # only classes
psg_classes_ = list(set([i[1] for i in psg_classes])) # boildown B,Btn,Butt -into-> Button
@ -514,6 +573,7 @@ def main(do_full_readme=False,
# >1 REMOVE HEADER
if verbose: timee(''' REMOVE HEADER ''')
started_mark = '<!-- Start from here -->'
if started_mark in readme:
readme = readme.split('<!-- Start from here -->')[1]
@ -521,6 +581,7 @@ def main(do_full_readme=False,
# 2> find good tags
if verbose: timee(''' find good tags ''')
re_tags = re.compile(r'<!-- <\+[a-zA-Z_]+[\d\w_]*\.([a-zA-Z_]+[\d\w_]*)\+> -->')
mark_points = [i for i in readme.split('\n') if re_tags.match(i)]
@ -534,6 +595,7 @@ def main(do_full_readme=False,
readme = readme.replace(i, '\n')
# 4> log repeated tags
if verbose: timee(''' log repeated tags ''')
if output_repeated_tags:
if not allow_multiple_tags and len(list(set(mark_points))) != len(mark_points):
mark_points_copy = mark_points[:]
@ -555,6 +617,7 @@ def main(do_full_readme=False,
if verbose: timee('''# 0===0 functions 0===0''')
# 0===0 functions 0===0
for tag in func_tags:
@ -592,6 +655,7 @@ def main(do_full_readme=False,
logger.error(f' General error in parsing function tag: tag = "{tag}"; error="{str(e)}"')
continue
if verbose: timee('''# 0===0 classes 0===0''')
injection_points.append('now, classes.')
# 0===0 classes 0===0
for tag in classes_method_tags:
@ -648,35 +712,38 @@ def main(do_full_readme=False,
# =========== 5 injecting =========== #
# 888888888888888888888888888888888888888
# PLAN:
# (1) replace tags in 2_readme
# (1) replace tags in main_md_file
# with properly formateed text
# (2) log some data
# 8888888888888888888888888888888888888888888888888888888
bar_it = lambda x: '\n' + '='*len(x) + f'\nSTARTING TO INSERT markdown text into 2_readme.md\n' + '='*len(x) + '\n'
if verbose: timee('''bar_it = lambda x''')
bar_it = lambda x: '\n' + '='*len(x) + f'\nSTARTING TO INSERT markdown text into main_md_file\n' + '='*len(x) + '\n'
# 1> log some data
success_tags = []
bad_tags = []
for injection in injection_points:
if injection == 'now, classes.':
logger.info(bar_it('STARTING TO INSERT markdown text into 2_readme.md'))
logger.info(bar_it('STARTING TO INSERT markdown text into main_md_file'))
continue
# SPECIAL CASE: X.doc tag
if injection['part2'] == 'doc':
a_tag = injection['tag']
logger.info(f'a_tag = {a_tag, type(a_tag).__name__}')
# logger.info(f'a_tag = {a_tag.split('.')[0].split('+')[1], type(a_tag).__name__}')
logger.info('a_tag = ' + a_tag.split('.')[0].split('+')[1])
doc_ = '' if not injection['parent_class'].__doc__ else injection['parent_class'].__doc__
# if doc_ == None or a_tag == None:
readme = readme.replace(a_tag, doc_)
else:
if verbose: timee('''content = render''')
content = render(injection, logger=logger, line_break=line_break,
insert_md_section_for__class_methods=insert_md_section_for__class_methods)
insert_md_section_for__class_methods=insert_md_section_for__class_methods,
replace_pipe_bar_in_TYPE_TEXT_char=replace_pipe_bar_in_TYPE_TEXT_char)
if verbose: timee('''content = render end''')
tag = injection["tag"]
if content:
@ -687,6 +754,7 @@ def main(do_full_readme=False,
readme = readme.replace(tag, content)
if verbose: timee('''readme = readme.replace(bad_p''')
bad_part = '''\n\nParameter Descriptions:\n\n|Type|Name|Meaning|\n|--|--|--|\n\n'''
readme = readme.replace(bad_part, '\n')
@ -713,6 +781,7 @@ def main(do_full_readme=False,
if verbose: timee('''files = []''')
files = []
if 0 in files_to_include: files.append(readfile('markdown input files/1_HEADER_top_part.md'))
if 1 in files_to_include: files.append(readme)
@ -721,6 +790,7 @@ def main(do_full_readme=False,
Joined_MARKDOWN = '\n\n'.join(files) if do_full_readme or files else readme
if verbose: timee('''if output_name:''')
if output_name:
with open(output_name, 'w', encoding='utf-8') as ff:
CURR_DT = datetime.today().strftime('<!-- CREATED: %Y-%m-%d %H.%M.%S -->\n')

View File

@ -25,7 +25,11 @@ HOW DO I INSERT IMAGES ???
-->
![pysimplegui_logo](https://user-images.githubusercontent.com/13696193/43165867-fe02e3b2-8f62-11e8-9fd0-cc7c86b11772.png)
<p align="center">
<img src="https://raw.githubusercontent.com/PySimpleGUI/PySimpleGUI/master/images/for_readme/Logo%20with%20text%20for%20GitHub%20Top.png" alt="Python GUIs for Humans">
<h2 align="center">Python GUIs for Humans</h2>
</p>
[![tkinter](http://pepy.tech/badge/pysimplegui)](http://pepy.tech/project/pysimplegui) tkinter
[![tkinter27](https://pepy.tech/badge/pysimplegui27)](https://pepy.tech/project/pysimplegui27) tk 2.7
@ -1198,7 +1202,7 @@ Like above, you may have to install either pip or tkinter. To do this on Python
`sudo apt install python-tkinter`
### Upgrading from GitHub Using PySimpleGUI
## Upgrading from GitHub Using PySimpleGUI
Starting in version 4.17.0 there is code in the PySimpleGUI package that upgrades your previously pip installed package using the latest version checked into GitHub.

View File

@ -145,7 +145,7 @@ Preview of popups:
<img src="https://user-images.githubusercontent.com/13696193/44957595-9e15da00-aea1-11e8-8909-6b6121b74509.jpg">
</p>
<!-- <+func.Popup+> -->
<!-- <+func.popup+> -->
The other output Popups are variations on parameters. Usually the button_type parameter is the primary one changed.
@ -996,15 +996,96 @@ You have a couple of options for dealing this with. If your operation can be br
If, on the other hand, your operation is not under your control or you are unable to add `Refresh` calls, then the next option available to you is to move your long operations into a thread.
### The "Old Way"
There are a couple of demo programs available for you to see how to do this. You basically put your work into a thread. When the thread is completed, it tells the GUI by sending a message through a queue. The event loop will run with a timer set to a value that represents how "responsive" you want your GUI to be to the work completing.
These 2 demo programs are called
### The "New Way" - `Window.write_event_value`
This new function that is available currently only in the tkinter port as of July 2020 is exciting and represents the future way multi-threading will be handled in PySimpleGUI (or so is hoped).
Previously, a queue was used where your event loop would **poll** for incoming messages from a thread.
Now, threads can directly inject events into a Window so that it will show up in the `window.read()` calls. This allows a your event loop to "pend", waiting for normal window events as well as events being generated by threads.
You can see this new capability in action in this demo: Demo_Multithreaded_Write_Event_Value.py
Here is that program for your inspection and education. It's SO nice to no longer poll for threaded events.
```python
Demo_Threaded_Work.py - Best documented. Single thread used for long task
Demo_Multithreaded_Long_Tasks.py - Similar to above, but with less fancy GUI. Allows you to set amount of time
import threading
import time
import PySimpleGUI as sg
"""
Threaded Demo - Uses Window.write_event_value communications
Requires PySimpleGUI.py version 4.25.0 and later
This is a really important demo to understand if you're going to be using multithreading in PySimpleGUI.
Older mechanisms for multi-threading in PySimpleGUI relied on polling of a queue. The management of a communications
queue is now performed internally to PySimpleGUI.
The importance of using the new window.write_event_value call cannot be emphasized enough. It will hav a HUGE impact, in
a positive way, on your code to move to this mechanism as your code will simply "pend" waiting for an event rather than polling.
Copyright 2020 PySimpleGUI.org
"""
THREAD_EVENT = '-THREAD-'
cp = sg.cprint
def the_thread(window):
"""
The thread that communicates with the application through the window's events.
Once a second wakes and sends a new event and associated value to the window
"""
i = 0
while True:
time.sleep(1)
window.write_event_value('-THREAD-', (threading.current_thread().name, i)) # Data sent is a tuple of thread name and counter
cp('This is cheating from the thread', c='white on green')
i += 1
def main():
"""
The demo will display in the multiline info about the event and values dictionary as it is being
returned from window.read()
Every time "Start" is clicked a new thread is started
Try clicking "Dummy" to see that the window is active while the thread stuff is happening in the background
"""
layout = [ [sg.Text('Output Area - cprint\'s route to here', font='Any 15')],
[sg.Multiline(size=(65,20), key='-ML-', autoscroll=True, reroute_stdout=True, write_only=True, reroute_cprint=True)],
[sg.T('Input so you can see data in your dictionary')],
[sg.Input(key='-IN-', size=(30,1))],
[sg.B('Start A Thread'), sg.B('Dummy'), sg.Button('Exit')] ]
window = sg.Window('Window Title', layout, finalize=True)
while True: # Event Loop
event, values = window.read()
cp(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event.startswith('Start'):
threading.Thread(target=the_thread, args=(window,), daemon=True).start()
if event == THREAD_EVENT:
cp(f'Data from the thread ', colors='white on purple', end='')
cp(f'{values[THREAD_EVENT]}', colors='white on red')
window.close()
if __name__ == '__main__':
main()
```
These 2 particular demos have a LOT of comments showing you where to add your code, etc.. The amount of code to do this is actually quite small and you don't need to understand the mechanisms used if you simply follow the demo that's been prepared for you.
### Multithreaded Programs
@ -1129,7 +1210,26 @@ theme_previewer
The first step is to create the window object using the desired window customizations.
Note - There is no direct support for "**modal windows**" in PySimpleGUI. All windows are accessible at all times unless you manually change the windows' settings.
## Modal Windows (only applied to tkinter port currently
)
NOTE - as of PySimpleGUI 4.25.0 Modal Windows are supported! By default the `popup` windows that block will be marked Modal by default. This is a somewhat risky change because your expisting applications will behave differently. However, in theory, you shouldn't have been interacting with other windows while the popup is active. All of those actions are at best queued. It's implementation dependent.
"Modal" in this case means that you must close this "modal" window before you will be able to interact with windows created before this window. Think about an "about" box. You normally have to close this little popup in most programs. So, that's what PySimpleGUI is doing now.
## Making your window modal
To make a Modal Wio=ndow you have 2 options.
1. Set the `moodel=True` parameter in your Window calls.
2. Call the method `Window.make_modal()` to chance a window from non-modal to modal. There is no modal to non-modal. Don't see the need for one. If one comes up, sure!
### Disabling modal windows
Popups that block are the only windows that have modal on by default. There is a modal parameter than you can set to False to turn it off.
For the earlier than 4.25.0 and other ports of PySimpleGUI There is no direct support for "**modal windows**" in PySimpleGUI. All windows are accessible at all times unless you manually change the windows' settings.
**IMPORTANT** - Many of the `Window` methods require you to either call `Window.read` or `Window.Finalize` (or set `finalize=True` in your `Window` call) before you call the method. This is because these 2 calls are what actually creates the window using the underlying GUI Framework. Prior to one of those calls, the methods are likely to crash as they will not yet have their underlying widgets created.
@ -1773,6 +1873,160 @@ Then to turn off return values for that element, the `Multiline` element would b
sg.Multiline(size=(40,8), key='-MLINE-' + sg.WRITE_ONLY_KEY)
```
## Key Errors - Key error recovery algorithm
In the primary (tkinter) port of PySimpleGUI, starting in version 4.27.0 (not yet on PyPI... but available on GitHub as 4.26.0.14+)
There are now 3 controls over key error handling and a whole new era of key reporting.
```python
SUPPRESS_ERROR_POPUPS = False
SUPPRESS_RAISE_KEY_ERRORS = False
SUPPRESS_KEY_GUESSING = False
```
You can modify these values by calling `set_options`.
```python
sg.set_options(suppress_raise_key_errors=False, suppress_error_popups=False, suppress_key_guessing=False)
```
A basic definition of them are:
`suppress_error_popups` - Disables error popups that are generated within PySimpleGUI itself to not be shown
`suppress_raise_key_errors` - Disables raising a key error if a key or a close match are not found
`suppress_key_guessing` - Disables the key guessing algorithm should you have a key error
With the defaults left as defined (all `False`), here is how key errors work.
This is the program being used in this example:
```python
import PySimpleGUI as sg
def main():
sg.set_options(suppress_raise_key_errors=False, suppress_error_popups=False, suppress_key_guessing=False)
layout = [ [sg.Text('My Window')],
[sg.Input(k='-IN-'), sg.Text(size=(12,1), key='-OUT-')],
[sg.Button('Go'), sg.Button('Exit')] ]
window = sg.Window('Window Title', layout, finalize=True)
while True: # Event Loop
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
window['-O U T'].update(values['-IN-'])
window.close()
def func():
main()
func()
```
A few things to note about it:
* There are multiple levels of functions being called, not just a flat program
* There are 2 keys explicitly defined, both are text at this point (we'll change them later)
* There are 2 lookups happening, one with `window` the other with `values`
This key error recovery algorithm only applies to element keys being used to lookup keys inside of windows. The `values` key lookup is a plain dictionary and so nothing fancy is done for that lookup.
### Example 1 - Simple text string misspelling
In our example, this line of code has an error:
```python
window['-O U T'].update(values['-IN-'])
```
There are multiple problems with the key `'-OUT-'`. It is missing a dash and it has a bunch of extra spaces.
When the program runs, you'll first see the layout with no apparent problems:
![SNAG-0882](https://user-images.githubusercontent.com/46163555/88704649-60954800-d0dc-11ea-885a-1ebadba039b7.jpg)
Clicking the OK button will cause the program to return from `window.read()` and thus hit our bad key. The result will be a popup window that resembles this:
![SNAG-0883](https://user-images.githubusercontent.com/46163555/88704635-5bd09400-d0dc-11ea-88a2-42e7386b076b.jpg)
Note a few things about this error popup. Your shown your bad key and you're also shown what you likely meant. Additionally, you're shown the filename, the line number and the line of code itself that has the error.
Because this error was recoverable, the program continues to run after you close the error popup. The result is what you expect from this program... the output field is the same as your information input.
![SNAG-0884](https://user-images.githubusercontent.com/46163555/88704691-71de5480-d0dc-11ea-8800-9379044a3f1f.jpg)
### Example 2 - Tuple error
Keys can be a variety of types, including tuples. In this particular program we have a tuple specified in the layout and have used an incorrect tuple in the lookup. Once again the recovery process worked and the program continued.
![SNAG-0885](https://user-images.githubusercontent.com/46163555/88705188-2d9f8400-d0dd-11ea-9a91-f92cef9f6219.jpg)
### Example 3 - No close match found
In this example, as you can see in the error popup, there was such a mismatch that no substitution could be performed.
![SNAG-0886](https://user-images.githubusercontent.com/46163555/88705707-e6fe5980-d0dd-11ea-8fcc-bc024298705d.jpg)
This is an unrecoverable error, so a key error exception is raised.
```python
Traceback (most recent call last):
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 25, in <module>
func()
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 23, in func
main()
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 17, in main
window[(1,2,3)].update(values['-IN-'])
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 8591, in __getitem__
return self.FindElement(key)
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 7709, in FindElement
raise KeyError(key)
KeyError: (1, 2, 3)
```
If you're running an IDE such as PyCharm, you can use the information from the assert to jump to the line of code in your IDE based on the crash data provided.
### Choose Your Desired Combination
There are enough controls on this error handling that you can control how you want your program to fail. If you don't want any popups, and no guessing and would instead like to simply get an exception when the key error happens, then call `set_options` with this combination:
```python
sg.set_options(suppress_raise_key_errors=False, suppress_error_popups=True, suppress_key_guessing=True)
```
This will cause Example #1 above to immediately get an exception when hitting the statement with the error. Even though the guessing is turned off, you're still provided with the closest match to help with your debugging....
```
** Error looking up your element using the key: -O U T The closest matching key: -OUT-
Traceback (most recent call last):
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 25, in <module>
func()
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 23, in func
main()
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 17, in main
window['-O U T'].update(values['-IN-'])
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 8591, in __getitem__
return self.FindElement(key)
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 7709, in FindElement
raise KeyError(key)
KeyError: '-O U T'
```
## Common Element Parameters
@ -2660,7 +2914,6 @@ For example, this layout has a single slider element that spans several rows fol
Without a Column Element you can't create a layout like this. But with it, you should be able to closely match any layout created using tkinter only.
```python
import PySimpleGUI as sg
@ -2695,9 +2948,10 @@ sg.Popup(event, values, line_width=200)
```
### Column, Frame, Tab, Window element_justification
Beginning in Release 4.3 you can set the justification for any container element. This is done through the `element_justification` parameter. This will greatly help anyone that wants to center all of their content in a window. Previously it was difficult to do these kinds of layouts, if not impossible.
## Columns As a Way to Modify Elements
Sometimes Columns are used to contain a single elemnet, but to give that elemously it was difficult to do these kinds of layouts, if not impossible.
justify the `Column` element's row by setting the `Column`'s `justification` parameter.
@ -3675,6 +3929,47 @@ You'll find the pattern - `window.Element(key)` in older code. All of code afte
Note that to change a progress meter's progress, you call `update_bar`, not `update`. A change to this is being considered for a future release.
# Cursors - Setting for Elements and Windows
It is possible to change the normal arrow cursor into something else by setting the cursor for an element or the entire window. The result will be the cursor changing when you move the mouse over the elements or Window.
One of the best examples is URLs. Users are accustomed to seeing a hand cursor when the mouse is moved over a link. By setting the cursor to a hand for a Text element that has text that is in the format of a URL, it signals to the user that it's a link that can be clicked.
The `set_cursor` method is used to set the cursor for an element. Perform an element look-up or use a variable containing an element, and call the `set_cursor` method, passing in a string that selects the cursor. The valid cursor names are documented in the tkinter docs as this call maps directly to a tkinter call.
These cursor strings were obtained from the Tk manual and are what you pass into the `set_cursor` methods.
## Windows Level Cursor
You can also set the cursor for the Window as a whole, including the margins and areas elements don't directly fill. Call `Window.set_cursor()` to set the cursor at the Window level.
## Valid Cursor Strings
`X_cursor, arrow, based_arrow_down, based_arrow_up, boat, bogosity, bottom_left_corner, bottom_right_corner, bottom_side, bottom_tee, box_spiral, center_ptr, circle, clock, coffee_mug, cross, cross_reverse, crosshair, diamond_cross, dot, dotbox, double_arrow, draft_large, draft_small, draped_box, exchange, fleur, gobbler, gumby, hand1, hand2, heart, icon, iron_cross, left_ptr, left_side, left_tee, leftbutton, ll_angle, lr_angle, man, middlebutton, mouse, pencil, pirate, plus, question_arrow, right_ptr, right_side, right_tee, rightbutton, rtl_logo, sailboat, sb_down_arrow, sb_h_double_arrow, sb_left_arrow, sb_right_arrow, sb_up_arrow, sb_v_double_arrow, shuttle, sizing, spider, spraycan, star, target, tcross, top_left_arrow, top_left_corner, top_right_corner, top_side, top_tee, trek, ul_angle, umbrella, ur_angle, watch, xterm`
## No Cursor
To specify no cursor should be shown, the cursor `'no'` can be used on some platforms
## Windows OS Specific
One windows, these cursors map to native Windows cursors:
`arrow, center_ptr, crosshair, fleur, ibeam, icon, sb_h_double_arrow, sb_v_double_arrow, watch, xterm`
And these are also available:
`no, starting, size, size_ne_sw, size_ns, size_nw_se, size_we, uparrow, wait`
## Mac OS Specific
`arrow, cross, crosshair, ibeam, plus, watch, xterm`
And these additional native cursors are available for the Mac
`copyarrow, aliasarrow, contextualmenuarrow, text, cross-hair, closedhand, openhand, pointinghand, resizeleft, resizeright, resizeleftright, resizeup, resizedown, resizeupdown, none, notallowed, poof, countinguphand, countingdownhand, countingupanddownhand, spinning`
# Keyboard & Mouse Capture
@ -4203,6 +4498,400 @@ Exception module 'tkinter' has no attribute '__version__'
```
---
# User Settings API
In release 4.30.0 there is a new set of API calls available to help with "user settings". Think of user settings as a dictionary that is automatically written to your hard drive. That's basically what it is. Underpinning the code is the JSON package provided by Python.
While using JSON files to save and load a settings dictionary isn't very difficult, it is still code you'll need to write if you want to save settings as part of your GUI. Since having "settings" for a GUI based program isn't uncommon, it made sense to build this capability into PySimpleGUI. Clearly you can still use your own method for saving settings, but if you're looking for a simple and easy way to do it, these calls are likely about as easy as it gets.
There have already been some demo programs written that use JSON files to store settings. You can expect that this capability will begin to show up in more demos in the future since it's now part of PySimpleGUI.
User settings are stored in a Python dictionary which is saved to / loaded from disk. Individual settings are thus keys into a dictionary. You do not need to explicitly read nor write the file. Changing any entry will cause the file to be saved. Reading any entry will cause the file to be read if it hasn't already been read.
## Two Interfaces
There are 2 ways to access User Settings
1. User Settings function calls
2. The `UserSettings` class
They both offer the same basic operations. The class interface has an added benefit of being able to access the individual settings using the same syntax as Python dictionary.
## List of Calls for Function Interface
|Function|Description|
| --- | --- |
|user_settings|Returns settings as a dictionary|
|user_settings_delete_entry|Deletes a setting|
|user_settings_delete_filename|Deletes the settings file|
|user_settings_file_exists|Returns True if settings file specified exists|
|user_settings_filename|Returns full path and filename of current settings file|
|user_settings_get_entry|Returns value for a setting. If no setting found, then specified default value is returned|
|user_settings_load|Loads dictionary from the settings file. This is not normally needed||
|user_settings_save|Saves settings to current or newly specified file. Not normally needed|
|user_settings_set_entry|Sets an entry to a particular value
|user_settings_write_new_dictionary|Writes a specified dictionary to settings file|
## Operations
There are 2 categories that the calls can be divided into.
1. File operations
2. Settings operations
File operations involve working with the JSON file itself. They include:
* Setting the path and/or filename
* Load/save the file (these are somewhat optional as the saving loading/saving is done automatically)
* Deleting the settings file
* Checking if settings file exists
Generally speaking, a setting is specified with a key which is generally a string. Settings operations are for working with the individual settings and include:
* Get the value of a setting (returns a default value if not found)
* Set the value of a setting (also saves the settings to disk)
Any setting operation may cause the file to be written. This is because a "get" operation can include returning a default value if the setting isn't found. This means a new entry is made in your settings dictionary is one didn't exist before. Since a new entry is made, that means it needs to be also be written to disk.
## Filenames
The settings filename defaults the filename of your Python file making the call with the extension ".json" added. If your Python program is called `test.py` then your default settings filename will be `test.json`.
In addition to the filename having a default value, the path to the file also has a default value. The default depends on your operating system.
|Operating System|Default Path|
| --- | --- |
| Windows | \user\user_name\AppData\Local\PySimpleGUI\settings |
| Linux | ~/.config/PySimpleGUI/settings |
| Mac | ~/Library/Application Support/PySimpleGUI/settings |
When calling the User Settings APIs, if a parameter is named `filename`, you can specify a full path or just the filename. This will save you the trouble of having to split up your path and filename in your code. If you specify only the path, the the filename will be added to that path and named as defined earlier.
Like the rest of PySimpleGUI, the idea is for you to write as little code as possible. The default values for the filename and path should be fine for you to use. They will be stored in a location on your system that is meant to store user settings.
### Setting Filename
If you want to see what the current filename is for your settings, then you can call `user_settings_filename()` with no parameters and you'll get back an absolute path and filename.
To make the code for specifying the folder and filename as simple as possible, the 2 parts are separated in the call specifying the name of the settings file. However, it is possible to supply a full and complete folder + filename as well.
The default filename for your settings file is the name of the file that makes the call to the User Settings API's with the `.py` extension changed to a `.json` extension. If your source file is called `demo.py`, then your settings filename will be `demo.json`.
#### Setting only the filename
If you want to control the name of the file and/or the path to the settings file, then you will use the `user_settings_filename` call. This function takes 2 parameters.
```python
user_settings_filename(filename=None, path=None)
```
If you set only the path, then the filename will default to the value already described. If you set only the filename, then the path will be the default path is dependent on your operating system. See the table above for the locations for each OS.
```python
import PySimpleGUI as sg
sg.user_settings_filename(filename='my_settings.json')
print(sg.user_settings_filename())
```
If you are running on Windows, then the result of running this code will be this printed on the console:
```
C:\Users\your_use_name\AppData\Local\PySimpleGUI\settings\my_settings.json
```
You are not restricted to naming your settings file to an extension of .json. That is simply the default extension that's used by PySimpleGUI. You can use any extension you would like, including no extension.
#### Setting only the path
Maybe you don't care about the settings filename itself, but you do care about where the settings are stored. Let's say you want the settings to be stored in the same folder as your Python source file. Specifying `path='.'` will achieve this.
#### Setting a fully qualified filename
If you want to specify the full absolute path and filename of the settings file, you can do it by using the filename parameter. Instead of passing the filename only, pass in a fully qualified path and filename. If you want to name your settings file `a:\temp\my_settings`, then your call will look like this:
```python
sg.user_settings_filename(filename=r'a:\temp\my_settings')
```
You are not required to break your file down into 2 parameters. You could if you wanted to however. The equivalent to the above call using 2 parameters would be:
```python
sg.user_settings_filename(filename='my_settings' , path=r'a:\temp')
```
### Getting the current filename
Calling `user_settings_filename` with no parameters will return the full path and filename of your settings file as a single string.
### File Loading / Saving
Generally speaking you will not need to load or save your settings file. It is automatically saved after every change.
Note that reading a setting can also cause the file to be written. If you read a setting and the setting did not exist, then your call to `user_settings_get_entry` will return the default value you specified. As a result, the dictionary is updated with this default value and in return the file is written with this value as well.
One of the situations where you may want to explicitly read/load the settings file is if you're expecting it to be modified by another program.
Like so much of PySimpleGUI, as much as possible is automatically done on your behalf. This includes the requirement of saving and loading your settings file. Even naming your settings file is optional.
## The `UserSettings` Class Interface
The `UserSettings` class makes working with settings look like a Python dictionary. The familiar [ ] syntax is used to read, write and delete entries.
### Creating a `UserSettings` Object
The first step is to create your setting object. The parameters are the same as calling the `user_settings_filename` function. If you want to use the default values, then leave the parameters unchanged.
```python
settings = sg.UserSettings()
```
This is the same as calling `sg.user_settings_filename()`
### Reading, Writing, and Deleting an Individual Settings Using [ ] Syntax
The first operation will be to create the User Settings object.
```python
settings = sg.UserSettings()
```
To read a setting the dictionary-style [ ] syntax is used. If the item's name is `'-item-'`, then reading the value is achieved by writing
```python
item_value = settings['-item-']
```
Writing the setting is the same syntax except the expression is reversed.
```python
settings['-item-'] = new_value
```
To delete an item, again the dictionary style syntax is used.
```python
del settings['-item-']
```
You can also call the delete_entry method to delete the entry.
```python
settings.delete_entry('-item-')
```
### `UserSettings` Methods
You'll find all of the `UserSettings` methods available to you detailed in the Call Reference documentation.
One operation in particular that is not achievable using the [ ] notation is a "get" operation with a default value. For dictionaries, this method is `get` and for the `UserSettings` class the method is also called `get`. They both have an optional second parameter that represents a "default value" should the key not be found in the dictionary.
If you would like a setting with key `'-item-'` to return an empty string `''` instead of `None` if they key isn't found, then you can use this code to achieve that:
```python
value = settings.get('-item-', '')
```
It's the same kind of syntax that you're used to using with dictionaries.
### Default Value
Normally the default value will be `None` if a key is not found and you get the value of the entry using the bracket format:
```python
item_value = settings['-item-']
```
You can change the default value by calling `settings.set_default_value(new_default)`. This will set the default value to return in the case when no key is found. Note that an exception is not raised when there is a key error (see next section on error handling). Instead, the default value is returned with a warning displayed.
## Displaying the Settings Dictionary
The class interface makes it easy to dump out the dictionary. If you print the UserSettings object you'll get a printout of the dictionary.
Note that you'll need to "load" the settings from disk if you haven't performed any operations on the settings.
```python
settings = sg.UserSettings()
settings.load()
print(settings)
```
If you were to print the dictionary after creating the object, then the `load` is not needed
```python
settings = sg.UserSettings()
print(settings['-item-'])
print(settings)
```
To print the dictionary using the function call interface:
```python
print(sg.user_settings())
```
## Error Handling for User Settings
From a GUI perspective, user settings are not critical to the GUI operations itself. There is nothing about settings that will cause your window to not function. As a result, errors that occur in the User Settings are "soft errors". An error message is displayed along with information about how you called the function, when possible, and then execution continues.
One reason for treating these as soft errors and thus not raising an exception is that raising an exception will crash your GUI. If you have redirected your output, which many GUIs do, then you will see no error information and your window will simply disappear. If you double clicked a .py file to launch your GUI, both the GUI and the console window will instantly disappear if the GUI crashes, leaving you no information to help you debug the problem.
The only time errors can occur are during file operations. Typically these errors happen because you've specified a bad path or you don't have write permission for the path you specified.
Example error message. If you executed this code:
```python
def main():
sg.user_settings_filename(path='...')
sg.user_settings_set_entry('-test-',123)
```
Then you'll get an error when trying to set the '-test-' entry because `'...'` is not a valid path.
```
*** Error saving settings to file:***
...\scratch_1065.json [Errno 2] No such file or directory: '...\\scratch_1065.json'
The PySimpleGUI internal reporting function is save
The error originated from:
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_1065.py"
line 8
in main
sg.user_settings_set_entry('-test-',123)
```
You should be able to easily figure out these errors as they are file operations and the error messages are clear in detailing what's happened and where the call originated.
### Silenting the Errors
If you're the type that doesn't want to see any error messages printed out on your console, then you can silence the error output.
When using the class interface, there is a parameter `silent_on_error` that you can set to `True`.
For the function interface, call the function `user_settings_silent_on_error()` and set the parameter to `True`
## Coding Convention for User Settings Keys
The User Settings prompted a new coding convention that's been added to PySimpleGUI examples. As you're likely aware, keys in layouts have the format `'-KEY-`'. For UserSettings, a similar format is used, but instead of the string being in all upper case, the characters are lower case. In the example below, the user setting for "filename" has a User Setting key of `'-filename-'`. Coding conventions are a good thing to have in your projects. You don't have to follow this one of course, but you're urged to create your own for places in your code that it makes sense. You could say that PEP8 is one giant coding convention for the Python language as a whole. You don't have to follow it, but most Python programmers do. We follow it "by convention".
The reason this is done in PySimpleGUI is so that the keys are immediately recognizable. Perhaps your application has dictionaries that you use. If you follow the PySimpleGUI coding convention of Element keys have the format `'-KEY-'` and User Settings keys have the format of `'-key-'`, then you'll immediately understand what a specific key is used for. Your company may have its own coding conventions so follow those if appropriate instead of what you see in the PySimpleGUI examples.
## Example User Settings Usage
One of the primary places settings are likely to be used is for filenames / folder names. How many times have you run the same program and needed to enter the same filename? Even if the name of the file is on your clipboard, it's still a pain in the ass to paste it into the input field every time you run the code. Wouldn't it be so much simpler if your program remembered the last value you entered? Well, that's exactly why this set of APIs was developed.... again it was from laziness that this capability gained life.
If you want your `Input` elements to default to an entry from your settings, then you simply set the first parameter (`default_text`) to the value of a setting from your settings file.
Let's say your layout had this typical file input row:
```python
[sg.Input(key='-IN-'), sg.FileBrowse()]
```
To automatically fill in the `Input` to be the last value entered, use this layout row:
```python
[sg.Input(sg.user_settings_get_entry('-filename-', ''), key='-IN-'), sg.FileBrowse()]
```
When your user clicks OK or closes the window in a way that is in a positive way (instead of cancelling), then add this statement to save the value.
```python
sg.user_settings_set_entry('-filename-', values['-IN-'])
```
Here's an entire program demonstrating this way of using user settings
![image](https://user-images.githubusercontent.com/46163555/96048583-cde78800-0e44-11eb-87fe-c2465e1b6cf8.png)
```python
import PySimpleGUI as sg
layout = [[sg.Text('Enter a filename:')],
[sg.Input(sg.user_settings_get_entry('-filename-', ''), key='-IN-'), sg.FileBrowse()],
[sg.B('Save'), sg.B('Exit Without Saving', key='Exit')]]
window = sg.Window('Filename Example', layout)
while True:
event, values = window.read()
if event in (sg.WINDOW_CLOSED, 'Exit'):
break
elif event == 'Save':
sg.user_settings_set_entry('-filename-', values['-IN-'])
window.close()
```
In 2 lines of code you've just made life for your user so much easier. And, by not specifying a location and name for your file, the settings are stored out of sight / out of mind. If you wanted to have the settings be stored with your program file so that it's more visible, then add this statement before your layout:
```python
sg.user_settings_filename(path='.')
```
## Example Using UserSettings Class with [ ] Syntax
The same example can be written using the `UserSettings` class and the [ ] lookup syntax.
Here's the same program as above.
```python
import PySimpleGUI as sg
settings = sg.UserSettings()
layout = [[sg.Text('Enter a filename:')],
[sg.Input(settings.get('-filename-', ''), key='-IN-'), sg.FileBrowse()],
[sg.B('Save'), sg.B('Exit Without Saving', key='Exit')]]
window = sg.Window('Filename Example', layout)
while True:
event, values = window.read()
if event in (sg.WINDOW_CLOSED, 'Exit'):
break
elif event == 'Save':
settings['-filename-'] = values['-IN-']
window.close()
```
If you were to place these 2 examples in the same file so that one ran after the other, you will find that the same settings file is used and thus the value saved in the first example will be read by the second one.
There was one additional line of code added:
```python
settings.set_default_value('') # Set the default not-found value to ''
```
Strictly speaking, this line isn't needed because the Input Element now takes `None` to be the same as a value of `''`, but to produce identical results I added this line of code.
## Demo Programs
There are a number of demo programs that show how to use UserSettings to create a richer experience for your users by remember the last value input into input elements or by adding a Combobox with a history of previously entered values. These upgrades make for a much easier to use GUI, especially when you find yourself typing in the same values or using the same files/folders.
## Brief Caution - User Settings Stick Around
If you're using the default path, remember that previous runs of your file may have old settings that are still in your settings file. It can get confusing when you've forgotten that you previously wrote a setting. Not seeing the filename can have drawbacks like this.
Also, because the settings automatically save after every update, it can be easy to accidently overwrite a previously saved setting. If you want to avoid this, then perhaps it's best that you work with a dictionary within your code and then explicitly save your dictionary when you're ready to commit it to disk.
To save your Python dictionary to a settings file, simply call `user_settings_write_new_dictionary(dict)`, passing in your dictionary as the parameter.
-------------------------
# Extending PySimpleGUI

View File

@ -13,9 +13,9 @@
| 2.7.0 | July 30, 2018 - realtime buttons, window_location default setting
| 2.8.0 | Aug 9, 2018 - New None default option for Checkbox element, text color option for all elements, return values as a dictionary, setting focus, binding return key
| 2.9.0 | Aug 16,2018 - Screen flash fix, `do_not_clear` input field option, `autosize_text` defaults to `True` now, return values as ordered dict, removed text target from progress bar, rework of return values and initial return values, removed legacy Form.Refresh() method (replaced by Form.ReadNonBlockingForm()), COLUMN elements!!, colored text defaults
| 2.10.0 | Aug 25, 2018 - Keyboard & Mouse features (Return individual keys as if buttons, return mouse scroll-wheel as button, bind return-key to button, control over keyboard focus), SaveAs Button, Update & Get methods for InputText, Update for Listbox, Update & Get for Checkbox, Get for Multiline, Color options for Text Element Update, Progess bar Update can change max value, Update for Button to change text & colors, Update for Image Element, Update for Slider, Form level text justification, Turn off default focus, scroll bar for Listboxes, Images can be from filename or from in-RAM, Update for Image). Fixes - text wrapping in buttons, msg box, removed slider borders entirely and others
| 2.11.0 | Aug 29, 2018 - Lots of little changes that are needed for the demo programs to work. Buttons have their own default element size, fix for Mac default button color, padding support for all elements, option to immediately return if list box gets selected, FilesBrowse button, Canvas Element, Frame Element, Slider resolution option, Form.Refresh method, better text wrapping, 'SystemDefault' look and feel settin
| 2.20.0 | Sept 4, 2018 - Some sizable features this time around of interest to advanced users. Renaming of the MsgBox functions to Popup. Renaming GetFile, etc, to PopupGetFile. High-level windowing capabilities start with Popup, PopupNoWait/PopupNonblocking, PopupNoButtons, default icon, change_submits option for Listbox/Combobox/Slider/Spin/, New OptionMenu element, updating elements after shown, system defaul color option for progress bars, new button type (Dummy Button) that only closes a window, SCROLLABLE Columns!! (yea, playing in the Big League now), LayoutAndShow function removed, form.Fill - bulk updates to forms, FindElement - find element based on key value (ALL elements have keys now), no longer use grid packing for row elements (a potentially huge change), scrolled text box sizing changed, new look and feel themes (Dark, Dark2, Black, Tan, TanBlue, DarkTanBlue, DarkAmber, DarkBlue, Reds, Green)
| 2.10.0 | Aug 25, 2018 - Keyboard & Mouse features (Return individual keys as if buttons, return mouse scroll-wheel as button, bind return-key to button, control over keyboard focus), SaveAs Button, Update & Get methods for InputText, Update for Listbox, Update & Get for Checkbox, Get for Multiline, Color options for Text Element Update, Progress bar Update can change max value, Update for Button to change text & colors, Update for Image Element, Update for Slider, Form level text justification, Turn off default focus, scroll bar for Listboxes, Images can be from filename or from in-RAM, Update for Image). Fixes - text wrapping in buttons, msg box, removed slider borders entirely and others
| 2.11.0 | Aug 29, 2018 - Lots of little changes that are needed for the demo programs to work. Buttons have their own default element size, fix for Mac default button color, padding support for all elements, option to immediately return if list box gets selected, FilesBrowse button, Canvas Element, Frame Element, Slider resolution option, Form.Refresh method, better text wrapping, 'SystemDefault' look and feel setting
| 2.20.0 | Sept 4, 2018 - Some sizable features this time around of interest to advanced users. Renaming of the MsgBox functions to Popup. Renaming GetFile, etc, to PopupGetFile. High-level windowing capabilities start with Popup, PopupNoWait/PopupNonblocking, PopupNoButtons, default icon, change_submits option for Listbox/Combobox/Slider/Spin/, New OptionMenu element, updating elements after shown, system default color option for progress bars, new button type (Dummy Button) that only closes a window, SCROLLABLE Columns!! (yea, playing in the Big League now), LayoutAndShow function removed, form.Fill - bulk updates to forms, FindElement - find element based on key value (ALL elements have keys now), no longer use grid packing for row elements (a potentially huge change), scrolled text box sizing changed, new look and feel themes (Dark, Dark2, Black, Tan, TanBlue, DarkTanBlue, DarkAmber, DarkBlue, Reds, Green)
| 2.30.0 | Sept 6, 2018 - Calendar Chooser (button), borderless windows, load/save form to disk
| 3.0.0 | Sept 7, 2018 - The "fix for poor choice of 2.x numbers" release. Color Chooser (button), "grab anywhere" windows are on by default, disable combo boxes, Input Element text justification (last part needed for 'tables'), Image Element changes to support OpenCV?, PopupGetFile and PopupGetFolder have better no_window option
| 3.01.01 | Sept 10, 2018 - Menus! (sort of a big deal)
@ -1270,17 +1270,383 @@ Horizontal Separator, cprint, docstrings
* Fix for removing too many PySimpleGUI installs when using the GitHub upgrade tooltip
## 4.22.0 PySimpleGUI 28-Jun-2020
More cprint stuff
* Additional window and key parameter to cprint
* May seem like a small change, but the results are powerful
* Can now easily "print" to anywhere, in color!
## 4.23.0 PySimpleGUI 3-Jul-2020
Table Colors Fix - workaround for problems with tables and tree colors in Python 3.7.2 to 3.9+
Mac crash fixed - tkinter.TclError: expected boolean value but got "" (hopefully)
New shortcut "k" parameter for all elements that is the same as "key" - may be experimental / temporary if not well received
More error checks
popup extensions
### Upcoming
* Fix for missing Table and Tree colors created in tk 8.6.9
* This is a problem in all versions of Python 3.7.2 - 3.9.0 with no target fix date published
* As a result of no fixes in sight, added a fix in PySimpleGUI if the tk version is 8.6.9
* New Element creation parameter "k" - exact same thing as "key" but shorter. Helps with complex layouts
* New error reporting on all element.update calls - checks to see if element has been fully created
* set_options - new option to supress popup_errors coming from PySimpleGUI.py
* Mac specific crash fix - if on a Mac, no longer calling wm_overrideredirect as it crashes the Mac now
* Additional error checking (shows error instead of asserting:
* Check for Widget creation before widget operations like bind, unbind, expand
* Check for window finalize / read before some window operations like maximize, hide, etc
* docstrings - more added. Fixed up a number of missing / erroneous ones
* Tree element - caches images so that will not create new ones if previously used on another Tree item
* popup - two new options
* any_key_closes - bool. If True, any key pressed will close the window
* image - can be a bytes (base64) or string (filename). Image will be shown at top of the popup
* all popups - new image parameter (base64 or string)
* a few new built-in icons
There will always be overlapping work as the ports will never actually be "complete" as there's always something new that can be built. However there's a definition for the base functionality for PySimpleGUI. This is what is being strived for with the current ports that are underway.
## 4.24.0 PySimpleGUI 3-Jul-2020
The current road ahead is to complete these ports - Qt (very close), Web (pretty close), Wx (not all that close).
Selective control over tk 8.6.9 treeview color patch
PySimpleGUIDroid is in the works....
* Disabled the code that patched the problem with background colors for Tree and Table elements
* Can enable the patched code by calling set_options
* To enable set parameter enable_treeview_869_patch = True (defaults to false)
In addition to the ports there is ongoing work with educators that want to bring PySimpleGUI into their classrooms. Some projects have already started with teachers. One effort is to examine a number of books that teach Python to kids and convert the exercises to use PySimpleGUI instead of tkinter or command line. Another educational effort is in integrating with Circuit Python. It's unclear exactly how PySimpleGUI will fit into the picture. A board from Adafruit is arriving soon which should help solidify what's possible.
## 4.25.0 PySimpleGUI 17-Jul-2020
Biggest / most impactful set of changes in a while (fingers crossed)
Modal windows
Multithreaded Window.write_event_value method
stdout re-route to any Multiline
table/tree highlights
k element parameter
* New "k" parameter for all elements.
* Same as "key"
* Created so layouts can be even more compact if desired
* New docstring for keys (basically anything except a list)
* Popups
* New text wrap behavior. Will wrap text between \n in user's string
* All popups are now "modal" unless they are non-blocking (can be turned off using new parameter)
* New button color and table/tree highlight color format
* Colors can still be tuple (text, background)
* Can also be a single string with format "text on background" (e.g. "white on red")
* Multiline
* Automatically refresh window when updating multiline or output elements
* For cprint use Multiline's autoscroll setting
* New autoscroll parameter in Multiline.print
* New parameters to make the print and cprint stuff much easier
* write_only=False (so that it's not returned when read)
* auto_refresh=False
* reroute_stdout=False
* reroute_stderr=False
* reroute_cprint=False (removes need to call the cprint cprint_set_output_destination function)
* Table / Tree Elements
* Re-enabled the tk 8.6.9 background color fix again
* selected_row_colors=(None, None) - tuple or string
* Automatically sets the selected row color based on the theme colors! (uses the button color)
* Can use 2 other constants for colors
* OLD_TABLE_TREE_SELECTED_ROW_COLORS - ('#FFFFFF', '#4A6984') the old blueish color
* ALTERNATE_TABLE_AND_TREE_SELECTED_ROW_COLORS - (SystemHighlightText, SystemHighlight)
* Tree image caching happens at the element level now
* Window
* make_modal - new method to turn a window into a modal window
* modal parameter when window is created. Default is False
* write_event_value - new method that can be called by threads! This will "queue" an event and a value for the next window.read()
* Display an error popup if read a closed window 100+ times (stops you from eating 100% of the CPU time)
* was_closed method added - returns True if a window has been closed
* Combo - don't select first entry if updated with a new set of values
* Tooltip - fix for stuck-on tooltips
* New theme_previewer with scrollbars. 3 new parameters
* cprint - now has all parameters shown in docstring versus using *args **kwargs
* New global variable __tclversion_detailed__ - string with full tkinter version (3 numbers instead of 2)
* Warning is displayed if tcl version is found to be 8.5.
## 4.26.0 PySimpleGUI 18-Jul-2020
* Multi-threaded tkvar initialization location changed so that thread doesn't intialize it now
* Removed thread key - no longer needed
* Window.write_event_values - now requires both parms
* Upgrade button typo
## 4.27.4 PySimpleGUI 3-Aug-2020
Multi-window support done right!
New capabilities for printing, Multiline
Main app additions
Theme searching
* read_all_windows - function that reads all currently open windows.
* Finally the efficient multi-window solution
* No longer need to do round-robin type scheduling
* Easily convert existing programs from single to multi-windows
* Demo programs with multi-window design patterns all updated
* Ideal for "floating palette / toolbar" window adds-ons
* Can read with timeout including timeout=0
* theme_previewer
* search option
* button in main app
* reset to previous theme following preview
* Sponsor button in main app
* Theme previewer in main app
* Progress bar
* colors can use the single string "foreground on background" color format
* update_bar combined with update for a single update interface
* Better element key error handling
* 3 options to control how lookup errors are handled
* popup now shows
* file, function, line #, actual line of code with error
* erroneous key provided
* best matching key
* will automatically try to continue with best matching key
* can assert with key error if desired (true by default)
* fix for get item
* Up/down arrow bindings for spinner if enabling events
* Multiline
* new justification parameter on creation and update
* print - justification parameter added
* cprint - justification parameter added - note tricky to set color of single word but possible
* Added mousewheel for Linux return_keyboard_events enabled
* Added get_globals function for extending easier
* Refactored callbacks
* Image element - can clear image by not setting any parameters when calling update
* Column Element's Widget member variable now being set
* Window's starting window location saved
* Early experimental "Move all windows in sync" when using grab_anywhere (coming soon)
* Fix for 3.4 (can't use f-strings)
## 4.28.0 PySimpleGUI 3-Aug-2020
Element pinning for invisibility!
* Better visible/invisible handling
* pin - new function to place an element in a layout that will hold its position
* border_width added to Canvas and Graph (so that they will default to 0)
* Combobox
* button color will match theme's button color
* background color set correctly when readonly indicated
* Spin element
* spin button color set to background color of spinner
* spin arrow color automatically set to text color
* Bad element key popup - fix for displaying correct line info in some situations
## 4.29.0 PySimpleGUI 25-Aug-2020
Custom titlebar capabilities (several new features required)
Better Alignment
Calendar button works again
* Window.visiblity_changed now refreshes the window
* Added Column.contents_changed which will update the scrollbar so corrently match the contents
* Separators expand only in 1 direction now
* Added 8 SYMBOLS:
SYMBOL_SQUARE = '█'
SYMBOL_CIRCLE = '⚫'
SYMBOL_CIRCLE_OUTLINE = '◯'
SYMBOL_UP = '▲'
SYMBOL_RIGHT = '►'
SYMBOL_LEFT = '◄'
SYMBOL_DOWN = '▼'
SYMBOL_X = '❎'
* New dark themes - dark grey 8, dark grey 9, dark green 9, dark purple 7
* When closing window no longer deletes the tkroot variable and rows but instead set to None
* Changd no-titlebar code to use try/except. Previously removed for Mac due to tk 8.6.10 errors calling wm_overrideredirect
* Fix for Column/window element justification
* New vertical_alignment parm for Column, Frame, pin
* New layout helper functions - vtop/vcenter/vbottom - Can pass an element or a row of elements
* Fixed statusbar expansion
* Added disabled button to theme previewer
* Fixed grab anywhere stop motion bug - was setting position to None and causing error changed to event.x
* Expanded main to include popup tests, theme tests, ability to hide tabs
* Grab parameter for Text Element, Column Element
* Added tclversion_detailed to get the detailed tkinter version
* All themes changed the progress bar definition that had a "DEFAULT" indicator. New constant DEFAULT_PROGRESS_BAR_COMPUTE indicates the other theme colors should be used to create the progess bar colors.
* Added expand_x and expand_y parameters to Columns
* Fix for Calendar Button. Still needs to be fixed for read_all_windows
* Force focus when no-titlebar window. Needed for Raspberry Pi
* Added Window.force_focus
* No longer closes the hidden master window. Closing it caused a memory leak within tkinter
* Disable close on one_line_progress_meter. There is a cancel button that will close the window
* Changed back toplevel to no parent - was causing problems with timeout=0 windows
## 4.30.0 PySimpleGUI 14-Oct-2020
User Settings APIs, lots more themes, theme swatch previewer, test harness additions
* Added shrink parameter to pin,
* added variable Window.maximized,
* added main_sdk_help_window function,
* New themes - DarkGrey10,DarkGrey11 DarkGrey12 DarkGrey13 DarkGrey14, Python, DarkBrown7
* Highlight Thickness for Button, Radio, Input elements
* Set to 1 now instead of 0 so that focus can be seen
* Color is automatically set for buttons, checkboxes, radio buttons
* Color can be manually set for Buttons using `highlight_colors` parameter
* Only used by Linux
* user_settings APIs
* Whole new set of API calls for handling "user settings"
* Settings are saved to json file
* For more info, see the documentation
* Radio.update - added text, background & text colors parameters
* Multiline & Output Elements:
* added parameter echo_stdout_stderr
* if True then stdout & stderr will go to the console AND to the Multiline
* "ver" is shortened version string
* modal docstring fix in some popups
* image parameter implemented in popup_scrolled
* Graph.draw_image - removed color, font, angle parameters
* fixed blank entry with main program's theme previewer
* added Window.set_min_size
* error message function for soft errors
* focus indicator for Button Checkbox Radio using highlights
* added main_sdk_help Window
* added theme_previewer_swatches function
* added "Buy Me A Coffee" button
* updated `pin` layout helper function - added `shrink` parameter
* Main debugger window set to keep on top
## 4.31.0 PySimpleGUI 13-Nov-2020
User Settings class, write_event_value fixes, Menus get colors, Mac no_titlebar patch
* InputText element - Now treating None as '' for default
* Combo - handling update calls with both disabled and readonly set
* Spin - readonly
* Added parameter added when creating
* Added parameter to update
* Spin.get() now returns value rather than string version of value
* Multiline print now autoscrolls by default
* FileSaveAs and SaveAs now has default_extension parameter like the popup_get_file has
* Button Menu - Color and font changes
* New create parameters - background color, text color, disabled text color, item font
* Fixed problem with button always being flat
* Menu (Menubar) - Color changes
* New create paramters - text color, disabled text color.
* Hooked up background color parameter that was already there but not functional
* write_event_value - fixed race conditions
* Window.read() and read_all_windows() now checks the thread queue for events before starting tkinter's mainloop in case events are queued
* Window.set_cursor added so that window's cursor can be set just like can be set for individual elements
* Icon is now set when no_window option used on popup_get_file or popup_get_folder
* Reformatted the theme definitions to save a LOT of lines of code
* UserSettings class
* Added a class interface for User Settings
* Can still use the function interface if desired
* One advantage of class is that [ ] can be used to get and set entries
* Looks and acts much like a "persistent global dictionary"
* The User Settings function interfaces now use the class
* main_get_debug_data()
* Function added that will display a popup and add to the clipboard data needed for GitHub Issues
* Added button to Test Harness to display the popup with version data
* Mac - Added parm enable_mac_notitlebar_patch to set_options to enable apply a "patch" if the Window has no_titlebar set.
## 4.32.0 PySimpleGUI 17-Nov-2020
Menu colors and font, fixes
* Menu, ButtonMenu, and right click menu now default to theme colors and Window font
* The background color for menus is the InputText background color
* The text color for menus is the InputText text color
* The font defaults to the Window font
* These theme colors have worked well in the past as they are the settings used for Table and Tree headers
* All settings can be changed
* Added ability to set the right click menu colors and font
* New parameters added to Window to control right click look
* Fixed problem with Button.update.
* Was crashing if button color changed to COLOR_SYSTEM_DEFAULT
* Fixed problem with right click menus introduced in the previous release
* Auto-close windows can now be finalized (previously could not do this)
* Window.read with timeout is faster
## 4.32.1 PySimpleGUI 17-Nov-2020
* Bug in finalize code
## 4.33.0 PySimpleGUI 2-Jan-2021 (Happy New Year!)
The let's kickoff 2021 release!
Custom Titlebars, Fix for Docstrings so PyCharm 2020 works correctly, New shortcuts, Window Close Attempted
* Custom Titlebar - new element
* Initial reason was Trinket, but great feature overall
* Allows windows to be styled to match the colors of the window
* Automatically uses theme for colors
* Automatically used when running on Trinket
* Can specify using them by using set_options, a Titlebar element, or parameters in Window creation
* Documentation is coming soonish
* Demo exists showing how to use (it's enough that you won't need a Cookbook / detailed docs to add it to your own program)
* Changes include adding a 16x16 pixel version of the PySimpleGUI icon
* popups - If custom titlebar is set using set_options (and is thus globally applied) then popups will use the custom titlebar
* MASSIVE number of changes to docstrings so that PyCharm again works correctly. All Unions had to be changed to use "|" instead
* Internal functions added to check what OS is being used rather than having os.platform checks all over thee place
* Element.Visible removed. Element.visible property returns current visibility state for element
* This is a read-only property
* Added dummy Element.update method so that PyCharm doesn't complain about a missing method
* InputElement class changed to Input. Both will work as InputElement is now an alias
* Fix in Spin.update. Was using an illegal state value of "enable" rather than "normal"
* New Shortcuts for Elements
* Sp = Spin
* SBar = StatusBar
* BM = ButtonMenu
* Progress = ProgressBar
* Im = Image
* G = Graph
* Fr = Frame
* Sl = Slider
* Button - Allow button color background to be specified as None. This will cause the theme's color to be auto chosen as background
* Image.DrawArc - fill_color parameter added
* Column - update now uses the expand information so that a column will re-pack correctly when made invisible to visible. Added fill parm to pack call
* New Window parameters:
* enable_close_attempted_event=False
* titlebar_background_color=None
* titlebar_text_color=None
* titlebar_font=None
* titlebar_icon=None
* use_custom_titlebar=None
* Removed "Faking Timeout" print because the state that triggered it can be reached using a normal auto-closing window
* Can now intercept window close attempts when the user clicks X
* If you don't want "X" to close the window, set enable_close_attempted_event to True when creating window
* When enabled a WINDOW_CLOSE_ATTEMPTED_EVENT will be returned from read instead of WIN_CLOSED
* Multiple aliases for the key: WINDOW_CLOSE_ATTEMPTED_EVENT, WIN_X_EVENT, WIN_CLOSE_ATTEMPTED_EVENT
* New Window property - key_dict
* Returns the "All keys dictionary" which has keys and elements for all elements that have keys specified
* pin helper function got 2 new parameters - expand_x=None, expand_y=None. Was needed for the Titlebar element
* no_titlebar implementation changed on Linux. Not using "splash" and instead using "dock". Was needed to get the minimize/restore to work
* New set_options parameters in support of custom Titlebar element
* use_custom_titlebar=None
* titlebar_background_color=None
* titlebar_text_color=None
* titlebar_font=None
* titlebar_icon=None
* get_globals removed - not required for normal PySimpleGUI. Was only used by patched packages which will need to fork PySimpleGUI for this feature.
* popup - support for custom titlebar!
* Changed from pathlib to os.path
## Upcoming
The future for PySimpleGUI looks bright!
The overall roadmap is a simple one:
* Continue to build-out the tkinter port
* Continue to bring features forward from the tkinter port to the other ports (Qt, WxPython, Remi)
* Add mobile applications (native built applications instead of PyDriod3 that's used today)
@ -1292,7 +1658,7 @@ In addition to the ports there is ongoing work with educators that want to bring
It's a recipe for success if done right. PySimpleGUI has completed the "Make it run" phase. It's far from "right" in many ways. These are being worked on. The module has historically been particularly poor for PEP8 compliance. It was a learning exercise that turned into a somewhat complete GUI solution for lightweight problems.
While the internals to PySimpleGUI are a tad sketchy, the public interfaces into the SDK are more strictly defined and comply with PEP8 naming conventions. A set of "PEP8 Bindings" was released in summar 2019 to ensure the externally facing interfaces all adhere to PEP8 names.
While the internals to PySimpleGUI are a tad sketchy, the public interfaces into the SDK are more strictly defined and comply with PEP8 naming conventions. A set of "PEP8 Bindings" was released in summer of 2019 to ensure the externally facing interfaces all adhere to PEP8 names.
Please log bugs and suggestions **only on the PySimpleGUI GitHub**! It will only make the code stronger and better in the end, a good thing for us all, right? Logging them elsewhere doesn't enable the core developer and other PySimpleGUI users to help. To make matters worse, you may get bad advice from other sites because there are simply not many PySimpleGUI experts, yet.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,221 @@
C:\Python\Anaconda3\python.exe C:/Users/mike/AppData/Roaming/JetBrains/PyCharmCE2020.3/scratches/scratch_1152.py
Go {'-IN-': 'C:\\Python\\PycharmProjects\\PSG\\DemoPrograms\\screenshots', 'Browse': ''}
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_All_Widgets.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_All_Widgets.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Animated_GIFs.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Animated_GIFs.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Bar_Chart.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Bar_Chart.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Base64_Image_Encoder.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Base64_Image_Encoder.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Base64_Single_Image_Encoder.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Base64_Single_Image_Encoder.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Borderless_Window.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Borderless_Window.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Buttons_Base64_Shaded.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Buttons_Base64_Shaded.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Buttons_Base64_Simple.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Buttons_Base64_Simple.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Buttons_Base64_User_Settings.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Buttons_Base64_User_Settings.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Buttons_Mac.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Buttons_Mac.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Buttons_Nice_Graphics.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Buttons_Nice_Graphics.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Button_Click.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Button_Click.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Button_Events_From_Browse.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Button_Events_From_Browse.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Button_Func_Calls.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Button_Func_Calls.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Button_States.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Button_States.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Button_Toggle.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Button_Toggle.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Calendar.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Calendar.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Canvas.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Canvas.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Change_Submits_InputText.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Change_Submits_InputText.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Chat.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Chat.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Chatterbot.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Chatterbot.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Chatterbot_With_TTS.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Chatterbot_With_TTS.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Chat_With_History.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Chat_With_History.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Color.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Color.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Color_Chooser_Custom.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Color_Chooser_Custom.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Color_Names.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Color_Names.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Color_Names_Smaller_List.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Color_Names_Smaller_List.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Color_Swatches.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Color_Swatches.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Columns.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Columns.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Column_And_Frames.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Column_And_Frames.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Column_Collapsible_Sections.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Column_Collapsible_Sections.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Column_Elem_Swap_Entire_Window.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Column_Elem_Swap_Entire_Window.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Compact_Layouts_Element_Renaming.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Compact_Layouts_Element_Renaming.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Compare_Files.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Compare_Files.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Control_Panel_Button_Grid.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Control_Panel_Button_Grid.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Conways_Game_of_Life.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Conways_Game_of_Life.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Crossword_Puzzle.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Crossword_Puzzle.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Cursor_Changed_To_Hand.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Cursor_Changed_To_Hand.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Dashboard.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Dashboard.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Date_Chooser.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Date_Chooser.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Debugger_Built_Into_PSG.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Debugger_Built_Into_PSG.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Debugger_ImWatchingYou.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Debugger_ImWatchingYou.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Patterns.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Patterns.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Multiple_Windows.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows1.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Multiple_Windows1.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows2.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Multiple_Windows2.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows3.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Multiple_Windows3.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows_Both_Visible.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Multiple_Windows_Both_Visible.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows_OLD METHOD.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Multiple_Windows_OLD METHOD.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Persistent_Window.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Persistent_Window.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Save_Theme.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Design_Pattern_Save_Theme.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Floating_Toolbar.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Floating_Toolbar.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_CPU_Dashboard.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_CPU_Dashboard.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_CPU_Gauge.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_CPU_Gauge.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_CPU_Graph.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_CPU_Graph.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_CPU_Grid_Of_Gauges.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_CPU_Grid_Of_Gauges.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_CPU_Square.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_CPU_Square.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_CPU_Top_Processes.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_CPU_Top_Processes.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_CPU_Utilization_Simple.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_CPU_Utilization_Simple.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_Drive_Usage.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_Drive_Usage.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_Email_Notification.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_Email_Notification.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_psutil_Dashboard.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_psutil_Dashboard.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_RAM_Gauge.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_RAM_Gauge.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_RAM_Square.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_RAM_Square.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_RAM_Used.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_RAM_Used.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_Timer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_Timer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Desktop_Widget_Weather.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Desktop_Widget_Weather.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Disable_Elements.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Disable_Elements.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_DuplicateFileFinder.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_DuplicateFileFinder.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Email_Send.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Email_Send.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Event_Binding.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Event_Binding.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Event_Callback_Simulation.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Event_Callback_Simulation.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_EXE_Maker.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_EXE_Maker.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Fill_Form.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Fill_Form.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Floating_Toolbar.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Floating_Toolbar.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Font_Previewer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Font_Previewer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Font_Sizer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Font_Sizer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Font_String.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Font_String.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Game_Frontend_Battleship.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Game_Frontend_Battleship.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Game_Frontend_Battleship_No_List_Comprehensions.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Game_Frontend_Battleship_No_List_Comprehensions.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Game_Frontend_Battleship_Single_List_Comprehension.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Game_Frontend_Battleship_Single_List_Comprehension.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_GitHub_File_Copier.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_GitHub_File_Copier.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_GoodColors.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_GoodColors.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_GoodColors_2.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_GoodColors_2.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Google_TTS.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Google_TTS.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Ball_Game.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Ball_Game.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Drawing.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Drawing.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Drawing_And_Dragging_Figures.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Drawing_And_Dragging_Figures.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Drawing_And_Dragging_Figures_2_Windows.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Drawing_And_Dragging_Figures_2_Windows.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Element.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Element.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Element_Sine_Wave.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Element_Sine_Wave.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Elem_Image_Album.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Elem_Image_Album.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_FourierTransform.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_FourierTransform.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_Noise.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_Noise.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_pymunk_2D_Graphics.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_pymunk_2D_Graphics.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Graph_pymunk_Desktop_Balls.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Graph_pymunk_Desktop_Balls.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Hello_World.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Hello_World.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_HowDoI.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_HowDoI.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Image_Elem_Image_Viewer_PIL_Based.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Image_Elem_Image_Viewer_PIL_Based.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Image_Elem_Splash_Screen.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Image_Elem_Splash_Screen.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Img_Viewer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Img_Viewer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Input_Auto_Complete.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Input_Auto_Complete.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Input_Validation.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Input_Validation.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Invisible_Elements.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Invisible_Elements.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Invisible_Elements_Pinning.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Invisible_Elements_Pinning.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_IP_Address_Entry.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_IP_Address_Entry.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Keyboard.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Keyboard.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Keyboard_ENTER_Presses_Button.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Keyboard_ENTER_Presses_Button.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Keyboard_Realtime.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Keyboard_Realtime.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Keypad.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Keypad.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Layout_Extend.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Layout_Extend.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Layout_Generation.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Layout_Generation.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Layout_Vertical.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Layout_Vertical.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Layout_Vertical_Centered.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Layout_Vertical_Centered.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_LED_Clock_Weather.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_LED_Clock_Weather.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_LED_Indicators.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_LED_Indicators.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_LED_Indicators_Text_Based.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_LED_Indicators_Text_Based.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Listbox_Search_Filter.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Listbox_Search_Filter.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Look_And_Feel_Theme_Browser.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Look_And_Feel_Theme_Browser.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Look_And_Feel_Theme_Dump.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Look_And_Feel_Theme_Dump.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Machine_Learning.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Machine_Learning.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Main_Control_Test_Panel.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Main_Control_Test_Panel.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Animated.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Animated.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Animated_Scatter.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Animated_Scatter.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Browser.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Browser.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Browser_Paned.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Browser_Paned.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Embedded_TEMPLATE.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Embedded_TEMPLATE.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Embedded_Toolbar.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Embedded_Toolbar.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Grid_of_Graphs_Using_PIL.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Grid_of_Graphs_Using_PIL.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_PyLab.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_PyLab.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Styles.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Styles.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Two_Windows.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Matplotlib_Two_Windows.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Media_Player.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Media_Player.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Media_Player_VLC_Based.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Media_Player_VLC_Based.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Menus.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Menus.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Menu_With_Toolbar.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Menu_With_Toolbar.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multiline_cprint_Printing.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multiline_cprint_Printing.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multiline_Multicolored_Text.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multiline_Multicolored_Text.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multiple_Windows_Experimental.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multiple_Windows_Experimental.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Animated_Shell_Command.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Animated_Shell_Command.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Different_Threads.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Different_Threads.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Logging.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Logging.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Long_Shell_Operation_Animated.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Long_Shell_Operation_Animated.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Long_Tasks.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Long_Tasks.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Long_Task_Simple.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Long_Task_Simple.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Multiple_Threads.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Multiple_Threads.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Write_Event_Value.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Write_Event_Value.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multithreaded_Write_Event_Value_MultiWindow.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Multithreaded_Write_Event_Value_MultiWindow.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Nice_Buttons.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Nice_Buttons.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_NonBlocking_Form.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_NonBlocking_Form.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Notification_Window_Alpha_Channel.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Notification_Window_Alpha_Channel.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Notification_Window_Fade_In_Out.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Notification_Window_Fade_In_Out.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_OpenCV.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_OpenCV.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_OpenCV_Webcam.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_OpenCV_Webcam.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Paned_Window.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Paned_Window.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Password_Login.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Password_Login.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Ping_Line_Graph.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Ping_Line_Graph.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Pi_LEDs.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Pi_LEDs.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Pi_Robotics.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Pi_Robotics.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_PNG_Thumbnail_Viewer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_PNG_Thumbnail_Viewer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_PNG_Viewer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_PNG_Viewer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Pong.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Pong.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Pong_Multiple_Platforms.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Pong_Multiple_Platforms.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Popups.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Popups.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Popup_Custom.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Popup_Custom.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Progress_Meters.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Progress_Meters.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_psutil_Kill_Processes.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_psutil_Kill_Processes.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_psutil_Kill_Python_Processes.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_psutil_Kill_Python_Processes.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_PyCharm_Launcher.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_PyCharm_Launcher.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_PyCharm_Self_Edit.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_PyCharm_Self_Edit.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Pyplot_Bar_Chart.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Pyplot_Bar_Chart.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Pyplot_Bar_Chart2.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Pyplot_Bar_Chart2.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Radio_Buttons_Simulated.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Radio_Buttons_Simulated.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Reddit_Search.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Reddit_Search.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Save_Window_As_Image.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Save_Window_As_Image.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Script_Launcher_ANSI_Color_Output.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Script_Launcher_ANSI_Color_Output.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Script_Launcher_Realtime_Output.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Script_Launcher_Realtime_Output.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Script_Parameters.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Script_Parameters.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Separator_Elements.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Separator_Elements.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Settings_Save_Load.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Settings_Save_Load.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Sort_Visualizer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Sort_Visualizer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Spinner_Compound_Element.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Spinner_Compound_Element.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Status_Bar.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Status_Bar.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Stdout.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Stdout.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Sudoku.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Sudoku.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Super_Simple_Form.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Super_Simple_Form.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_System_Tray_GUI_Window_Design_Pattern.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_System_Tray_GUI_Window_Design_Pattern.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_System_Tray_Icon.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_System_Tray_Icon.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_System_Tray_Reminder.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_System_Tray_Reminder.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Table_Element.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Table_Element.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Table_Simulation.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Table_Simulation.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Table_Simulation_Arrow_Keys.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Table_Simulation_Arrow_Keys.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Tabs.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Tabs.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Tabs_Nested.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Tabs_Nested.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Tabs_Simple.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Tabs_Simple.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Theme_Browser.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Theme_Browser.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Theme_Color_Swatches.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Theme_Color_Swatches.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Theme_Previewer_Dark.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Theme_Previewer_Dark.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Timer.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Timer.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Titlebar_Custom_Async.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Titlebar_Custom_Async.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Titlebar_Custom_Dark_Theme.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Titlebar_Custom_Dark_Theme.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Titlebar_Custom_Multiple_Combinations.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Titlebar_Custom_Multiple_Combinations.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Touch_Keyboard.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Touch_Keyboard.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Tree_Element.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Tree_Element.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Turtle.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Turtle.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Unicode_Characters.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Unicode_Characters.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Uno_Card_Game.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Uno_Card_Game.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_User_Settings.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_User_Settings.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_User_Settings_Browse_File_Folder.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_User_Settings_Browse_File_Folder.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_User_Settings_Class_Remember_Input_and_Combo.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_User_Settings_Class_Remember_Input_and_Combo.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_User_Settings_Remember_Input_and_Combo.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_User_Settings_Remember_Input_and_Combo.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Window_Background_Image.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Window_Background_Image.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Window_Disappear.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Window_Disappear.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Window_Open_Multiple_Times.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Window_Open_Multiple_Times.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Youtube-dl_Frontend.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_Youtube-dl_Frontend.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_YouTube_Intro.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/Demo_YouTube_Intro.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/ping.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/ping.png)
![https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/READM.py](https://raw.githubusercontent.com/Chr0nicT/PySimpleGUI/master/DemoPrograms/Markdown_Project/READM.png)

File diff suppressed because it is too large Load Diff

View File

@ -1,381 +0,0 @@
import re,datetime,time,os,platform,json,PySimpleGUI as sg; from subprocess import Popen; from make_real_readme import main
cd = os.path.dirname(os.path.abspath(__file__))
def readfile(filename):
with open(filename, 'r', encoding='utf-8') as ff: return ff.read()
def writefile(fpath, content):
with open(fpath, 'w', encoding='utf-8') as ff: ff.write(content)
def writejson(a_path:str, a_dict:dict) -> None:
with open(a_path, 'w', encoding='utf-8') as output_file: json.dump(a_dict, output_file, ensure_ascii=False, indent=2)
def readjson(a_path:str) -> dict:
with open(a_path, 'r', encoding='utf-8') as f: return json.load(f)
def openfile(a_path):
# File exists?
if not os.path.exists(a_path): return sg.Popup(f"Error! This file doesn't exists: {a_path}")
# check: OS
if 'Windows' in platform.system():
os.startfile(a_path)
elif 'Linux' in platform.system():
Popen(f'exo-open "{a_path}"', shell=True)
def opendir(a_path):
# Folder exists?
if not os.path.exists(a_path): return sg.Popup(f"Error! This directory doesn't exists: {a_path}")
# check: OS
if 'Windows' in platform.system():
os.startfile(a_path)
elif 'Linux' in platform.system():
Popen(f'exo-open --launch FileManager --working-directory "{a_path}"', shell=True)
########################################################################
# __ _ _ #
# / _(_) | | #
# __ __ ___ ___ _ __ | |_ _ __ _ | |__ ___ _ __ ___ #
# \ \ / / / __/ _ \| '_ \| _| |/ _` | | '_ \ / _ \ '__/ _ \ #
# \ V / | (_| (_) | | | | | | | (_| | | | | | __/ | | __/ #
# \_/ \___\___/|_| |_|_| |_|\__, | |_| |_|\___|_| \___| #
# __/ | #
# |___/ #
########################################################################
def load_configs(): return readjson(os.path.join(cd, 'app_configs.json'))
def save_configs(a_config:dict): writejson(os.path.join(cd, 'app_configs.json'), a_config)
APP_CONFIGS = load_configs()
README_OFILENAME = APP_CONFIGS['README_FILENAME']
CALL_REFERENCE_OFILENAME = APP_CONFIGS['CALL_REFERENCE_FILENAME']
##-#-#-# ##-#-#-#
# Post-process logic
##-#-#-# ##-#-#-#
insert_md_section_for__class_methods = False
remove_repeated_sections_classmethods = False
class BESTLOG(object):
def __init__(self, filename):
# my_file = logging.FileHandler(filename, mode='w')
# my_file.setLevel(logging.DEBUG)
# my_file.setFormatter(logging.Formatter('%(asctime)s>%(levelname)s: %(message)s'))
# logger = logging.getLogger(__name__)
# logger.setLevel(logging.DEBUG)
# logger.addHandler(my_file)
self.filename = filename
self.json_name = filename + '.json'
self.error_list = []
self.warning_list = []
self.info_list = []
self.debug_list = []
self.tick_amount=1
self.names = self.messages_names = 'error warning info debug'.split(' ')
def tick(self):
self.tick_amount+=1
return self.tick_amount
#######################################################################
# __ _ _ _ #
# / _| | | (_) | #
# | |_ ___ _ __ | |_ _ __ __ _ _ __ ___ _ __ _| | ___ _ __ #
# | _/ _ \| '__| | __| '__/ _` | '_ \/ __| '_ \| | |/ _ \ '__| #
# | || (_) | | | |_| | | (_| | | | \__ \ |_) | | | __/ | #
# |_| \___/|_| \__|_| \__,_|_| |_|___/ .__/|_|_|\___|_| #
# | | #
# |_| #
#######################################################################
def error(self, m): self.error_list.append([self.tick(), m])
def warning(self, m): self.warning_list.append([self.tick(), m])
def info(self, m): self.info_list.append([self.tick(), m])
def debug(self, m): self.debug_list.append([self.tick(), m])
##########################################
# __ #
# / _| #
# | |_ ___ _ __ _ __ ___ ___ #
# | _/ _ \| '__| | '_ ` _ \ / _ \ #
# | || (_) | | | | | | | | __/ #
# |_| \___/|_| |_| |_| |_|\___| #
# #
# #
##########################################
def tolist(self): return zip([self.error_list, self.warning_list, self.info_list, self.debug_list], self.names)
def todict(self): return {'error' : self.error_list, 'warning' : self.warning_list, 'info' : self.info_list, 'debug' : self.debug_list}
def save(self):
all_messages_list = []
for messages, message_type in self.tolist():
all_messages_list.extend([{'message_type' : message_type, 'message_text' : m_text, 'message_time' : m_time} for m_time, m_text in messages])
# sort messages on time
all_messages_list = sorted(all_messages_list, key=lambda x: x['message_time'])
# convert time
# for i in all_messages_list: i['message_time'] = i['message_time'].strftime('%Y-%m-%d %H:%M:%S.%f')
writejson(self.json_name, all_messages_list)
def load(self, **kw):
'''
return dict with messages
kw = {
use_psg_color : bool
show_time : bool
}
'''
# plan:
# read json, convert time
# read
all_messages_list = readjson(self.json_name)
# convert time
# for i in all_messages_list: i['message_time'] = datetime.datetime.strptime(i['message_time'], '%Y-%m-%d %H:%M:%S.%f')
def format_message(message):
if kw['show_time']:
return str(message['message_time']) + ':' + message['message_text']
else:
return message['message_text']
#=========#
# 4 lists #
#=========#
error_list = [i for i in all_messages_list if i['message_type'] == 'error']
warning_list = [i for i in all_messages_list if i['message_type'] == 'warning']
info_list = [i for i in all_messages_list if i['message_type'] == 'info']
debug_list = [i for i in all_messages_list if i['message_type'] == 'debug']
#=================#
# and 1 more list #
#=================#
colors = {'warning' : 'magenta', 'info' : 'black'}
warning_info_ = []
for message in sorted(warning_list + info_list, key=lambda x: x['message_time']):
if kw['use_psg_color']:
warning_info_.append([ format_message(message),
colors.get(message['message_type']) ])
else:
warning_info_.append(format_message(message))
error_list = map(format_message, error_list)
warning_list = map(format_message, warning_list)
info_list = map(format_message, info_list)
debug_list = map(format_message, debug_list)
return error_list, warning_list, info_list, debug_list, warning_info_
def compile_call_ref(output_filename='output/LoG_call_ref', **kw):
''' Compile a "5_call_reference.md" file'''
log_obj = BESTLOG(os.path.join(cd, output_filename))
main(logger=log_obj,
main_md_file='markdown input files/5_call_reference.md',
insert_md_section_for__class_methods=insert_md_section_for__class_methods,
remove_repeated_sections_classmethods=remove_repeated_sections_classmethods,
files_to_include=[],
output_name=CALL_REFERENCE_OFILENAME,
delete_html_comments=True)
log_obj.save()
return log_obj.load(**kw)
def compile_readme(output_filename='output/LoG', **kw):
''' Compile a "2_readme.md" file'''
log_obj = BESTLOG(os.path.join(cd, output_filename))
main(logger=log_obj,
insert_md_section_for__class_methods=insert_md_section_for__class_methods,
remove_repeated_sections_classmethods=remove_repeated_sections_classmethods,
files_to_include=[0, 1, 2, 3],
output_name=README_OFILENAME,
delete_html_comments=True)
log_obj.save()
return log_obj.load(**kw)
def compile_all_stuff(**kw):
'''
Compile a "2_ and 5_" .md filess
return output from them
'''
result_readme = compile_readme(**kw)
result_call_ref = compile_call_ref(**kw)
return result_readme, result_call_ref
########################################
# _____ #
# | __ \ #
# | |__) |__ _ __ _ _ _ __ #
# | ___/ _ \| '_ \| | | | '_ \ #
# | | | (_) | |_) | |_| | |_) | #
# |_| \___/| .__/ \__,_| .__/ #
# | | | | #
# |_| |_| #
########################################
def md2psg(target_text):
# target = 'This is **bold** and *italic* words'
# V
# sg.T('This is '), sg.T('bold', font=...bold), ...'
# imports
from collections import namedtuple
spec = namedtuple('spec', 'char text'.split(' '))
# START
# =====
parts = re.compile(r'([\*]{1,2})([\s\S]*?)([\*]{1,2})', flags=re.M|re.DOTALL).split(target_text)
chuncks, skip_this = [], 0
for index, part in enumerate(parts):
if skip_this != 0:
skip_this -= 1; continue
if part not in ['*', '**']: chuncks.append(part)
else:
skip_this = 2
chuncks.append(spec(part, parts[index+1]))
font_norm = ('Mono 13 ') # (*sg.DEFAULT_FONT, 'italic')
font_bold = ('Mono 13 italic') # (*sg.DEFAULT_FONT, 'italic')
font_ita = ('Mono 13 bold') # (*sg.DEFAULT_FONT, 'bold')
list_of_Ts = []
for chunck in chuncks:
if type(chunck) is str: list_of_Ts.append(sg.T(chunck, font=font_norm, size=(len(chunck), 1), pad=(0,0)))
elif type(chunck) is spec:
if chunck.char == '*': list_of_Ts.append(sg.T(chunck.text, font=font_ita, pad=(0,0), size=(len(chunck.text), 1)))
if chunck.char == '**': list_of_Ts.append(sg.T(chunck.text, font=font_bold, pad=(0,0), size=(len(chunck.text), 1)))
return list_of_Ts
def mini_GUI():
my_font = ("Helvetica", 12)
my_font2 = ("Helvetica", 12, "bold")
my_font3 = ("Helvetica", 15, "bold")
my_font4 = ("Mono", 18, "bold")
def make_tab(word):
return [[
sg.Column(layout=[
[sg.T('debug', font=my_font, text_color='blue')],
[sg.ML(size=(70-15, 15), key=f'-{word}-debug-')],
[sg.T('error', font=my_font, text_color='red')],
[sg.ML(size=(70-15, 15), key=f'-{word}-error-')],
]),
sg.T(' '), sg.Column(layout=[
[sg.T('warning', font=my_font2)],
[sg.ML(size=(70-12, 15), key=f'-{word}-warning-')],
[sg.T('info', font=my_font2)],
[sg.ML(size=(70-12, 15), key=f'-{word}-info-')],
]),
sg.Column(layout=[
[sg.T('warning_info', font=my_font3)],
[sg.ML(size=(110, 42-8), key=f'-{word}-warning_info-')],
]),
]]
layout = [
[ sg.TabGroup( [[
sg.Tab('README', make_tab('README')),
sg.Tab('CALL_REF', make_tab('CALL_REF'))
]]
)
]
]
window = sg.Window('We are live! Again! --- ' + 'Completed making {}, {}'.format(os.path.basename(README_OFILENAME), os.path.basename(CALL_REFERENCE_OFILENAME)), [
[sg.T(size=(25,1), font=my_font, key='-compile-time-')],
[sg.T(f'The PySimpleGUI module being processed is {sg}')],
[
sg.B('Run again (F1)', key='-run-')
,sg.CB('show time in logs (F2)', False, key='show_time')
,sg.CB('Logs with Color (F3)', True, key='use_psg_color')
,sg.B('open call ref', key='-open_call_ref-')
,sg.B('open readme.txt', key='-open_readme.txt-')
,sg.B('open "db folder"', key='-open_db_folder-')
,sg.T(' '*30)
,sg.Col([
# [sg.T('output name for call_ref markdown file', key=(15,1)), sg.I(key='')],
[*md2psg('markdown outputFileName *FOR* **readme **: '), sg.I(README_OFILENAME, key='md1'), sg.B('open in explorer', key='open in explorer_readme')],
[*md2psg('markdown outputFileName *FOR* **call ref**: '), sg.I(CALL_REFERENCE_OFILENAME, key='md2'), sg.B('open in explorer', key='open in explorer_calref')]
])
]
,*layout
], resizable=True, finalize=True, location=(0,0), return_keyboard_events = True)
def update_time_in_GUI(): window['-compile-time-'](datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S.%f'))
def update_compilation_in_psg(values):
# get results
result_readme, result_call_ref = compile_all_stuff(use_psg_color=values['use_psg_color'], show_time=values['show_time'])
# DO 2_readme
window['-README-error-']('\n'.join(result_readme[0]))
window['-README-warning-']('\n'.join(result_readme[1]))
window['-README-info-']('\n'.join(result_readme[2]))
window['-README-debug-']('\n'.join(result_readme[3]))
# /// colors warning_info
window['-README-warning_info-'].update('')
if values['use_psg_color']:
for text, color in result_readme[-1]:
window['-README-warning_info-'].print(text, text_color=color)
else:
window['-README-warning_info-']('\n'.join(result_readme[-1]))
# DO 5_cal_ref
window['-CALL_REF-error-']('\n'.join(result_call_ref[0]))
window['-CALL_REF-warning-']('\n'.join(result_call_ref[1]))
window['-CALL_REF-info-']('\n'.join(result_call_ref[2]))
window['-CALL_REF-debug-']('\n'.join(result_call_ref[3]))
# /// colors warning_info
window['-CALL_REF-warning_info-'].update('')
if values['use_psg_color']:
for text, color in result_call_ref[-1]:
window['-CALL_REF-warning_info-'].print(text, text_color=color)
else:
window['-CALL_REF-warning_info-']('\n'.join(result_call_ref[-1]))
# ~~~~~~~~~~~~
# GUI updating
# ~~~~~~~~~~~~
update_time_in_GUI()
update_compilation_in_psg({'use_psg_color':not False, 'show_time':False})
while True:
event, values = window()
# print(values)
if event in ('Exit', None):
APP_CONFIGS['README_FILENAME'], APP_CONFIGS['CALL_REFERENCE_FILENAME'] = window['md1'].get(), window['md2'].get()
save_configs(APP_CONFIGS)
break
print('PSG event>', event)
# buttons
if event == '-run-': update_compilation_in_psg(values)
if event == '-open_readme.txt-': openfile(README_OFILENAME)
if event == '-open_call_ref-': openfile(CALL_REFERENCE_OFILENAME)
if event == '-open_db_folder-': opendir(cd)
if event == '-open_github_gallery-': opendir(cd)
if event == 'open in explorer_readme': opendir(os.path.dirname(os.path.join(cd, values['md1'])))
if event == 'open in explorer_calref': opendir(os.path.dirname(os.path.join(cd, values['md2'])))
# hotkeys
if 'F1' in event: update_compilation_in_psg(values)
if 'F2' in event: window['show_time'](not values['show_time'])
if 'F3' in event: window['use_psg_color'](not values['use_psg_color'])
window.close()
if __name__ == '__main__':
mini_GUI()
# sg.PopupScrolled('Completed making {}'.format(README_OFILENAME), ''.join(lines), size=(80,50))

View File

@ -1,8 +1,6 @@
import PySimpleGUI as sg
import PySimpleGUI;sg = PySimpleGUI
import datetime, inspect
module_to_process = sg
"""
Create All Possible Tags
Will output to STDOUT all of the different tags for classes, members and functions for a given PySimpleGUI.py
@ -29,7 +27,7 @@ def valid_field(pair):
# # ]
psg_members = [i for i in inspect.getmembers(module_to_process) if valid_field(i)] # ]
psg_members = [i for i in inspect.getmembers(PySimpleGUI) if valid_field(i)] # ]
psg_funcs = [o[0] for o in psg_members if inspect.isfunction(o[1])] # ] Grabing PSG objects
psg_classes = [o for o in psg_members if inspect.isclass(o[1])] # ]
# psg_props = [o for o in psg_members if type(o[1]).__name__ == 'property'] # ]

1
readme_creator/start.bat Normal file
View File

@ -0,0 +1 @@
py 'RUN_ME and START_HERE.py'