diff --git a/DemoPrograms/Demo_Python_REPL_Variable_Watches_In_GUI.py b/DemoPrograms/Demo_Python_REPL_Variable_Watches_In_GUI.py new file mode 100644 index 00000000..c1eb9d7c --- /dev/null +++ b/DemoPrograms/Demo_Python_REPL_Variable_Watches_In_GUI.py @@ -0,0 +1,131 @@ +import subprocess +import sys +import ast +import copy +import PySimpleGUI as sg + +""" + Python repl with a "watch list" + This Demo Program was created as an answer to a Reddit question. + The idea is to build a Python interpreter >>> prompt, accept command and execute them... and + to also inslude a watcher feature. The "Watched" variables or expressions are constantly updated + as the proram runs. + At the moment, the event loop runs once a second. It could easily be shortened if that's too slow +""" + +WIDTH_VARIABLES = 12 +WIDTH_RESULTS = 16 + +def convertExpr2Expression(Expr): + Expr.lineno = 0 + Expr.col_offset = 0 + result = ast.Expression(Expr.value, lineno=0, col_offset = 0) + + return result + +def exec_with_return(code): + code_ast = ast.parse(code) + + init_ast = copy.deepcopy(code_ast) + init_ast.body = code_ast.body[:-1] + + last_ast = copy.deepcopy(code_ast) + last_ast.body = code_ast.body[-1:] + + exec(compile(init_ast, "", "exec"), globals()) + if type(last_ast.body[0]) == ast.Expr: + return eval(compile(convertExpr2Expression(last_ast.body[0]), "", "eval"),globals()) + else: + exec(compile(last_ast, "", "exec"),globals()) + + +def func(x=''): + return f'return value from func()={x}' + + +def init(): + def InVar(key1, key2): + row1 = [sg.T(' '), + sg.I(key=key1, size=(WIDTH_VARIABLES,1)), + sg.I(key=key1+'CHANGED_', size=(WIDTH_RESULTS,1)),sg.B('Detail', key=key1+'DETAIL_'), sg.T(' '), + sg.T(' '), sg.I(key=key2, size=(WIDTH_VARIABLES, 1)), sg.I(key=key2 + 'CHANGED_', size=(WIDTH_RESULTS, 1)), sg.B('Detail', key=key2+'DETAIL_'),] + return row1 + + variables_frame = [ InVar('_VAR1_', '_VAR2_'), + InVar('_VAR3_', '_VAR4_'), + InVar('_VAR5_', '_VAR6_'),] + + interactive_frame = [[sg.T('>>> '), sg.In(size=(70,1), key='_INTERACTIVE_'), sg.B('Go', bind_return_key=True, visible=False)], + [sg.Output(size=(70,8))],] + + layout = [ [sg.Frame('Variables or Expressions to Watch', variables_frame)], + [sg.Frame('Interactive REPL', interactive_frame)], + [sg.Button('Exit')]] + + window = sg.Window('Realtime REPL Command Output + Watches', layout).Finalize() + window.Element('_INTERACTIVE_').SetFocus() + return window + # event_loop(window) + +event_once = lambda window, var=0 : exec(""" + +# while True: # Event Loop +event, values = window.Read(timeout=100) +print(event, values) if event != sg.TIMEOUT_KEY else None +if event in (None, 'Exit'): + False +cmd = values['_INTERACTIVE_'] +if event == 'Run': + runCommand(cmd=cmd, window=window) +elif event == 'Go': + window.Element('_INTERACTIVE_').Update('') + out='' + print(">>> ", cmd) + try: + print(exec_with_return(cmd)) + except Exception as e: + print(f'Exception on output {e}') +elif event.endswith('_DETAIL_'): + try: sg.PopupScrolled(eval(values[f'_VAR{event[4]}_'])) + except: pass +# -------------------- Process the "watch list" ------------------ +for i in range(1, 6): + key = f'_VAR{i}_' + out_key = f'_VAR{i}_CHANGED_' + if window.Element(key): + if values[key]: + try: + window.Element(out_key).Update(eval(values[key])) + except: + window.Element(out_key).Update('') +var += 1 +True +""") + +def runCommand(cmd, timeout=None, window=None): + """ run shell command + @param cmd: command to execute + @param timeout: timeout for command execution + @param window: the PySimpleGUI window that the output is going to (needed to do refresh on) + @return: (return code from command, command output) + """ + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + output = '' + for line in p.stdout: + line = line.decode(errors='replace' if (sys.version_info) < (3, 5) else 'backslashreplace').rstrip() + output += line + print(line) + window.Refresh() if window else None # yes, a 1-line if, so shoot me + + retval = p.wait(timeout) + return (retval, output) + +if __name__ == '__main__': + window = init() + my_variable=1000 + while True: + event_once(window) + # if not event_once(window): + # break + window.Close() +