PySimpleGUI/readme_creator/RUN_ME and START_HERE.py

693 lines
24 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()