PySimpleGUI Debugger! Initial checkin

This commit is contained in:
MikeTheWatchGuy 2019-05-24 08:51:17 -04:00
parent a903936436
commit bf762e12b9
3 changed files with 163 additions and 131 deletions

View File

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

View File

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

125
PSGdebugger.py Normal file
View File

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