add rand_color

This commit is contained in:
jdegenstein
2022-10-28 22:02:32 -05:00
committed by GitHub
parent fefb57c2b9
commit 73a5825f3f

View File

@@ -1,365 +1,386 @@
import sys import sys
from contextlib import ExitStack, contextmanager from contextlib import ExitStack, contextmanager
from enum import Enum, auto from enum import Enum, auto
from types import SimpleNamespace, FrameType, ModuleType from types import SimpleNamespace, FrameType, ModuleType
from typing import List from typing import List
import cadquery as cq import cadquery as cq
from PyQt5 import QtCore from PyQt5 import QtCore
from PyQt5.QtCore import Qt, QObject, pyqtSlot, pyqtSignal, QEventLoop, QAbstractTableModel from PyQt5.QtCore import Qt, QObject, pyqtSlot, pyqtSignal, QEventLoop, QAbstractTableModel
from PyQt5.QtWidgets import (QAction, from PyQt5.QtWidgets import (QAction,
QTableView) QTableView)
from logbook import info from logbook import info
from path import Path from path import Path
from pyqtgraph.parametertree import Parameter from pyqtgraph.parametertree import Parameter
from spyder.utils.icon_manager import icon from spyder.utils.icon_manager import icon
from random import randrange as rrr, seed
from ..cq_utils import find_cq_objects, reload_cq
from ..mixins import ComponentMixin from ..cq_utils import find_cq_objects, reload_cq
from ..mixins import ComponentMixin
DUMMY_FILE = '<string>'
DUMMY_FILE = '<string>'
class DbgState(Enum):
class DbgState(Enum):
STEP = auto()
CONT = auto() STEP = auto()
STEP_IN = auto() CONT = auto()
RETURN = auto() STEP_IN = auto()
RETURN = auto()
class DbgEevent(object):
class DbgEevent(object):
LINE = 'line'
CALL = 'call' LINE = 'line'
RETURN = 'return' CALL = 'call'
RETURN = 'return'
class LocalsModel(QAbstractTableModel):
class LocalsModel(QAbstractTableModel):
HEADER = ('Name','Type', 'Value')
HEADER = ('Name','Type', 'Value')
def __init__(self,parent):
def __init__(self,parent):
super(LocalsModel,self).__init__(parent)
self.frame = None super(LocalsModel,self).__init__(parent)
self.frame = None
def update_frame(self,frame):
def update_frame(self,frame):
self.frame = \
[(k,type(v).__name__, str(v)) for k,v in frame.items() if not k.startswith('_')] self.frame = \
[(k,type(v).__name__, str(v)) for k,v in frame.items() if not k.startswith('_')]
def rowCount(self,parent=QtCore.QModelIndex()):
def rowCount(self,parent=QtCore.QModelIndex()):
if self.frame:
return len(self.frame) if self.frame:
else: return len(self.frame)
return 0 else:
return 0
def columnCount(self,parent=QtCore.QModelIndex()):
def columnCount(self,parent=QtCore.QModelIndex()):
return 3
return 3
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole and orientation == Qt.Horizontal: def headerData(self, section, orientation, role=Qt.DisplayRole):
return self.HEADER[section] if role == Qt.DisplayRole and orientation == Qt.Horizontal:
return QAbstractTableModel.headerData(self, section, orientation, role) return self.HEADER[section]
return QAbstractTableModel.headerData(self, section, orientation, role)
def data(self, index, role):
if role == QtCore.Qt.DisplayRole: def data(self, index, role):
i = index.row() if role == QtCore.Qt.DisplayRole:
j = index.column() i = index.row()
return self.frame[i][j] j = index.column()
else: return self.frame[i][j]
return QtCore.QVariant() else:
return QtCore.QVariant()
class LocalsView(QTableView,ComponentMixin):
class LocalsView(QTableView,ComponentMixin):
name = 'Variables'
name = 'Variables'
def __init__(self,parent):
def __init__(self,parent):
super(LocalsView,self).__init__(parent)
ComponentMixin.__init__(self) super(LocalsView,self).__init__(parent)
ComponentMixin.__init__(self)
header = self.horizontalHeader()
header.setStretchLastSection(True) header = self.horizontalHeader()
header.setStretchLastSection(True)
vheader = self.verticalHeader()
vheader.setVisible(False) vheader = self.verticalHeader()
vheader.setVisible(False)
@pyqtSlot(dict)
def update_frame(self,frame): @pyqtSlot(dict)
def update_frame(self,frame):
model = LocalsModel(self)
model.update_frame(frame) model = LocalsModel(self)
model.update_frame(frame)
self.setModel(model)
self.setModel(model)
class Debugger(QObject,ComponentMixin):
class Debugger(QObject,ComponentMixin):
name = 'Debugger'
name = 'Debugger'
preferences = Parameter.create(name='Preferences',children=[
{'name': 'Reload CQ', 'type': 'bool', 'value': False}, preferences = Parameter.create(name='Preferences',children=[
{'name': 'Add script dir to path','type': 'bool', 'value': True}, {'name': 'Reload CQ', 'type': 'bool', 'value': False},
{'name': 'Change working dir to script dir','type': 'bool', 'value': True}, {'name': 'Add script dir to path','type': 'bool', 'value': True},
{'name': 'Reload imported modules', 'type': 'bool', 'value': True}, {'name': 'Change working dir to script dir','type': 'bool', 'value': True},
]) {'name': 'Reload imported modules', 'type': 'bool', 'value': True},
])
sigRendered = pyqtSignal(dict)
sigLocals = pyqtSignal(dict) sigRendered = pyqtSignal(dict)
sigTraceback = pyqtSignal(object,str) sigLocals = pyqtSignal(dict)
sigTraceback = pyqtSignal(object,str)
sigFrameChanged = pyqtSignal(object)
sigLineChanged = pyqtSignal(int) sigFrameChanged = pyqtSignal(object)
sigLocalsChanged = pyqtSignal(dict) sigLineChanged = pyqtSignal(int)
sigCQChanged = pyqtSignal(dict,bool) sigLocalsChanged = pyqtSignal(dict)
sigDebugging = pyqtSignal(bool) sigCQChanged = pyqtSignal(dict,bool)
sigDebugging = pyqtSignal(bool)
_frames : List[FrameType]
_frames : List[FrameType]
def __init__(self,parent):
def __init__(self,parent):
super(Debugger,self).__init__(parent)
ComponentMixin.__init__(self) super(Debugger,self).__init__(parent)
ComponentMixin.__init__(self)
self.inner_event_loop = QEventLoop(self)
self.inner_event_loop = QEventLoop(self)
self._actions = \
{'Run' : [QAction(icon('run'), self._actions = \
'Render', {'Run' : [QAction(icon('run'),
self, 'Render',
shortcut='F5', self,
triggered=self.render), shortcut='F5',
QAction(icon('debug'), triggered=self.render),
'Debug', QAction(icon('debug'),
self, 'Debug',
checkable=True, self,
shortcut='ctrl+F5', checkable=True,
triggered=self.debug), shortcut='ctrl+F5',
QAction(icon('arrow-step-over'), triggered=self.debug),
'Step', QAction(icon('arrow-step-over'),
self, 'Step',
shortcut='ctrl+F10', self,
triggered=lambda: self.debug_cmd(DbgState.STEP)), shortcut='ctrl+F10',
QAction(icon('arrow-step-in'), triggered=lambda: self.debug_cmd(DbgState.STEP)),
'Step in', QAction(icon('arrow-step-in'),
self, 'Step in',
shortcut='ctrl+F11', self,
triggered=lambda: self.debug_cmd(DbgState.STEP_IN)), shortcut='ctrl+F11',
QAction(icon('arrow-continue'), triggered=lambda: self.debug_cmd(DbgState.STEP_IN)),
'Continue', QAction(icon('arrow-continue'),
self, 'Continue',
shortcut='ctrl+F12', self,
triggered=lambda: self.debug_cmd(DbgState.CONT)) shortcut='ctrl+F12',
]} triggered=lambda: self.debug_cmd(DbgState.CONT))
]}
self._frames = []
self._frames = []
def get_current_script(self):
def get_current_script(self):
return self.parent().components['editor'].get_text_with_eol()
return self.parent().components['editor'].get_text_with_eol()
def get_breakpoints(self):
def get_breakpoints(self):
return self.parent().components['editor'].debugger.get_breakpoints()
return self.parent().components['editor'].debugger.get_breakpoints()
def compile_code(self, cq_script):
def compile_code(self, cq_script):
try:
module = ModuleType('temp') try:
cq_code = compile(cq_script, '<string>', 'exec') module = ModuleType('temp')
return cq_code, module cq_code = compile(cq_script, '<string>', 'exec')
except Exception: return cq_code, module
self.sigTraceback.emit(sys.exc_info(), cq_script) except Exception:
return None, None self.sigTraceback.emit(sys.exc_info(), cq_script)
return None, None
def _exec(self, code, locals_dict, globals_dict):
def _exec(self, code, locals_dict, globals_dict):
with ExitStack() as stack:
fname = self.parent().components['editor'].filename with ExitStack() as stack:
p = Path(fname if fname else '').abspath().dirname() fname = self.parent().components['editor'].filename
p = Path(fname if fname else '').abspath().dirname()
if self.preferences['Add script dir to path'] and p.exists():
sys.path.insert(0,p) if self.preferences['Add script dir to path'] and p.exists():
stack.callback(sys.path.remove, p) sys.path.insert(0,p)
if self.preferences['Change working dir to script dir'] and p.exists(): stack.callback(sys.path.remove, p)
stack.enter_context(p) if self.preferences['Change working dir to script dir'] and p.exists():
if self.preferences['Reload imported modules']: stack.enter_context(p)
stack.enter_context(module_manager()) if self.preferences['Reload imported modules']:
stack.enter_context(module_manager())
exec(code, locals_dict, globals_dict)
exec(code, locals_dict, globals_dict)
def _inject_locals(self,module):
def _inject_locals(self,module):
cq_objects = {}
cq_objects = {}
def _show_object(obj,name=None, options={}):
def _show_object(obj,name=None, options={}):
if name:
cq_objects.update({name : SimpleNamespace(shape=obj,options=options)}) if name:
else: cq_objects.update({name : SimpleNamespace(shape=obj,options=options)})
cq_objects.update({str(id(obj)) : SimpleNamespace(shape=obj,options=options)}) else:
cq_objects.update({str(id(obj)) : SimpleNamespace(shape=obj,options=options)})
def _debug(obj,name=None):
def _debug(obj,name=None):
_show_object(obj,name,options=dict(color='red',alpha=0.2))
_show_object(obj,name,options=dict(color='red',alpha=0.2))
module.__dict__['show_object'] = _show_object
module.__dict__['debug'] = _debug def _rand_color(alpha = 0., cfloat=False):
module.__dict__['log'] = lambda x: info(str(x)) #helper function to generate a random color dict
module.__dict__['cq'] = cq #for CQ-editor's show_object function
lower = 10
return cq_objects, set(module.__dict__)-{'cq'} upper = 100 #not too high to keep color brightness in check
if cfloat: #for two output types depending on need
def _cleanup_locals(self,module,injected_names): return (
(rrr(lower,upper)/255),
for name in injected_names: module.__dict__.pop(name) (rrr(lower,upper)/255),
(rrr(lower,upper)/255),
@pyqtSlot(bool) alpha,
def render(self): )
return {"alpha": alpha,
if self.preferences['Reload CQ']: "color": (
reload_cq() rrr(lower,upper),
rrr(lower,upper),
cq_script = self.get_current_script() rrr(lower,upper),
cq_code,module = self.compile_code(cq_script) )}
if cq_code is None: return module.__dict__['show_object'] = _show_object
module.__dict__['debug'] = _debug
cq_objects,injected_names = self._inject_locals(module) module.__dict__['rand_color'] = _rand_color
module.__dict__['log'] = lambda x: info(str(x))
try: module.__dict__['cq'] = cq
self._exec(cq_code, module.__dict__, module.__dict__)
return cq_objects, set(module.__dict__)-{'cq'}
#remove the special methods
self._cleanup_locals(module,injected_names) def _cleanup_locals(self,module,injected_names):
#collect all CQ objects if no explicit show_object was called for name in injected_names: module.__dict__.pop(name)
if len(cq_objects) == 0:
cq_objects = find_cq_objects(module.__dict__) @pyqtSlot(bool)
self.sigRendered.emit(cq_objects) def render(self):
self.sigTraceback.emit(None, seed(371353) #reset the seed every time render is called (preserves colors run to run)
cq_script) if self.preferences['Reload CQ']:
self.sigLocals.emit(module.__dict__) reload_cq()
except Exception:
exc_info = sys.exc_info() cq_script = self.get_current_script()
sys.last_traceback = exc_info[-1] cq_code,module = self.compile_code(cq_script)
self.sigTraceback.emit(exc_info, cq_script)
if cq_code is None: return
@property
def breakpoints(self): cq_objects,injected_names = self._inject_locals(module)
return [ el[0] for el in self.get_breakpoints()]
try:
@pyqtSlot(bool) self._exec(cq_code, module.__dict__, module.__dict__)
def debug(self,value):
#remove the special methods
previous_trace = sys.gettrace() self._cleanup_locals(module,injected_names)
if value: #collect all CQ objects if no explicit show_object was called
self.sigDebugging.emit(True) if len(cq_objects) == 0:
self.state = DbgState.STEP cq_objects = find_cq_objects(module.__dict__)
self.sigRendered.emit(cq_objects)
self.script = self.get_current_script() self.sigTraceback.emit(None,
code,module = self.compile_code(self.script) cq_script)
self.sigLocals.emit(module.__dict__)
if code is None: except Exception:
self.sigDebugging.emit(False) exc_info = sys.exc_info()
self._actions['Run'][1].setChecked(False) sys.last_traceback = exc_info[-1]
return self.sigTraceback.emit(exc_info, cq_script)
cq_objects,injected_names = self._inject_locals(module) @property
def breakpoints(self):
#clear possible traceback return [ el[0] for el in self.get_breakpoints()]
self.sigTraceback.emit(None,
self.script) @pyqtSlot(bool)
def debug(self,value):
try:
sys.settrace(self.trace_callback) previous_trace = sys.gettrace()
exec(code,module.__dict__,module.__dict__)
except Exception: if value:
exc_info = sys.exc_info() self.sigDebugging.emit(True)
sys.last_traceback = exc_info[-1] self.state = DbgState.STEP
self.sigTraceback.emit(exc_info,
self.script) self.script = self.get_current_script()
finally: code,module = self.compile_code(self.script)
sys.settrace(previous_trace)
self.sigDebugging.emit(False) if code is None:
self._actions['Run'][1].setChecked(False) self.sigDebugging.emit(False)
self._actions['Run'][1].setChecked(False)
if len(cq_objects) == 0: return
cq_objects = find_cq_objects(module.__dict__)
self.sigRendered.emit(cq_objects) cq_objects,injected_names = self._inject_locals(module)
self._cleanup_locals(module,injected_names) #clear possible traceback
self.sigLocals.emit(module.__dict__) self.sigTraceback.emit(None,
self.script)
self._frames = []
else: try:
sys.settrace(previous_trace) sys.settrace(self.trace_callback)
self.inner_event_loop.exit(0) exec(code,module.__dict__,module.__dict__)
except Exception:
exc_info = sys.exc_info()
def debug_cmd(self,state=DbgState.STEP): sys.last_traceback = exc_info[-1]
self.sigTraceback.emit(exc_info,
self.state = state self.script)
self.inner_event_loop.exit(0) finally:
sys.settrace(previous_trace)
self.sigDebugging.emit(False)
def trace_callback(self,frame,event,arg): self._actions['Run'][1].setChecked(False)
filename = frame.f_code.co_filename if len(cq_objects) == 0:
cq_objects = find_cq_objects(module.__dict__)
if filename==DUMMY_FILE: self.sigRendered.emit(cq_objects)
if not self._frames:
self._frames.append(frame) self._cleanup_locals(module,injected_names)
self.trace_local(frame,event,arg) self.sigLocals.emit(module.__dict__)
return self.trace_callback
self._frames = []
else: else:
return None sys.settrace(previous_trace)
self.inner_event_loop.exit(0)
def trace_local(self,frame,event,arg):
lineno = frame.f_lineno def debug_cmd(self,state=DbgState.STEP):
if event in (DbgEevent.LINE,): self.state = state
if (self.state in (DbgState.STEP, DbgState.STEP_IN) and frame is self._frames[-1]) \ self.inner_event_loop.exit(0)
or (lineno in self.breakpoints):
if lineno in self.breakpoints: def trace_callback(self,frame,event,arg):
self._frames.append(frame)
filename = frame.f_code.co_filename
self.sigLineChanged.emit(lineno)
self.sigFrameChanged.emit(frame) if filename==DUMMY_FILE:
self.sigLocalsChanged.emit(frame.f_locals) if not self._frames:
self.sigCQChanged.emit(find_cq_objects(frame.f_locals),True) self._frames.append(frame)
self.trace_local(frame,event,arg)
self.inner_event_loop.exec_() return self.trace_callback
elif event in (DbgEevent.RETURN): else:
self.sigLocalsChanged.emit(frame.f_locals) return None
self._frames.pop()
def trace_local(self,frame,event,arg):
elif event == DbgEevent.CALL:
func_filename = frame.f_code.co_filename lineno = frame.f_lineno
if self.state == DbgState.STEP_IN and func_filename == DUMMY_FILE:
self.sigLineChanged.emit(lineno) if event in (DbgEevent.LINE,):
self.sigFrameChanged.emit(frame) if (self.state in (DbgState.STEP, DbgState.STEP_IN) and frame is self._frames[-1]) \
self.state = DbgState.STEP or (lineno in self.breakpoints):
self._frames.append(frame)
if lineno in self.breakpoints:
self._frames.append(frame)
@contextmanager
def module_manager(): self.sigLineChanged.emit(lineno)
""" unloads any modules loaded while the context manager is active """ self.sigFrameChanged.emit(frame)
loaded_modules = set(sys.modules.keys()) self.sigLocalsChanged.emit(frame.f_locals)
self.sigCQChanged.emit(find_cq_objects(frame.f_locals),True)
try:
yield self.inner_event_loop.exec_()
finally:
new_modules = set(sys.modules.keys()) - loaded_modules elif event in (DbgEevent.RETURN):
for module_name in new_modules: self.sigLocalsChanged.emit(frame.f_locals)
del sys.modules[module_name] self._frames.pop()
elif event == DbgEevent.CALL:
func_filename = frame.f_code.co_filename
if self.state == DbgState.STEP_IN and func_filename == DUMMY_FILE:
self.sigLineChanged.emit(lineno)
self.sigFrameChanged.emit(frame)
self.state = DbgState.STEP
self._frames.append(frame)
@contextmanager
def module_manager():
""" unloads any modules loaded while the context manager is active """
loaded_modules = set(sys.modules.keys())
try:
yield
finally:
new_modules = set(sys.modules.keys()) - loaded_modules
for module_name in new_modules:
del sys.modules[module_name]