Release 3.30. New PySimpleGUIdebugger release to PyPI
This commit is contained in:
parent
bf762e12b9
commit
7348cb67ba
8 changed files with 527 additions and 309 deletions
42
PySimpleGUIdebugger/Demo_Debugger_Integration.py
Normal file
42
PySimpleGUIdebugger/Demo_Debugger_Integration.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
import PySimpleGUI as sg
|
||||
import PySimpleGUIdebugger
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
There are THREE steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import PySimpleGUIdebugger
|
||||
2. Initialize the debugger by calling:
|
||||
PySimpleGUIdebugger.initialize()
|
||||
2. At the top of your app's event loop add
|
||||
PySimpleGUIdebugger.refresh(locals(), globals())
|
||||
"""
|
||||
|
||||
PySimpleGUIdebugger.initialize()
|
||||
|
||||
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=(50,6))],
|
||||
[sg.Ok(), sg.Exit()],
|
||||
]
|
||||
|
||||
|
||||
window = sg.Window('This is your Application Window', layout)
|
||||
window.Element('_OUT_').Update(background_color='red')
|
||||
# Variables that we'll use to demonstrate the debugger's features
|
||||
counter = 0
|
||||
timeout = 100
|
||||
|
||||
while True: # Event Loop
|
||||
PySimpleGUIdebugger.refresh(locals(), globals()) # call the debugger to refresh the items being shown
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
elif event == 'Ok':
|
||||
print('You clicked Ok.... this is where print output goes')
|
||||
counter += 1
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
125
PySimpleGUIdebugger/PySimpleGUIdebugger.py
Normal file
125
PySimpleGUIdebugger/PySimpleGUIdebugger.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()
|
121
PySimpleGUIdebugger/readme.md
Normal file
121
PySimpleGUIdebugger/readme.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
# PySimpleGUIdebugger
|
||||
|
||||
PySimpleGUI now has it's own built-in, sorta, debugger.
|
||||
|
||||
What you can do with this "debugger" is:
|
||||
* Set "watch points" that update in realtime
|
||||
* Write expressions that update in realtime
|
||||
* Use a REPL style prompt to type in "code" / modify variables
|
||||
|
||||
All of this is done using a window secondary and separate from your primary application window.
|
||||
|
||||
Check out this video as a guide. The user's window is the smaller one one top. The PySimpleGUIdebugger is the green window on the buttom. You can watch variables, evaluate expressions, even xecute code.
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
Installation is via pip:
|
||||
|
||||
`pip install PySimpleGUIdebugger`
|
||||
|
||||
or if you need to upgrade later:
|
||||
|
||||
`pip install --upgrade --no-cache-dir PySimpleGUIdebugger`
|
||||
|
||||
Should this not work, you can copy and paste the file PySimpleGUIdebugger.py into your application folder.
|
||||
|
||||
## Integrating PySimpleGUIdebugger Into Your Application
|
||||
|
||||
There are 3 lines of code to add to a PySimpleGUI program in order to make it debugger ready - The import, an initialization, once each time through the even loop.
|
||||
|
||||
Here is an entire program including this integration code:
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
import PySimpleGUIdebugger
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
There are THREE steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import PySimpleGUIdebugger
|
||||
2. Initialize the debugger by calling:
|
||||
PySimpleGUIdebugger.initialize()
|
||||
2. At the top of your app's event loop add
|
||||
PySimpleGUIdebugger.refresh(locals(), globals())
|
||||
"""
|
||||
|
||||
PySimpleGUIdebugger.initialize()
|
||||
|
||||
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=(50,6))],
|
||||
[sg.Ok(), sg.Exit()],
|
||||
]
|
||||
|
||||
|
||||
window = sg.Window('This is your Application Window', layout)
|
||||
window.Element('_OUT_').Update(background_color='red')
|
||||
# Variables that we'll use to demonstrate the debugger's features
|
||||
counter = 0
|
||||
timeout = 100
|
||||
|
||||
while True: # Event Loop
|
||||
PySimpleGUIdebugger.refresh(locals(), globals()) # call the debugger to refresh the items being shown
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
elif event == 'Ok':
|
||||
print('You clicked Ok.... this is where print output goes')
|
||||
counter += 1
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
||||
```
|
||||
|
||||
|
||||
## Using PySimpleGUIdebugger
|
||||
|
||||
To use the debugger in your code you will need to add TWO lines of code:
|
||||
The import at the top of your code:
|
||||
`import PySimpleGUIdebugger`
|
||||
|
||||
You need to "initialize" the PySimpleGUIdebugger package by calling near the top of your code. This is what creates the debugger window:
|
||||
`PySimpleGUIdebugger.initialize()`
|
||||
|
||||
This "refresh" call that must be added to your event loop. Your `window.Read` call should have a timeout value so that it does not block. If you do not have a timeout value, the debugger will not update in realtime.
|
||||
|
||||
Add this line to the top of your event loop.
|
||||
`PySimpleGUIdebugger.refresh(locals(), globals())`
|
||||
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
You'll need to have PySimpleGUI installed.
|
||||
|
||||
The debugger itself is written using PySimpleGUI, the tkinter version. It could be changed to use Qt for example.
|
||||
|
||||
|
||||
## What's it good for, when should it be used??
|
||||
|
||||
Hell if I know. Maybe it's a terrible idea! Or, maybe it'll be really helpful, particularly in situations where you don't have many resources on the target system and perhaps you can't fit a debugger onto that system. PySimpleGUIdebugger provides another tool for your PySimpleGUI GUI Toolbox.
|
||||
|
||||
|
||||
# Design
|
||||
# Author
|
||||
Mike B.
|
||||
|
||||
|
||||
# License
|
||||
GNU Lesser General Public License (LGPL 3) +
|
Loading…
Add table
Add a link
Reference in a new issue