PySimpleGUI Debugger! Initial checkin
This commit is contained in:
		
							parent
							
								
									a903936436
								
							
						
					
					
						commit
						bf762e12b9
					
				
					 3 changed files with 163 additions and 131 deletions
				
			
		
							
								
								
									
										38
									
								
								DemoPrograms/Demo_Debugger_Integration.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								DemoPrograms/Demo_Debugger_Integration.py
									
										
									
									
									
										Normal 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()) | ||||||
|  | @ -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
									
								
							
							
						
						
									
										125
									
								
								PSGdebugger.py
									
										
									
									
									
										Normal 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() | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue