PySimpleGUI Debugger! Initial checkin
This commit is contained in:
parent
a903936436
commit
bf762e12b9
|
@ -0,0 +1,38 @@
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
import PSGdebugger
|
||||||
|
|
||||||
|
"""
|
||||||
|
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||||
|
into your program.
|
||||||
|
There are TWO steps, and they are copy and pastes.
|
||||||
|
1. At the top of your app to debug add
|
||||||
|
import PSGdebugger
|
||||||
|
2. At the end of your app's event loop add
|
||||||
|
PSGdebugger.refresh(locals(), globals())
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
layout = [
|
||||||
|
[sg.T('A typical PSG application')],
|
||||||
|
[sg.In(key='_IN_')],
|
||||||
|
[sg.T(' ', key='_OUT_')],
|
||||||
|
[sg.Radio('a',1, key='_R1_'), sg.Radio('b',1, key='_R2_'), sg.Radio('c',1, key='_R3_')],
|
||||||
|
[sg.Combo(['c1', 'c2', 'c3'], size=(6,3), key='_COMBO_')],
|
||||||
|
[sg.Output(size=(40,6))],
|
||||||
|
[sg.Ok(), sg.Exit()],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
window = sg.Window('This is your Application Window', layout)
|
||||||
|
|
||||||
|
# Variables that we'll use to demonstrate the debugger's features
|
||||||
|
counter = 0
|
||||||
|
timeout = 100
|
||||||
|
|
||||||
|
while True: # Event Loop
|
||||||
|
event, values = window.Read(timeout=timeout)
|
||||||
|
if event in (None, 'Exit'):
|
||||||
|
break
|
||||||
|
counter += 1
|
||||||
|
window.Element('_OUT_').Update(values['_IN_'])
|
||||||
|
PSGdebugger.refresh(locals(), globals())
|
|
@ -1,131 +0,0 @@
|
||||||
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, "<ast>", "exec"), globals())
|
|
||||||
if type(last_ast.body[0]) == ast.Expr:
|
|
||||||
return eval(compile(convertExpr2Expression(last_ast.body[0]), "<ast>", "eval"),globals())
|
|
||||||
else:
|
|
||||||
exec(compile(last_ast, "<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()
|
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
The Offiicial Unofficiall official PySimpleGUI debug tool
|
||||||
|
Not calling it a debugger, but it is also quite a step up from "print statemements"
|
||||||
|
"""
|
||||||
|
PSGDebugLogo = b'R0lGODlhMgAtAPcAAAAAADD/2akK/4yz0pSxyZWyy5u3zZ24zpW30pG52J250J+60aC60KS90aDC3a3E163F2K3F2bPI2bvO3rzP3qvJ4LHN4rnR5P/zuf/zuv/0vP/0vsDS38XZ6cnb6f/xw//zwv/yxf/1w//zyP/1yf/2zP/3z//30wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAyAC0AAAj/AP8JHEiwoMGDCBMqXMiwoUOFAiJGXBigYoAPDxlK3CigwUGLIAOEyIiQI8cCBUOqJFnQpEkGA1XKZPlPgkuXBATK3JmRws2bB3TuXNmQw8+jQoeCbHj0qIGkSgNobNoUqlKIVJs++BfV4oiEWalaHVpyosCwJidw7Sr1YMQFBDn+y4qSbUW3AiDElXiWqoK1bPEKGLixr1jAXQ9GuGn4sN22Bl02roo4Kla+c8OOJbsQM9rNPJlORlr5asbPpTk/RP2YJGu7rjWnDm2RIQLZrSt3zgp6ZmqwmkHAng3ccWDEMe8Kpnw8JEHlkXnPdh6SxHPILaU/dp60LFUP07dfRq5aYntohAO0m+c+nvT6pVMPZ3jv8AJu8xktyNbw+ATJDtKFBx9NlA20gWU0DVQBYwZhsJMICRrkwEYJJGRCSBtEqGGCAQEAOw=='
|
||||||
|
|
||||||
|
COLOR_SCHEME = 'LightGreen'
|
||||||
|
|
||||||
|
WIDTH_VARIABLES = 12
|
||||||
|
WIDTH_RESULTS = 36
|
||||||
|
|
||||||
|
# done purely for testing / show
|
||||||
|
def func(x=''):
|
||||||
|
return f'return value from func()={x}'
|
||||||
|
|
||||||
|
|
||||||
|
def _init():
|
||||||
|
global watcher_window
|
||||||
|
sg.ChangeLookAndFeel(COLOR_SCHEME)
|
||||||
|
def InVar(key1, key2):
|
||||||
|
row1 = [sg.T(' '),
|
||||||
|
sg.I(key=key1, size=(WIDTH_VARIABLES,1)),
|
||||||
|
sg.T('',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.T('',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=(83,1), key='_INTERACTIVE_'), sg.B('Go', bind_return_key=True, visible=False)],
|
||||||
|
[sg.Multiline(size=(88,12),key='_OUTPUT_',autoscroll=True, do_not_clear=True)],]
|
||||||
|
|
||||||
|
layout = [ [sg.Frame('Variables or Expressions to Watch', variables_frame, )],
|
||||||
|
[sg.Frame('REPL-Light', interactive_frame,)],
|
||||||
|
[sg.Button('Exit')]]
|
||||||
|
|
||||||
|
window = sg.Window('PySimpleGUI Debugger', layout, icon=PSGDebugLogo).Finalize()
|
||||||
|
window.Element('_INTERACTIVE_').SetFocus()
|
||||||
|
watcher_window = window
|
||||||
|
sg.ChangeLookAndFeel('SystemDefault')
|
||||||
|
|
||||||
|
return window
|
||||||
|
|
||||||
|
def _event_once(_window, mylocals, myglobals):
|
||||||
|
_window = watcher_window
|
||||||
|
global myrc
|
||||||
|
_event, _values = _window.Read(timeout=100)
|
||||||
|
if _event in (None, 'Exit'):
|
||||||
|
return False
|
||||||
|
cmd = _values['_INTERACTIVE_']
|
||||||
|
if _event == 'Run':
|
||||||
|
_runCommand(cmd=cmd, window=_window)
|
||||||
|
elif _event == 'Go':
|
||||||
|
_window.Element('_INTERACTIVE_').Update('')
|
||||||
|
_window.Element('_OUTPUT_').Update(">>> {}\n".format(cmd), append=True, autoscroll=True)
|
||||||
|
expression = """
|
||||||
|
global myrc
|
||||||
|
PSGdebugger.myrc = {} """.format(cmd)
|
||||||
|
try:
|
||||||
|
exec(expression, myglobals, mylocals)
|
||||||
|
_window.Element('_OUTPUT_').Update('{}\n'.format(myrc),append=True, autoscroll=True)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
_window.Element('_OUTPUT_').Update('Exception {}\n'.format(e),append=True, autoscroll=True)
|
||||||
|
|
||||||
|
elif _event.endswith('_DETAIL_'):
|
||||||
|
expression = """
|
||||||
|
global myrc
|
||||||
|
PSGdebugger.myrc = {} """.format(_values[f'_VAR{_event[4]}_'])
|
||||||
|
try:
|
||||||
|
exec(expression, myglobals, mylocals)
|
||||||
|
sg.PopupScrolled(myrc)
|
||||||
|
except:
|
||||||
|
print('Detail failed')
|
||||||
|
|
||||||
|
# -------------------- Process the "watch list" ------------------
|
||||||
|
for i in range(1, 7):
|
||||||
|
key = f'_VAR{i}_'
|
||||||
|
out_key = f'_VAR{i}_CHANGED_'
|
||||||
|
myrc =''
|
||||||
|
if _window.Element(key):
|
||||||
|
if _values[key]:
|
||||||
|
expression = """
|
||||||
|
global myrc
|
||||||
|
PSGdebugger.myrc = {} """.format(_values[key])
|
||||||
|
try:
|
||||||
|
exec(expression, myglobals, mylocals)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
_window.Element(out_key).Update(myrc)
|
||||||
|
else:
|
||||||
|
_window.Element(out_key).Update('')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def refresh(locals, globals):
|
||||||
|
global watcher_window
|
||||||
|
_event_once(watcher_window, locals, globals)
|
||||||
|
|
||||||
|
myrc = ''
|
||||||
|
watcher_window = _init()
|
Loading…
Reference in New Issue