PySimpleGUI/DemoPrograms/Demo_Demo_Programs_Browser.py

248 lines
18 KiB
Python
Raw Normal View History

import os.path
import subprocess
import sys
import PySimpleGUI as sg
"""
Demo Browser
Makes using the Demo Programs each even more powerful.
Now you can quickly filter and search to find the demo you need.
You can launch editing the demo using your favorite editor.
On first run, click settings button to enter location of your demo programs.
Filter the list of demos using:
* Search using filename
* Searching within the demo program's source code (like grep)
The basic file operations are
* Edit a file in your editor
* Run a file
* Filter file list
* Search in files
Additional operations
* Edit this file in editor
Copyright 2021 PySimpleGUI.org
"""
def get_demo_git_files():
"""
Get the files in the demo and the GitHub folders
Returns files as 2 lists
:return: two lists of files
:rtype: Tuple[List[str], List[str]]
"""
demo_path = sg.user_settings_get_entry('-demos folder-', '.')
try:
demo_files = os.listdir(demo_path)
except:
demo_files = []
return demo_files
def find_in_file(string):
"""
Search through the demo files for a string.
The case of the string and the file contents are ignored
:param string: String to search for
:return: List of files containing the string
:rtype: List[str]
"""
demo_path = sg.user_settings_get_entry('-demos folder-')
demo_files = get_demo_git_files()
string = string.lower()
file_list = []
for file in demo_files:
filename = os.path.join(demo_path, file)
try:
with open(filename, 'r') as f:
for line in f.readlines():
if string in line.lower():
file_list.append(file)
# print(f'{os.path.basename(file)} -- {line}')
except Exception as e:
pass
# print(e)
return list(set(file_list))
def settings_window():
"""
Show the settings window.
This is where the folder paths and program paths are set.
Returns True if settings were changed
:return: True if settings were changed
:rtype: (bool)
"""
layout = [[sg.T('Program Settings', font='DEFAIULT 18')],
[sg.T('Path to Demos', size=(20,1)), sg.In(sg.user_settings_get_entry('-demos folder-', '.'), k='-DEMOS-'), sg.FolderBrowse()],
[sg.T('Editor Program', size=(20,1)), sg.In(sg.user_settings_get_entry('-Editor Program-', ''),k='-EDITOR PROGRAM-'), sg.FileBrowse()],
[sg.T(r"For PyCharm, Add this to your PyCharm main program's folder \bin\pycharm.bat")],
[sg.Combo(sg.theme_list(), sg.user_settings_get_entry('-theme-', None), k='-THEME-')],
[sg.B('Ok', bind_return_key=True), sg.B('Cancel')],
]
window = sg.Window('Settings', layout)
event, values = window.read(close=True)
if event == 'Ok':
sg.user_settings_set_entry('-demos folder-', values['-DEMOS-'])
sg.user_settings_set_entry('-Editor Program-', values['-EDITOR PROGRAM-'])
sg.user_settings_set_entry('-theme-', values['-THEME-'])
return True
return False
# --------------------------------- Create the window ---------------------------------
def make_window():
"""
Creates the main window
:return: The main window object
:rtype: (Window)
"""
theme = sg.user_settings_get_entry('-theme-')
demo_files = get_demo_git_files()
if not theme:
theme = sg.OFFICIAL_PYSIMPLEGUI_THEME
sg.theme(theme)
# First the window layout...2 columns
find_tooltip = "Find in file\nEnter a string in box to search for string inside of the files.\nFile list will update with list of files string found inside."
filter_tooltip = "Filter files\nEnter a string in box to narrow down the list of files.\nFile list will update with list of files with string in filename."
ML_KEY = '-ML-' # Multline's key
left_col = [
[sg.Listbox(values=demo_files, select_mode=sg.SELECT_MODE_EXTENDED, size=(40, 20), key='-DEMO LIST-')],
2021-01-24 18:08:24 +00:00
[sg.Text('Filter:', tooltip=filter_tooltip), sg.Input(size=(25, 1), enable_events=True, key='-FILTER-', tooltip=filter_tooltip),
sg.T(size=(20,1), k='-FILTER NUMBER-')],
[sg.Button('Run'), sg.B('Edit'), sg.B('Clear')],
2021-01-24 18:08:24 +00:00
[sg.Text('Find:', tooltip=find_tooltip), sg.Input(size=(25, 1), enable_events=True, key='-FIND-', tooltip=find_tooltip),
sg.T(size=(20,1), k='-FIND NUMBER-')]]
right_col = [
[sg.Multiline(size=(70, 21), write_only=True, key=ML_KEY, reroute_stdout=True, echo_stdout_stderr=True)],
[sg.Button('Edit Me (this program)'), sg.B('Settings'), sg.Button('Exit')],
[sg.T('PySimpleGUI ver ' + sg.version.split(' ')[0] + ' tkinter ver ' + sg.tclversion_detailed, font='Default 8', pad=(0,0))],
]
# ----- Full layout -----
layout = [[sg.Text('Demo Programs Browser', font='Any 20')],
[sg.T('Click settings to specify location of demo files')],
sg.vtop([sg.Column(left_col, element_justification='c'),sg.Col(right_col, element_justification='c') ])]
# --------------------------------- Create Window ---------------------------------
window = sg.Window('PySimpleGUI Demo Program Browser', layout, icon=icon)
sg.cprint_set_output_destination(window, ML_KEY)
return window
# --------------------------------- Main Program Layout ---------------------------------
def main():
"""
The main program that contains the event loop.
It will call the make_window function to create the window.
"""
demo_path = sg.user_settings_get_entry('-demos folder-', '.')
editor_program = sg.user_settings_get_entry('-Editor Program-', '')
demo_files = get_demo_git_files()
window = make_window()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED or event == 'Exit':
break
if event == 'Edit':
for file in values['-DEMO LIST-']:
sg.cprint(f'opening (in editor)', text_color='white', background_color='red', end='')
sg.cprint('')
sg.cprint(f'{os.path.join(demo_path, file)}', text_color='white', background_color='purple')
execute_command_subprocess(f'{editor_program}', os.path.join(demo_path, file))
elif event == 'Run':
sg.cprint('Running Demo Programs....', c='white on green', end='')
sg.cprint('')
for file in values['-DEMO LIST-']:
sg.cprint(os.path.join(demo_path, file),text_color='white', background_color='purple')
run_py(os.path.join(demo_path, file))
elif event.startswith('Edit Me'):
sg.cprint(f'opening using {editor_program}\nThis file - {__file__}', text_color='white', background_color='green', end='')
execute_command_subprocess(f'{editor_program}', __file__)
elif event == '-FILTER-':
new_list = [i for i in demo_files if values['-FILTER-'].lower() in i.lower()]
window['-DEMO LIST-'].update(new_list)
2021-01-24 18:08:24 +00:00
window['-FILTER NUMBER-'].update(f'{len(new_list)} files')
window['-FIND NUMBER-'].update('')
elif event == '-FIND-':
file_list = find_in_file(values['-FIND-'])
window['-DEMO LIST-'].update(sorted(file_list))
2021-01-24 18:08:24 +00:00
window['-FIND NUMBER-'].update(f'{len(file_list)} files')
window['-FILTER NUMBER-'].update('')
elif event == 'Settings':
if settings_window() is True:
window.close()
window = make_window()
demo_path = sg.user_settings_get_entry('-demos folder-')
editor_program = sg.user_settings_get_entry('-Editor Program-')
demo_files = get_demo_git_files()
elif event == 'Clear':
window['-FILTER-'].update('')
window['-FIND-'].update('')
window['-DEMO LIST-'].update(demo_files)
2021-01-24 18:08:24 +00:00
window['-FILTER NUMBER-'].update('')
window['-FIND NUMBER-'].update('')
window.close()
def run(app_name, parm=''):
execute_command_subprocess(app_name, parm)
def run_py(pyfile, parms=None):
if parms is not None:
execute_command_subprocess('python', pyfile, parms)
else:
execute_command_subprocess('python', pyfile)
def execute_command_subprocess(command, *args, wait=False):
if sys.platform == 'linux':
arg_string = ''
for arg in args:
arg_string += ' ' + str(arg)
sp = subprocess.Popen(['python3' + arg_string, ], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
else:
expanded_args = ' '.join(args)
sp = subprocess.Popen([command, expanded_args], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if wait:
out, err = sp.communicate()
if out:
print(out.decode("utf-8"))
if err:
print(err.decode("utf-8"))
if __name__ == '__main__':
icon = b'iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAAsSAAALEgHS3X78AAAbWElEQVR4nO3d/XEUx7rA4elb/h9uBOAIkCMARwAnAkQExhEYR2AcAXIEhggQERwRwYEIDkTQpwa37EWspP2Yj7e7n6dKZR9X3ctqdtn+TU9PT8o5D0tLKT0ahuHt4n8wwLSe5ZzPHFNq9H/eNYCDvUopnTp81EgAABxHBFAlAQBwPBFAdQQAwDREAFURAADTEQFUQwAATEsEUAUBADA9EUB4AgBgHiKA0AQAwHxEAGEJAIB5iQBCEgAA8xMBhCMAAJYhAghFAAAsRwQQhgAAWJYIIAQBALA8EcDqBADAOkQAqxIAAOsRAaxGAACsSwSwCgEAsD4RwOIEAEAMIoBFCQCAOEQAixEAALGIABYhAADiEQHMTgAAxCQCmJUAAIhLBDAbAQAQmwhgFgIAID4RwOQEAEAdRACTEgAA9RABTEYAANRFBDAJAQBQHxHA0QQAQJ1EAEcRAAD1EgEc7DuHbvgxwGsA1nMyDMNvFR//MQKGnPNZgNdCRboPgJzzeYCXAaznPKX0aRxIK34PRAB7cwkA6F4ZOJ9VfhxcDmAvAgBABNAhAQBQiAB6IgAANogAeiEAAK4QAfRAAABsIQJonQAAuIYIoGUCAOAGIoBWCQCAW4gAWiQAAHYgAmiNAADYkQigJQIAYA8igFYIAIA9iQBaIAAADiACqJ0AADiQCKBmAgDgCCKAWgkAgCOJAGokAAAmIAKojQAAmIgIoCYCAGBCIoBaCACAiYkAaiAAAGYgAohOAADMRAQQmQAAmJEIICoBADAzEUBEAgBgASKAaAQAwEJEAJEIAIAFiQCiEAAACxMBRCAAAFYgAlibAABYiQhgTQIAYEUigLUIAICViQDWIAAAAhABLE0AAAQhAliSAAAIRASwFAEAEIwIYAkCACAgEcDcBABAUCKAOQkAgMBEAHMRAADBiQDmIAAAKiACmJoAAKiECGBKAgCgIiKAqQgAgMqIAKYgAAAqJAI4lgAAqJQI4BgCAKBiIoBDCQCAyokADiEAABogAtiXAABohAhgHwIAoCEigF0JAIDGiAB2IQAAGiQCuI0AAGiUCOAmAgCgYSKA6wgAgMaJALYRAAAdEAFcJQAAOiEC2CQAADoiArgkAAA6IwIYBABAn0QAAgCgUyKgbwIAoGMioF8CAKBzIqBPAgAAEdAhAQDAFyKgLwIAgL+JgH4IAAC+IgL6IAAA+IYIaJ8AAGArEdA2AQDAtURAuwQAADcSAW0SAADcSgS0RwAAsBMR0BYBAMDOREA7BAAAexEBbRAAAOxNBNRPAABwEBFQNwEAwMFEQL0EAABHEQF1EgAAHE0E1EcAADAJEVAXAQDAZERAPQQAAJMSAXUQAABMTgTEJwAAmIUIiE0AADAbERCXAABgViIgJgEAwOxEQDwCAIBFiIBYBAAAixEBcQgAABYlAmIQAAAsTgSsTwAAsAoRsC4BAMBqRMB6BAAAqxIB6xAAAKxOBCxPAAAQgghYlgAAIAwRsBwBAEAoImAZAgCAcETA/AQAACGJgHkJAADCEgHzEQAAhCYC5iEAAAhPBExPAABQBREwLQEAQDVEwHQEAABVEQHT+G7NPxx6k1K6PwzD5s/dYRhONg7D+O939jws74dh+FT+/UP5Gf/3xfjPnPOFDxqtGSMgpTT+Vq8q/tXGCLgMmsWlnPPyf2hKj4ZheLvGL3xVzjlFeB20pQz0Jxs/4/9+sOIv+bGEwXn554UwoAXlLLrmCBj9sMbfRzMAMIEStZc/h5zFz+1e+Xl4+eeUs6d3JQrGL5/znPOntV4gHKKRmYC7a/yhAgAOkFIaB/knZcB/WPExfHglCt6XIHidcz5f96XBbhqJgMUJANhBSuluGeyflJ9oZ/hTeVB+fkopfb6MgRIEZgcISwTsz10AcIOU0pOU0jgA/ncYhj+HYXja8OB/1fh7Pi5fqP8dj8N4vbXEEITTyN0BixEAcMU4vZ9SGs8mPpVB/7Fj9MVmDJyVdQ8QigjYnQCAMsWfUnqeUhpXyP+7szP9Q4zH5+14vMpxMytAGCJgNwKAro23641ns+XWuN/KSnl2d68ct8tZgRPHjghEwO0EAF0ap6/LwP8fZ/uTGY/jv1NK5y4PEIEIuJkAoCtl4D8vG1E99e7P4mG5PCAEWJ0IuJ4AoAtXBv6a79uviRAgBBGwnQCgaRvX+A3869kMgfu9HgTWJQK+JQBoUlnV/2LjGj/rG0PgP2WxoLsGWJwI+JoAoDnl4SDjqv5fvLshjUH25fbB3g8EyxMB/xAANKNM95+XzWqs6o9tfH9+SylduHWQpYmAvwgAmrAx3e86f10elFsHX/R+IFjWWs/gj0QAULWybe+F6f7q/WI2AJYlAKhWuYb873IWSf3MBsCCBADVKSv8z8sWtLTncjbALYMwIwFAVcqGMh9c62/eOBswRsCT3g8EzEUAUI0yNfzWCv9ujO/znymll70fCJiDACC8MuX/2kK/bv1UdhG0eRBMSAAQWrkOPF7vf+yd6trDcknAXQIwEQFAWOXL/sIqf4p7Ywx6sBBMQwAQUtnO99z1fq64Ux4sdOrAwHEEAOGUL3fb+XKTV54lAMcRAIRSvtRfeVfYwW/lUc/AAb5z0IiifJl7dC/7eJpSGvd1d0kA9mQGgBAM/hzhqZkA2J8AYHUGfyYgAmBPAoBVGfyZkAiAPQgAVlMW/Bn8mZIIgB0JAFZRbvXzND/m8NQ+AXA7dwGwuI37/PnHu/Jv486Hnzb++/mWY7S5E964P/7l9riekPiPV+XuALMBcA0BwKLK9r49P93tfRnUP5TB/iLn/GmH/7tN26Lgb2Wr3PslDE46DoMxAsbjexHgtUA4AoDFlKe59ba977vyO5/nnG8cuKey7c8pUXD501MQfHl2gAiAbwkAFtHZ4P9mGIbx8cWvDzi7n0WJgi9hUN6LMQSelJ+W35PxdzsrERDivYAoBABLedn4U/3CDfrXKa/vdfm5XJPxpOFHLo+fu7PyOwKFuwCYXcO3+30ehuHXYRi+zzk/GRec1XiWWV73ODh+X36fjwFe1tQep5RetPUrwXEEALMqi/5au91vHCCf5Zzv5pxf5Jw/BHhNRxt/j/L7jAsInzUYAr+UtRDQvUEAMKdyrfl1Qwf5cuC/3/rtZWVWYAyBf5U7F1rxunwuoXsCgDmNg+S9Bo5wNwP/VTnncU3DSUMzAncai1I4mABgFuW6f+2Lyi6v8Z/0vqHMxozAr+W41Oxh+XxC1wQAk0spjQNF7Quu3pSB/4Xbx/4xHo+yydCbKK/pQL+V9SnQLQHAHM4qvrd8PLv9V1nV38TivqmNQVTuGvix8ssCtgmmawKASZWp1Vp3mhvPasfr/K4R76BsLnRS8WzAA7cG0jMBwGQqnvofz/p/Lmf9pvv3sDEb8KzStQG/lM8tdEcAMKWXFU79j1PY4zaxPT+g6GhlkeSjSm8ZdCmALgkAJpFSqnEr2XdloZ8HxUygHMcxAv6o7KU/LJ9f6IoAYCq1nUH/kXP2gJiJlUsCp+V2wZq8tEEQvREAHK0spKppw59nZZBiJuV2wWcVHd/x82tvALoiADhKOWuq6YvzWe+b+iylHOeaFgdaEEhXBADHqmnhn8F/YRuLA2uJALcF0g0BwMHK2VItj/k1+K9kY3FgDRHw1CwAvRAAHKOWsyWD/8oqiwCfFbogADhIRWf/Bv8gSgTUsF5kvC3wUYDXAbMSAByqhrP/nw3+sWwsDIzOHQE0TwCwt0rO/v+wu19MJQJ+D/4yH1sLQOsEAIeIfvb/zn3+seWcn1fwECF3BNA0AcBeyn3/kc/+x739betah9Pgzw5wRwBNEwDsK/q1UU/0q0R5n06D3xlgJolmCQD2FfkL8WcP9qlLeb8iT7VbDEizBAA7SymdBt7z/51Ff3Uq71vU9QB3yucemiMA2EfUL8LPpmqrF/lSgM8WTRIA7KQshnoY9Gi9yDl/CPA6ONDGeoCIHloMSIsEALuK+uVs6r8ROefX4/sZ9LexFoDmCA
main()