diff --git a/cq_editor/editor.py b/cq_editor/editor.py
index f0731fd..6e643fe 100644
--- a/cq_editor/editor.py
+++ b/cq_editor/editor.py
@@ -3,7 +3,7 @@ import spyder.utils.encoding
from modulefinder import ModuleFinder
from spyder.plugins.editor.widgets.codeeditor import CodeEditor
-from PySide6.QtCore import pyqtSignal, QFileSystemWatcher, QTimer
+from PySide6.QtCore import Signal as pyqtSignal, QFileSystemWatcher, QTimer
from PySide6.QtWidgets import QAction, QFileDialog
from PySide6.QtGui import QFontDatabase
from path import Path
@@ -17,91 +17,104 @@ from ..utils import get_save_filename, get_open_filename, confirm
from ..icons import icon
-class Editor(CodeEditor,ComponentMixin):
- name = 'Code Editor'
+class Editor(CodeEditor, ComponentMixin):
+ name = "Code Editor"
# This signal is emitted whenever the currently-open file changes and
# autoreload is enabled.
triggerRerender = pyqtSignal(bool)
sigFilenameChanged = pyqtSignal(str)
- preferences = Parameter.create(name='Preferences',children=[
- {'name': 'Font size', 'type': 'int', 'value': 12},
- {'name': 'Autoreload', 'type': 'bool', 'value': False},
- {'name': 'Autoreload delay', 'type': 'int', 'value': 50},
- {'name': 'Autoreload: watch imported modules', 'type': 'bool', 'value': False},
- {'name': 'Line wrap', 'type': 'bool', 'value': False},
- {'name': 'Color scheme', 'type': 'list',
- 'values': ['Spyder','Monokai','Zenburn'], 'value': 'Spyder'}])
+ preferences = Parameter.create(
+ name="Preferences",
+ children=[
+ {"name": "Font size", "type": "int", "value": 12},
+ {"name": "Autoreload", "type": "bool", "value": False},
+ {"name": "Autoreload delay", "type": "int", "value": 50},
+ {
+ "name": "Autoreload: watch imported modules",
+ "type": "bool",
+ "value": False,
+ },
+ {"name": "Line wrap", "type": "bool", "value": False},
+ {
+ "name": "Color scheme",
+ "type": "list",
+ "values": ["Spyder", "Monokai", "Zenburn"],
+ "value": "Spyder",
+ },
+ ],
+ )
- EXTENSIONS = 'py'
-
- def __init__(self,parent=None):
+ EXTENSIONS = "py"
+ def __init__(self, parent=None):
self._watched_file = None
- super(Editor,self).__init__(parent)
+ super(Editor, self).__init__(parent)
ComponentMixin.__init__(self)
- self.setup_editor(linenumbers=True,
- markers=True,
- edge_line=False,
- tab_mode=False,
- show_blanks=True,
- font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
- language='Python',
- filename='')
+ self.setup_editor(
+ linenumbers=True,
+ markers=True,
+ edge_line=False,
+ tab_mode=False,
+ show_blanks=True,
+ font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
+ language="Python",
+ filename="",
+ )
- self._actions = \
- {'File' : [QAction(icon('new'),
- 'New',
- self,
- shortcut='ctrl+N',
- triggered=self.new),
- QAction(icon('open'),
- 'Open',
- self,
- shortcut='ctrl+O',
- triggered=self.open),
- QAction(icon('save'),
- 'Save',
- self,
- shortcut='ctrl+S',
- triggered=self.save),
- QAction(icon('save_as'),
- 'Save as',
- self,
- shortcut='ctrl+shift+S',
- triggered=self.save_as),
- QAction(icon('autoreload'),
- 'Automatic reload and preview',
- self,triggered=self.autoreload,
- checkable=True,
- checked=False,
- objectName='autoreload'),
- ]}
+ self._actions = {
+ "File": [
+ QAction(
+ icon("new"), "New", self, shortcut="ctrl+N", triggered=self.new
+ ),
+ QAction(
+ icon("open"), "Open", self, shortcut="ctrl+O", triggered=self.open
+ ),
+ QAction(
+ icon("save"), "Save", self, shortcut="ctrl+S", triggered=self.save
+ ),
+ QAction(
+ icon("save_as"),
+ "Save as",
+ self,
+ shortcut="ctrl+shift+S",
+ triggered=self.save_as,
+ ),
+ QAction(
+ icon("autoreload"),
+ "Automatic reload and preview",
+ self,
+ triggered=self.autoreload,
+ checkable=True,
+ checked=False,
+ objectName="autoreload",
+ ),
+ ]
+ }
for a in self._actions.values():
self.addActions(a)
-
self._fixContextMenu()
# autoreload support
self._file_watcher = QFileSystemWatcher(self)
# we wait for 50ms after a file change for the file to be written completely
self._file_watch_timer = QTimer(self)
- self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
+ self._file_watch_timer.setInterval(self.preferences["Autoreload delay"])
self._file_watch_timer.setSingleShot(True)
self._file_watcher.fileChanged.connect(
- lambda val: self._file_watch_timer.start())
+ lambda val: self._file_watch_timer.start()
+ )
self._file_watch_timer.timeout.connect(self._file_changed)
self.updatePreferences()
def _fixContextMenu(self):
-
menu = self.menu
menu.removeAction(self.run_cell_action)
@@ -109,52 +122,52 @@ class Editor(CodeEditor,ComponentMixin):
menu.removeAction(self.run_selection_action)
menu.removeAction(self.re_run_last_cell_action)
- def updatePreferences(self,*args):
-
- self.set_color_scheme(self.preferences['Color scheme'])
+ def updatePreferences(self, *args):
+ self.set_color_scheme(self.preferences["Color scheme"])
font = self.font()
- font.setPointSize(self.preferences['Font size'])
+ font.setPointSize(self.preferences["Font size"])
self.set_font(font)
- self.findChild(QAction, 'autoreload') \
- .setChecked(self.preferences['Autoreload'])
+ self.findChild(QAction, "autoreload").setChecked(self.preferences["Autoreload"])
- self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
+ self._file_watch_timer.setInterval(self.preferences["Autoreload delay"])
- self.toggle_wrap_mode(self.preferences['Line wrap'])
+ self.toggle_wrap_mode(self.preferences["Line wrap"])
self._clear_watched_paths()
self._watch_paths()
def confirm_discard(self):
-
if self.modified:
- rv = confirm(self,'Please confirm','Current document is not saved - do you want to continue?')
+ rv = confirm(
+ self,
+ "Please confirm",
+ "Current document is not saved - do you want to continue?",
+ )
else:
rv = True
return rv
def new(self):
+ if not self.confirm_discard():
+ return
- if not self.confirm_discard(): return
-
- self.set_text('')
- self.filename = ''
+ self.set_text("")
+ self.filename = ""
self.reset_modified()
def open(self):
-
- if not self.confirm_discard(): return
+ if not self.confirm_discard():
+ return
curr_dir = Path(self.filename).abspath().dirname()
fname = get_open_filename(self.EXTENSIONS, curr_dir)
- if fname != '':
+ if fname != "":
self.load_from_file(fname)
- def load_from_file(self,fname):
-
+ def load_from_file(self, fname):
self.set_text_from_file(fname)
self.filename = fname
self.reset_modified()
@@ -164,24 +177,22 @@ class Editor(CodeEditor,ComponentMixin):
# this function returns the encoding spyder used to read the file
_, encoding = spyder.utils.encoding.read(fname)
# spyder returns a -guessed suffix in some cases
- return encoding.replace('-guessed', '')
+ return encoding.replace("-guessed", "")
else:
- return 'utf-8'
+ return "utf-8"
def save(self):
-
- if self._filename != '':
-
- if self.preferences['Autoreload']:
+ if self._filename != "":
+ if self.preferences["Autoreload"]:
self._file_watcher.blockSignals(True)
self._file_watch_timer.stop()
encoding = self.determine_encoding(self._filename)
encoded = self.toPlainText().encode(encoding)
- with open(self._filename, 'wb') as f:
+ with open(self._filename, "wb") as f:
f.write(encoded)
- if self.preferences['Autoreload']:
+ if self.preferences["Autoreload"]:
self._file_watcher.blockSignals(False)
self.triggerRerender.emit(True)
@@ -191,21 +202,26 @@ class Editor(CodeEditor,ComponentMixin):
self.save_as()
def save_as(self):
-
fname = get_save_filename(self.EXTENSIONS)
- if fname != '':
- encoded = self.toPlainText().encode('utf-8')
- with open(fname, 'wb') as f:
+ if fname != "":
+ encoded = self.toPlainText().encode("utf-8")
+ with open(fname, "wb") as f:
f.write(encoded)
self.filename = fname
self.reset_modified()
def _update_filewatcher(self):
- if self._watched_file and (self._watched_file != self.filename or not self.preferences['Autoreload']):
+ if self._watched_file and (
+ self._watched_file != self.filename or not self.preferences["Autoreload"]
+ ):
self._clear_watched_paths()
self._watched_file = None
- if self.preferences['Autoreload'] and self.filename and self.filename != self._watched_file:
+ if (
+ self.preferences["Autoreload"]
+ and self.filename
+ and self.filename != self._watched_file
+ ):
self._watched_file = self._filename
self._watch_paths()
@@ -227,8 +243,8 @@ class Editor(CodeEditor,ComponentMixin):
def _watch_paths(self):
if Path(self._filename).exists():
self._file_watcher.addPath(self._filename)
- if self.preferences['Autoreload: watch imported modules']:
- module_paths = self.get_imported_module_paths(self._filename)
+ if self.preferences["Autoreload: watch imported modules"]:
+ module_paths = self.get_imported_module_paths(self._filename)
if module_paths:
self._file_watcher.addPaths(module_paths)
@@ -241,51 +257,45 @@ class Editor(CodeEditor,ComponentMixin):
# Turn autoreload on/off.
def autoreload(self, enabled):
- self.preferences['Autoreload'] = enabled
+ self.preferences["Autoreload"] = enabled
self._update_filewatcher()
def reset_modified(self):
-
self.document().setModified(False)
-
+
@property
def modified(self):
-
return self.document().isModified()
- def saveComponentState(self,store):
+ def saveComponentState(self, store):
+ if self.filename != "":
+ store.setValue(self.name + "/state", self.filename)
- if self.filename != '':
- store.setValue(self.name+'/state',self.filename)
+ def restoreComponentState(self, store):
+ filename = store.value(self.name + "/state")
- def restoreComponentState(self,store):
-
- filename = store.value(self.name+'/state')
-
- if filename and self.filename == '':
+ if filename and self.filename == "":
try:
self.load_from_file(filename)
except IOError:
- self._logger.warning(f'could not open {filename}')
-
+ self._logger.warning(f"could not open {filename}")
def get_imported_module_paths(self, module_path):
-
finder = ModuleFinder([os.path.dirname(module_path)])
imported_modules = []
try:
finder.run_script(module_path)
except SyntaxError as err:
- self._logger.warning(f'Syntax error in {module_path}: {err}')
+ self._logger.warning(f"Syntax error in {module_path}: {err}")
except Exception as err:
self._logger.warning(
- f'Cannot determine imported modules in {module_path}: {type(err).__name__} {err}'
+ f"Cannot determine imported modules in {module_path}: {type(err).__name__} {err}"
)
else:
for module_name, module in finder.modules.items():
- if module_name != '__main__':
- path = getattr(module, '__file__', None)
+ if module_name != "__main__":
+ path = getattr(module, "__file__", None)
if path is not None and os.path.isfile(path):
imported_modules.append(path)
@@ -293,7 +303,6 @@ class Editor(CodeEditor,ComponentMixin):
if __name__ == "__main__":
-
from PySide6.QtWidgets import QApplication
app = QApplication(sys.argv)
diff --git a/cq_editor/main_window.py b/cq_editor/main_window.py
index f74ab21..d7ebc82 100644
--- a/cq_editor/main_window.py
+++ b/cq_editor/main_window.py
@@ -1,6 +1,8 @@
import sys
-from PySide6.QtWidgets import (QLabel, QMainWindow, QToolBar, QDockWidget, QAction)
+from PySide6.QtWidgets import QLabel, QMainWindow, QToolBar, QDockWidget
+from PySide6.QtGui import QAction
+from PySide6.QtCore import Signal as pyqtSignal
from logbook import Logger
import cadquery as cq
@@ -14,16 +16,26 @@ from .widgets.cq_object_inspector import CQObjectInspector
from .widgets.log import LogViewer
from . import __version__
-from .utils import dock, add_actions, open_url, about_dialog, check_gtihub_for_updates, confirm
+from .utils import (
+ dock,
+ add_actions,
+ open_url,
+ about_dialog,
+ check_gtihub_for_updates,
+ confirm,
+)
from .mixins import MainMixin
from .icons import icon
from .preferences import PreferencesWidget
-#DARKMODE edits: https://stackoverflow.com/questions/48256772/dark-theme-for-qt-widgets
+# DARKMODE edits: https://stackoverflow.com/questions/48256772/dark-theme-for-qt-widgets
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QPalette, QColor
-app = QApplication([])
+
+app = QApplication.instance()
+if app == None:
+ app = QApplication([])
# Force the style to be the same on all OSs:
app.setStyle("Fusion")
# Now use a palette to switch to dark colors:
@@ -43,141 +55,131 @@ palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
palette.setColor(QPalette.HighlightedText, Qt.black)
app.setPalette(palette)
-class MainWindow(QMainWindow,MainMixin):
- name = 'CQ-Editor'
- org = 'CadQuery'
+class MainWindow(QMainWindow, MainMixin):
+ name = "CQ-Editor"
+ org = "CadQuery"
- def __init__(self,parent=None, filename=None):
-
- super(MainWindow,self).__init__(parent)
+ def __init__(self, parent=None, filename=None):
+ super(MainWindow, self).__init__(parent)
MainMixin.__init__(self)
- self.setWindowIcon(icon('app'))
+ self.setWindowIcon(icon("app"))
self.viewer = OCCViewer(self)
- self.setCentralWidget(self.viewer.canvas)
+ # self.setCentralWidget(self.viewer.canvas)
- self.prepare_panes()
- self.registerComponent('viewer',self.viewer)
- self.prepare_toolbar()
- self.prepare_menubar()
+ # self.prepare_panes()
+ # self.registerComponent("viewer", self.viewer)
+ # self.prepare_toolbar()
+ # self.prepare_menubar()
- self.prepare_statusbar()
- self.prepare_actions()
-
- self.components['object_tree'].addLines()
+ # self.prepare_statusbar()
+ # self.prepare_actions()
- self.prepare_console()
+ # self.components["object_tree"].addLines()
- self.fill_dummy()
+ # self.prepare_console()
- self.setup_logging()
+ # self.fill_dummy()
- self.restorePreferences()
- self.restoreWindow()
+ # self.setup_logging()
- if filename:
- self.components['editor'].load_from_file(filename)
+ # self.restorePreferences()
+ # self.restoreWindow()
- self.restoreComponentState()
+ # if filename:
+ # self.components["editor"].load_from_file(filename)
- def closeEvent(self,event):
+ # self.restoreComponentState()
+ def closeEvent(self, event):
self.saveWindow()
self.savePreferences()
self.saveComponentState()
- if self.components['editor'].document().isModified():
+ if self.components["editor"].document().isModified():
+ rv = confirm(self, "Confirm close", "Close without saving?")
- rv = confirm(self, 'Confirm close', 'Close without saving?')
-
if rv:
event.accept()
- super(MainWindow,self).closeEvent(event)
+ super(MainWindow, self).closeEvent(event)
else:
event.ignore()
else:
- super(MainWindow,self).closeEvent(event)
+ super(MainWindow, self).closeEvent(event)
def prepare_panes(self):
+ self.registerComponent(
+ "editor",
+ Editor(self),
+ lambda c: dock(c, "Editor", self, defaultArea="left"),
+ )
- self.registerComponent('editor',
- Editor(self),
- lambda c : dock(c,
- 'Editor',
- self,
- defaultArea='left'))
+ self.registerComponent(
+ "object_tree",
+ ObjectTree(self),
+ lambda c: dock(c, "Objects", self, defaultArea="right"),
+ )
- self.registerComponent('object_tree',
- ObjectTree(self),
- lambda c: dock(c,
- 'Objects',
- self,
- defaultArea='right'))
+ self.registerComponent(
+ "console",
+ ConsoleWidget(self),
+ lambda c: dock(c, "Console", self, defaultArea="bottom"),
+ )
- self.registerComponent('console',
- ConsoleWidget(self),
- lambda c: dock(c,
- 'Console',
- self,
- defaultArea='bottom'))
+ self.registerComponent(
+ "traceback_viewer",
+ TracebackPane(self),
+ lambda c: dock(c, "Current traceback", self, defaultArea="bottom"),
+ )
- self.registerComponent('traceback_viewer',
- TracebackPane(self),
- lambda c: dock(c,
- 'Current traceback',
- self,
- defaultArea='bottom'))
+ self.registerComponent("debugger", Debugger(self))
- self.registerComponent('debugger',Debugger(self))
+ self.registerComponent(
+ "variables_viewer",
+ LocalsView(self),
+ lambda c: dock(c, "Variables", self, defaultArea="right"),
+ )
- self.registerComponent('variables_viewer',LocalsView(self),
- lambda c: dock(c,
- 'Variables',
- self,
- defaultArea='right'))
-
- self.registerComponent('cq_object_inspector',
- CQObjectInspector(self),
- lambda c: dock(c,
- 'CQ object inspector',
- self,
- defaultArea='right'))
- self.registerComponent('log',
- LogViewer(self),
- lambda c: dock(c,
- 'Log viewer',
- self,
- defaultArea='bottom'))
+ self.registerComponent(
+ "cq_object_inspector",
+ CQObjectInspector(self),
+ lambda c: dock(c, "CQ object inspector", self, defaultArea="right"),
+ )
+ self.registerComponent(
+ "log",
+ LogViewer(self),
+ lambda c: dock(c, "Log viewer", self, defaultArea="bottom"),
+ )
for d in self.docks.values():
d.show()
def prepare_menubar(self):
-
menu = self.menuBar()
- menu_file = menu.addMenu('&File')
- menu_edit = menu.addMenu('&Edit')
- menu_tools = menu.addMenu('&Tools')
- menu_run = menu.addMenu('&Run')
- menu_view = menu.addMenu('&View')
- menu_help = menu.addMenu('&Help')
+ menu_file = menu.addMenu("&File")
+ menu_edit = menu.addMenu("&Edit")
+ menu_tools = menu.addMenu("&Tools")
+ menu_run = menu.addMenu("&Run")
+ menu_view = menu.addMenu("&View")
+ menu_help = menu.addMenu("&Help")
- #per component menu elements
- menus = {'File' : menu_file,
- 'Edit' : menu_edit,
- 'Run' : menu_run,
- 'Tools': menu_tools,
- 'View' : menu_view,
- 'Help' : menu_help}
+ # per component menu elements
+ menus = {
+ "File": menu_file,
+ "Edit": menu_edit,
+ "Run": menu_run,
+ "Tools": menu_tools,
+ "View": menu_view,
+ "Help": menu_help,
+ }
for comp in self.components.values():
- self.prepare_menubar_component(menus,
- comp.menuActions())
+ self.prepare_menubar_component(menus, comp.menuActions())
- #global menu elements
+ # global menu elements
menu_view.addSeparator()
for d in self.findChildren(QDockWidget):
menu_view.addAction(d.toggleViewAction())
@@ -186,181 +188,200 @@ class MainWindow(QMainWindow,MainMixin):
for t in self.findChildren(QToolBar):
menu_view.addAction(t.toggleViewAction())
- menu_edit.addAction( \
- QAction(icon('preferences'),
- 'Preferences',
- self,triggered=self.edit_preferences))
+ menu_edit.addAction(
+ QAction(
+ icon("preferences"),
+ "Preferences",
+ self,
+ triggered=self.edit_preferences,
+ )
+ )
- menu_help.addAction( \
- QAction(icon('help'),
- 'Documentation',
- self,triggered=self.documentation))
+ menu_help.addAction(
+ QAction(icon("help"), "Documentation", self, triggered=self.documentation)
+ )
- menu_help.addAction( \
- QAction('CQ documentation',
- self,triggered=self.cq_documentation))
+ menu_help.addAction(
+ QAction("CQ documentation", self, triggered=self.cq_documentation)
+ )
- menu_help.addAction( \
- QAction(icon('about'),
- 'About',
- self,triggered=self.about))
-
- menu_help.addAction( \
- QAction('Check for CadQuery updates',
- self,triggered=self.check_for_cq_updates))
+ menu_help.addAction(QAction(icon("about"), "About", self, triggered=self.about))
- def prepare_menubar_component(self,menus,comp_menu_dict):
+ menu_help.addAction(
+ QAction(
+ "Check for CadQuery updates", self, triggered=self.check_for_cq_updates
+ )
+ )
- for name,action in comp_menu_dict.items():
+ def prepare_menubar_component(self, menus, comp_menu_dict):
+ for name, action in comp_menu_dict.items():
menus[name].addActions(action)
def prepare_toolbar(self):
-
- self.toolbar = QToolBar('Main toolbar',self,objectName='Main toolbar')
+ self.toolbar = QToolBar("Main toolbar", self, objectName="Main toolbar")
for c in self.components.values():
- add_actions(self.toolbar,c.toolbarActions())
+ add_actions(self.toolbar, c.toolbarActions())
self.addToolBar(self.toolbar)
def prepare_statusbar(self):
-
- self.status_label = QLabel('',parent=self)
+ self.status_label = QLabel("", parent=self)
self.statusBar().insertPermanentWidget(0, self.status_label)
def prepare_actions(self):
+ self.components["debugger"].sigRendered.connect(
+ self.components["object_tree"].addObjects
+ )
+ self.components["debugger"].sigTraceback.connect(
+ self.components["traceback_viewer"].addTraceback
+ )
+ self.components["debugger"].sigLocals.connect(
+ self.components["variables_viewer"].update_frame
+ )
+ self.components["debugger"].sigLocals.connect(
+ self.components["console"].push_vars
+ )
- self.components['debugger'].sigRendered\
- .connect(self.components['object_tree'].addObjects)
- self.components['debugger'].sigTraceback\
- .connect(self.components['traceback_viewer'].addTraceback)
- self.components['debugger'].sigLocals\
- .connect(self.components['variables_viewer'].update_frame)
- self.components['debugger'].sigLocals\
- .connect(self.components['console'].push_vars)
+ self.components["object_tree"].sigObjectsAdded[list].connect(
+ self.components["viewer"].display_many
+ )
+ self.components["object_tree"].sigObjectsAdded2[list, bool].connect(
+ self.components["viewer"].display_many
+ )
+ self.components["object_tree"].sigItemChanged.connect(
+ self.components["viewer"].update_item
+ )
+ self.components["object_tree"].sigObjectsRemoved.connect(
+ self.components["viewer"].remove_items
+ )
+ self.components["object_tree"].sigCQObjectSelected.connect(
+ self.components["cq_object_inspector"].setObject
+ )
+ self.components["object_tree"].sigObjectPropertiesChanged.connect(
+ self.components["viewer"].redraw
+ )
+ self.components["object_tree"].sigAISObjectsSelected.connect(
+ self.components["viewer"].set_selected
+ )
- self.components['object_tree'].sigObjectsAdded[list]\
- .connect(self.components['viewer'].display_many)
- self.components['object_tree'].sigObjectsAdded[list,bool]\
- .connect(self.components['viewer'].display_many)
- self.components['object_tree'].sigItemChanged.\
- connect(self.components['viewer'].update_item)
- self.components['object_tree'].sigObjectsRemoved\
- .connect(self.components['viewer'].remove_items)
- self.components['object_tree'].sigCQObjectSelected\
- .connect(self.components['cq_object_inspector'].setObject)
- self.components['object_tree'].sigObjectPropertiesChanged\
- .connect(self.components['viewer'].redraw)
- self.components['object_tree'].sigAISObjectsSelected\
- .connect(self.components['viewer'].set_selected)
+ self.components["viewer"].sigObjectSelected.connect(
+ self.components["object_tree"].handleGraphicalSelection
+ )
- self.components['viewer'].sigObjectSelected\
- .connect(self.components['object_tree'].handleGraphicalSelection)
+ self.components["traceback_viewer"].sigHighlightLine.connect(
+ self.components["editor"].go_to_line
+ )
- self.components['traceback_viewer'].sigHighlightLine\
- .connect(self.components['editor'].go_to_line)
+ self.components["cq_object_inspector"].sigDisplayObjects.connect(
+ self.components["viewer"].display_many
+ )
+ self.components["cq_object_inspector"].sigRemoveObjects.connect(
+ self.components["viewer"].remove_items
+ )
+ self.components["cq_object_inspector"].sigShowPlane.connect(
+ self.components["viewer"].toggle_grid
+ )
+ self.components["cq_object_inspector"].sigShowPlane2[bool, float].connect(
+ self.components["viewer"].toggle_grid
+ )
+ self.components["cq_object_inspector"].sigChangePlane.connect(
+ self.components["viewer"].set_grid_orientation
+ )
- self.components['cq_object_inspector'].sigDisplayObjects\
- .connect(self.components['viewer'].display_many)
- self.components['cq_object_inspector'].sigRemoveObjects\
- .connect(self.components['viewer'].remove_items)
- self.components['cq_object_inspector'].sigShowPlane\
- .connect(self.components['viewer'].toggle_grid)
- self.components['cq_object_inspector'].sigShowPlane[bool,float]\
- .connect(self.components['viewer'].toggle_grid)
- self.components['cq_object_inspector'].sigChangePlane\
- .connect(self.components['viewer'].set_grid_orientation)
-
- self.components['debugger'].sigLocalsChanged\
- .connect(self.components['variables_viewer'].update_frame)
- self.components['debugger'].sigLineChanged\
- .connect(self.components['editor'].go_to_line)
- self.components['debugger'].sigDebugging\
- .connect(self.components['object_tree'].stashObjects)
- self.components['debugger'].sigCQChanged\
- .connect(self.components['object_tree'].addObjects)
- self.components['debugger'].sigTraceback\
- .connect(self.components['traceback_viewer'].addTraceback)
+ self.components["debugger"].sigLocalsChanged.connect(
+ self.components["variables_viewer"].update_frame
+ )
+ self.components["debugger"].sigLineChanged.connect(
+ self.components["editor"].go_to_line
+ )
+ self.components["debugger"].sigDebugging.connect(
+ self.components["object_tree"].stashObjects
+ )
+ self.components["debugger"].sigCQChanged.connect(
+ self.components["object_tree"].addObjects
+ )
+ self.components["debugger"].sigTraceback.connect(
+ self.components["traceback_viewer"].addTraceback
+ )
# trigger re-render when file is modified externally or saved
- self.components['editor'].triggerRerender \
- .connect(self.components['debugger'].render)
- self.components['editor'].sigFilenameChanged\
- .connect(self.handle_filename_change)
+ self.components["editor"].triggerRerender.connect(
+ self.components["debugger"].render
+ )
+ self.components["editor"].sigFilenameChanged.connect(
+ self.handle_filename_change
+ )
def prepare_console(self):
+ console = self.components["console"]
+ obj_tree = self.components["object_tree"]
- console = self.components['console']
- obj_tree = self.components['object_tree']
-
- #application related items
- console.push_vars({'self' : self})
+ # application related items
+ console.push_vars({"self": self})
+
+ # CQ related items
+ console.push_vars(
+ {
+ "show": obj_tree.addObject,
+ "show_object": obj_tree.addObject,
+ "rand_color": self.components["debugger"]._rand_color,
+ "cq": cq,
+ "log": Logger(self.name).info,
+ }
+ )
- #CQ related items
- console.push_vars({'show' : obj_tree.addObject,
- 'show_object' : obj_tree.addObject,
- 'rand_color' : self.components['debugger']._rand_color,
- 'cq' : cq,
- 'log' : Logger(self.name).info})
-
def fill_dummy(self):
-
- self.components['editor']\
- .set_text('import cadquery as cq\nresult = cq.Workplane("XY" ).box(3, 3, 0.5).edges("|Z").fillet(0.125)')
+ self.components["editor"].set_text(
+ 'import cadquery as cq\nresult = cq.Workplane("XY" ).box(3, 3, 0.5).edges("|Z").fillet(0.125)'
+ )
def setup_logging(self):
-
from logbook.compat import redirect_logging
from logbook import INFO, Logger
redirect_logging()
- self.components['log'].handler.level = INFO
- self.components['log'].handler.push_application()
+ self.components["log"].handler.level = INFO
+ self.components["log"].handler.push_application()
self._logger = Logger(self.name)
def handle_exception(exc_type, exc_value, exc_traceback):
-
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
- self._logger.error("Uncaught exception occurred",
- exc_info=(exc_type, exc_value, exc_traceback))
+ self._logger.error(
+ "Uncaught exception occurred",
+ exc_info=(exc_type, exc_value, exc_traceback),
+ )
sys.excepthook = handle_exception
-
def edit_preferences(self):
-
- prefs = PreferencesWidget(self,self.components)
+ prefs = PreferencesWidget(self, self.components)
prefs.exec_()
def about(self):
-
about_dialog(
self,
- f'About CQ-editor',
- f'PyQt GUI for CadQuery.\nVersion: {__version__}.\nSource Code: https://github.com/CadQuery/CQ-editor',
+ f"About CQ-editor",
+ f"PyQt GUI for CadQuery.\nVersion: {__version__}.\nSource Code: https://github.com/CadQuery/CQ-editor",
)
-
+
def check_for_cq_updates(self):
-
- check_gtihub_for_updates(self,cq)
+ check_gtihub_for_updates(self, cq)
def documentation(self):
-
- open_url('https://github.com/CadQuery')
+ open_url("https://github.com/CadQuery")
def cq_documentation(self):
-
- open_url('https://cadquery.readthedocs.io/en/latest/')
+ open_url("https://cadquery.readthedocs.io/en/latest/")
def handle_filename_change(self, fname):
-
new_title = fname if fname else "*"
self.setWindowTitle(f"{self.name}: {new_title}")
-if __name__ == "__main__":
+if __name__ == "__main__":
pass
diff --git a/cq_editor/mixins.py b/cq_editor/mixins.py
index 0605a0c..4c6091a 100644
--- a/cq_editor/mixins.py
+++ b/cq_editor/mixins.py
@@ -10,72 +10,67 @@ from functools import reduce
from operator import add
from logbook import Logger
-from PySide6.QtCore import pyqtSlot, QSettings
+from PySide6.QtCore import QSettings
+from PySide6.QtCore import Slot as pyqtSlot
+
class MainMixin(object):
-
- name = 'Main'
- org = 'Unknown'
+ name = "Main"
+ org = "Unknown"
components = {}
docks = {}
preferences = None
def __init__(self):
+ self.settings = QSettings(self.org, self.name)
- self.settings = QSettings(self.org,self.name)
-
- def registerComponent(self,name,component,dock=None):
-
+ def registerComponent(self, name, component, dock=None):
self.components[name] = component
if dock:
self.docks[name] = dock(component)
def saveWindow(self):
-
- self.settings.setValue('geometry',self.saveGeometry())
- self.settings.setValue('windowState',self.saveState())
+ self.settings.setValue("geometry", self.saveGeometry())
+ self.settings.setValue("windowState", self.saveState())
def restoreWindow(self):
-
- if self.settings.value('geometry'):
- self.restoreGeometry(self.settings.value('geometry'))
- if self.settings.value('windowState'):
- self.restoreState(self.settings.value('windowState'))
+ if self.settings.value("geometry"):
+ self.restoreGeometry(self.settings.value("geometry"))
+ if self.settings.value("windowState"):
+ self.restoreState(self.settings.value("windowState"))
def savePreferences(self):
-
settings = self.settings
if self.preferences:
- settings.setValue('General',self.preferences.saveState())
+ settings.setValue("General", self.preferences.saveState())
for comp in (c for c in self.components.values() if c.preferences):
- settings.setValue(comp.name,comp.preferences.saveState())
+ settings.setValue(comp.name, comp.preferences.saveState())
def restorePreferences(self):
-
settings = self.settings
- if self.preferences and settings.value('General'):
- self.preferences.restoreState(settings.value('General'),
- removeChildren=False)
+ if self.preferences and settings.value("General"):
+ self.preferences.restoreState(
+ settings.value("General"), removeChildren=False
+ )
for comp in (c for c in self.components.values() if c.preferences):
if settings.value(comp.name):
- comp.preferences.restoreState(settings.value(comp.name),
- removeChildren=False)
+ comp.preferences.restoreState(
+ settings.value(comp.name), removeChildren=False
+ )
def saveComponentState(self):
-
settings = self.settings
for comp in self.components.values():
comp.saveComponentState(settings)
def restoreComponentState(self):
-
settings = self.settings
for comp in self.components.values():
@@ -83,42 +78,32 @@ class MainMixin(object):
class ComponentMixin(object):
-
-
- name = 'Component'
+ name = "Component"
preferences = None
_actions = {}
-
def __init__(self):
-
if self.preferences:
- self.preferences.sigTreeStateChanged.\
- connect(self.updatePreferences)
-
+ self.preferences.sigTreeStateChanged.connect(self.updatePreferences)
+
self._logger = Logger(self.name)
def menuActions(self):
-
return self._actions
def toolbarActions(self):
-
if len(self._actions) > 0:
- return reduce(add,[a for a in self._actions.values()])
+ return reduce(add, [a for a in self._actions.values()])
else:
return []
- @pyqtSlot(object,object)
- def updatePreferences(self,*args):
-
+ @pyqtSlot(object, object)
+ def updatePreferences(self, *args):
pass
- def saveComponentState(self,store):
-
+ def saveComponentState(self, store):
pass
- def restoreComponentState(self,store):
-
+ def restoreComponentState(self, store):
pass
diff --git a/cq_editor/preferences.py b/cq_editor/preferences.py
index 93b1429..d274e6a 100644
--- a/cq_editor/preferences.py
+++ b/cq_editor/preferences.py
@@ -1,6 +1,5 @@
-from PySide6.QtWidgets import (QTreeWidget, QTreeWidgetItem,
- QStackedWidget, QDialog)
-from PySide6.QtCore import pyqtSlot, Qt
+from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QStackedWidget, QDialog
+from PySide6.QtCore import Slot as pyqtSlot, Qt
from pyqtgraph.parametertree import ParameterTree
@@ -8,55 +7,54 @@ from .utils import splitter, layout
class PreferencesTreeItem(QTreeWidgetItem):
-
- def __init__(self,name,widget,):
-
- super(PreferencesTreeItem,self).__init__(name)
+ def __init__(
+ self,
+ name,
+ widget,
+ ):
+ super(PreferencesTreeItem, self).__init__(name)
self.widget = widget
+
class PreferencesWidget(QDialog):
-
- def __init__(self,parent,components):
-
- super(PreferencesWidget,self).__init__(
- parent,
- Qt.Window | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint,
- windowTitle='Preferences')
-
+ def __init__(self, parent, components):
+ super(PreferencesWidget, self).__init__(
+ parent,
+ Qt.Window | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint,
+ windowTitle="Preferences",
+ )
+
self.stacked = QStackedWidget(self)
- self.preferences_tree = QTreeWidget(self,
- headerHidden=True,
- itemsExpandable=False,
- rootIsDecorated=False,
- columnCount=1)
-
+ self.preferences_tree = QTreeWidget(
+ self,
+ headerHidden=True,
+ itemsExpandable=False,
+ rootIsDecorated=False,
+ columnCount=1,
+ )
+
self.root = self.preferences_tree.invisibleRootItem()
-
- self.add('General',
- parent)
-
+
+ self.add("General", parent)
+
for v in parent.components.values():
- self.add(v.name,v)
-
- self.splitter = splitter((self.preferences_tree,self.stacked),(2,5))
- layout(self,(self.splitter,),self)
-
+ self.add(v.name, v)
+
+ self.splitter = splitter((self.preferences_tree, self.stacked), (2, 5))
+ layout(self, (self.splitter,), self)
+
self.preferences_tree.currentItemChanged.connect(self.handleSelection)
- def add(self,name,component):
-
+ def add(self, name, component):
if component.preferences:
widget = ParameterTree()
widget.setHeaderHidden(True)
- widget.setParameters(component.preferences,showTop=False)
- self.root.addChild(PreferencesTreeItem((name,),
- widget))
-
+ widget.setParameters(component.preferences, showTop=False)
+ self.root.addChild(PreferencesTreeItem((name,), widget))
+
self.stacked.addWidget(widget)
-
- @pyqtSlot(QTreeWidgetItem,QTreeWidgetItem)
- def handleSelection(self,item,*args):
-
+
+ @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
+ def handleSelection(self, item, *args):
if item:
self.stacked.setCurrentWidget(item.widget)
-
diff --git a/cq_editor/utils.py b/cq_editor/utils.py
index 95c1d57..41552a3 100644
--- a/cq_editor/utils.py
+++ b/cq_editor/utils.py
@@ -7,17 +7,22 @@ from PySide6.QtGui import QDesktopServices
from PySide6.QtCore import QUrl
from PySide6.QtWidgets import QFileDialog, QMessageBox
-DOCK_POSITIONS = {'right' : QtCore.Qt.RightDockWidgetArea,
- 'left' : QtCore.Qt.LeftDockWidgetArea,
- 'top' : QtCore.Qt.TopDockWidgetArea,
- 'bottom' : QtCore.Qt.BottomDockWidgetArea}
+DOCK_POSITIONS = {
+ "right": QtCore.Qt.RightDockWidgetArea,
+ "left": QtCore.Qt.LeftDockWidgetArea,
+ "top": QtCore.Qt.TopDockWidgetArea,
+ "bottom": QtCore.Qt.BottomDockWidgetArea,
+}
-def layout(parent,items,
- top_widget = None,
- layout_type = QtWidgets.QVBoxLayout,
- margin = 2,
- spacing = 0):
-
+
+def layout(
+ parent,
+ items,
+ top_widget=None,
+ layout_type=QtWidgets.QVBoxLayout,
+ margin=2,
+ spacing=0,
+):
if not top_widget:
top_widget = QtWidgets.QWidget(parent)
top_widget_was_none = True
@@ -25,110 +30,124 @@ def layout(parent,items,
top_widget_was_none = False
layout = layout_type(top_widget)
top_widget.setLayout(layout)
-
- for item in items: layout.addWidget(item)
+
+ for item in items:
+ layout.addWidget(item)
layout.setSpacing(spacing)
- layout.setContentsMargins(margin,margin,margin,margin)
-
+ layout.setContentsMargins(margin, margin, margin, margin)
+
if top_widget_was_none:
return top_widget
else:
return layout
-
-def splitter(items,
- stretch_factors = None,
- orientation=QtCore.Qt.Horizontal):
-
+
+
+def splitter(items, stretch_factors=None, orientation=QtCore.Qt.Horizontal):
sp = QtWidgets.QSplitter(orientation)
-
- for item in items: sp.addWidget(item)
-
+
+ for item in items:
+ sp.addWidget(item)
+
if stretch_factors:
- for i,s in enumerate(stretch_factors):
- sp.setStretchFactor(i,s)
-
-
+ for i, s in enumerate(stretch_factors):
+ sp.setStretchFactor(i, s)
+
return sp
-def dock(widget,
- title,
- parent,
- allowedAreas = QtCore.Qt.AllDockWidgetAreas,
- defaultArea = 'right',
- name=None,
- icon = None):
-
- dock = QtWidgets.QDockWidget(title,parent,objectName=title)
-
- if name: dock.setObjectName(name)
- if icon: dock.toggleViewAction().setIcon(icon)
-
+
+def dock(
+ widget,
+ title,
+ parent,
+ allowedAreas=QtCore.Qt.AllDockWidgetAreas,
+ defaultArea="right",
+ name=None,
+ icon=None,
+):
+ dock = QtWidgets.QDockWidget(title, parent, objectName=title)
+
+ if name:
+ dock.setObjectName(name)
+ if icon:
+ dock.toggleViewAction().setIcon(icon)
+
dock.setAllowedAreas(allowedAreas)
dock.setWidget(widget)
action = dock.toggleViewAction()
action.setText(title)
-
- dock.setFeatures(QtWidgets.QDockWidget.DockWidgetFeatures(\
- QtWidgets.QDockWidget.AllDockWidgetFeatures))
-
- parent.addDockWidget(DOCK_POSITIONS[defaultArea],
- dock)
-
+
+ dock.setFeatures(
+ QtWidgets.QDockWidget.DockWidgetFeatures(
+ QtWidgets.QDockWidget.DockWidgetFloatable
+ )
+ )
+
+ parent.addDockWidget(DOCK_POSITIONS[defaultArea], dock)
+
return dock
-def add_actions(menu,actions):
-
+
+def add_actions(menu, actions):
if len(actions) > 0:
menu.addActions(actions)
menu.addSeparator()
-
+
+
def open_url(url):
-
- QDesktopServices.openUrl(QUrl(url))
-
-def about_dialog(parent,title,text):
-
- QtWidgets.QMessageBox.about(parent,title,text)
-
+ QDesktopServices.openUrl(QUrl(url))
+
+
+def about_dialog(parent, title, text):
+ QtWidgets.QMessageBox.about(parent, title, text)
+
+
def get_save_filename(suffix):
-
- rv,_ = QFileDialog.getSaveFileName(filter='*.{}'.format(suffix))
- if rv != '' and not rv.endswith(suffix): rv += '.'+suffix
-
+ rv, _ = QFileDialog.getSaveFileName(filter="*.{}".format(suffix))
+ if rv != "" and not rv.endswith(suffix):
+ rv += "." + suffix
+
return rv
+
def get_open_filename(suffix, curr_dir):
-
- rv,_ = QFileDialog.getOpenFileName(directory=curr_dir, filter='*.{}'.format(suffix))
- if rv != '' and not rv.endswith(suffix): rv += '.'+suffix
-
+ rv, _ = QFileDialog.getOpenFileName(
+ directory=curr_dir, filter="*.{}".format(suffix)
+ )
+ if rv != "" and not rv.endswith(suffix):
+ rv += "." + suffix
+
return rv
-def check_gtihub_for_updates(parent,
- mod,
- github_org='cadquery',
- github_proj='cadquery'):
-
- url = f'https://api.github.com/repos/{github_org}/{github_proj}/releases'
+
+def check_gtihub_for_updates(
+ parent, mod, github_org="cadquery", github_proj="cadquery"
+):
+ url = f"https://api.github.com/repos/{github_org}/{github_proj}/releases"
resp = requests.get(url).json()
-
- newer = [el['tag_name'] for el in resp if not el['draft'] and \
- parse_version(el['tag_name']) > parse_version(mod.__version__)]
-
+
+ newer = [
+ el["tag_name"]
+ for el in resp
+ if not el["draft"]
+ and parse_version(el["tag_name"]) > parse_version(mod.__version__)
+ ]
+
if newer:
- title='Updates available'
- text=f'There are newer versions of {github_proj} ' \
- f'available on github:\n' + '\n'.join(newer)
-
+ title = "Updates available"
+ text = (
+ f"There are newer versions of {github_proj} "
+ f"available on github:\n" + "\n".join(newer)
+ )
+
else:
- title='No updates available'
- text=f'You are already using the latest version of {github_proj}'
-
- QtWidgets.QMessageBox.about(parent,title,text)
-
-def confirm(parent,title,msg):
-
+ title = "No updates available"
+ text = f"You are already using the latest version of {github_proj}"
+
+ QtWidgets.QMessageBox.about(parent, title, text)
+
+
+def confirm(parent, title, msg):
rv = QMessageBox.question(parent, title, msg, QMessageBox.Yes, QMessageBox.No)
-
+
return True if rv == QMessageBox.Yes else False
diff --git a/cq_editor/widgets/console.py b/cq_editor/widgets/console.py
index 34d901d..87dfbaf 100644
--- a/cq_editor/widgets/console.py
+++ b/cq_editor/widgets/console.py
@@ -1,23 +1,23 @@
from PySide6.QtWidgets import QApplication
-from PySide6.QtCore import pyqtSlot
+from PySide6.QtCore import Slot as pyqtSlot
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager
from ..mixins import ComponentMixin
-class ConsoleWidget(RichJupyterWidget,ComponentMixin):
-
- name = 'Console'
+
+class ConsoleWidget(RichJupyterWidget, ComponentMixin):
+ name = "Console"
def __init__(self, customBanner=None, namespace=dict(), *args, **kwargs):
super(ConsoleWidget, self).__init__(*args, **kwargs)
-# if not customBanner is None:
-# self.banner = customBanner
+ # if not customBanner is None:
+ # self.banner = customBanner
self.font_size = 6
- self.style_sheet = '''
- '''
- self.syntax_style = 'zenburn' #CHANGES FOR DARKMODE
-
+ """
+ self.syntax_style = "zenburn" # CHANGES FOR DARKMODE
+
self.kernel_manager = kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel(show_banner=False)
- kernel_manager.kernel.gui = 'qt'
+ kernel_manager.kernel.gui = "qt"
kernel_manager.kernel.shell.banner1 = ""
-
+
self.kernel_client = kernel_client = self._kernel_manager.client()
kernel_client.start_channels()
@@ -51,9 +51,9 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
QApplication.instance().exit()
self.exit_requested.connect(stop)
-
+
self.clear()
-
+
self.push_vars(namespace)
@pyqtSlot(dict)
@@ -70,7 +70,6 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
"""
self._control.clear()
-
def print_text(self, text):
"""
Prints some plain text to the console
@@ -82,20 +81,17 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
Execute a command in the frame of the console widget
"""
self._execute(command, False)
-
- def _banner_default(self):
-
- return ''
-
+ def _banner_default(self):
+ return ""
+
+
if __name__ == "__main__":
-
-
import sys
-
+
app = QApplication(sys.argv)
-
- console = ConsoleWidget(customBanner='IPython console test')
+
+ console = ConsoleWidget(customBanner="IPython console test")
console.show()
-
+
sys.exit(app.exec_())
diff --git a/cq_editor/widgets/cq_object_inspector.py b/cq_editor/widgets/cq_object_inspector.py
index 2e30464..52029df 100644
--- a/cq_editor/widgets/cq_object_inspector.py
+++ b/cq_editor/widgets/cq_object_inspector.py
@@ -1,6 +1,6 @@
-from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QAction
-from PySide6.QtCore import Qt, pyqtSlot, pyqtSignal
-
+from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem
+from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal
+from PySide6.QtGui import QAction
from OCP.AIS import AIS_ColoredShape
from OCP.gp import gp_Ax3
@@ -10,63 +10,62 @@ from ..mixins import ComponentMixin
from ..icons import icon
-
class CQChildItem(QTreeWidgetItem):
-
- def __init__(self,cq_item,**kwargs):
-
- super(CQChildItem,self).\
- __init__([type(cq_item).__name__,str(cq_item)],**kwargs)
-
+ def __init__(self, cq_item, **kwargs):
+ super(CQChildItem, self).__init__(
+ [type(cq_item).__name__, str(cq_item)], **kwargs
+ )
+
self.cq_item = cq_item
+
class CQStackItem(QTreeWidgetItem):
-
- def __init__(self,name,workplane=None,**kwargs):
-
- super(CQStackItem,self).__init__([name,''],**kwargs)
-
+ def __init__(self, name, workplane=None, **kwargs):
+ super(CQStackItem, self).__init__([name, ""], **kwargs)
+
self.workplane = workplane
-class CQObjectInspector(QTreeWidget,ComponentMixin):
-
- name = 'CQ Object Inspector'
-
+class CQObjectInspector(QTreeWidget, ComponentMixin):
+ name = "CQ Object Inspector"
+
sigRemoveObjects = pyqtSignal(list)
- sigDisplayObjects = pyqtSignal(list,bool)
- sigShowPlane = pyqtSignal([bool],[bool,float])
+ sigDisplayObjects = pyqtSignal(list, bool)
+ sigShowPlane = pyqtSignal(bool)
+ sigShowPlane2 = pyqtSignal(bool, float)
sigChangePlane = pyqtSignal(gp_Ax3)
-
- def __init__(self,parent):
-
- super(CQObjectInspector,self).__init__(parent)
+
+ def __init__(self, parent):
+ super(CQObjectInspector, self).__init__(parent)
self.setHeaderHidden(False)
self.setRootIsDecorated(True)
self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setColumnCount(2)
- self.setHeaderLabels(['Type','Value'])
-
+ self.setHeaderLabels(["Type", "Value"])
+
self.root = self.invisibleRootItem()
self.inspected_items = []
-
- self._toolbar_actions = \
- [QAction(icon('inspect'),'Inspect CQ object',self,\
- toggled=self.inspect,checkable=True)]
-
+
+ self._toolbar_actions = [
+ QAction(
+ icon("inspect"),
+ "Inspect CQ object",
+ self,
+ toggled=self.inspect,
+ checkable=True,
+ )
+ ]
+
self.addActions(self._toolbar_actions)
-
+
def menuActions(self):
-
- return {'Tools' : self._toolbar_actions}
-
+ return {"Tools": self._toolbar_actions}
+
def toolbarActions(self):
-
return self._toolbar_actions
-
+
@pyqtSlot(bool)
- def inspect(self,value):
-
+ def inspect(self, value):
if value:
self.itemSelectionChanged.connect(self.handleSelection)
self.itemSelectionChanged.emit()
@@ -74,56 +73,52 @@ class CQObjectInspector(QTreeWidget,ComponentMixin):
self.itemSelectionChanged.disconnect(self.handleSelection)
self.sigRemoveObjects.emit(self.inspected_items)
self.sigShowPlane.emit(False)
-
- @pyqtSlot()
+
+ @pyqtSlot()
def handleSelection(self):
-
inspected_items = self.inspected_items
self.sigRemoveObjects.emit(inspected_items)
inspected_items.clear()
-
+
items = self.selectedItems()
if len(items) == 0:
return
-
+
item = items[-1]
if type(item) is CQStackItem:
cq_plane = item.workplane.plane
dim = item.workplane.largestDimension()
- plane = gp_Ax3(cq_plane.origin.toPnt(),
- cq_plane.zDir.toDir(),
- cq_plane.xDir.toDir())
+ plane = gp_Ax3(
+ cq_plane.origin.toPnt(), cq_plane.zDir.toDir(), cq_plane.xDir.toDir()
+ )
self.sigChangePlane.emit(plane)
- self.sigShowPlane[bool,float].emit(True,dim)
-
+ self.sigShowPlane[bool, float].emit(True, dim)
+
for child in (item.child(i) for i in range(item.childCount())):
obj = child.cq_item
- if hasattr(obj,'wrapped') and type(obj) != Vector:
+ if hasattr(obj, "wrapped") and type(obj) != Vector:
ais = AIS_ColoredShape(obj.wrapped)
inspected_items.append(ais)
-
+
else:
self.sigShowPlane.emit(False)
obj = item.cq_item
- if hasattr(obj,'wrapped') and type(obj) != Vector:
+ if hasattr(obj, "wrapped") and type(obj) != Vector:
ais = AIS_ColoredShape(obj.wrapped)
inspected_items.append(ais)
-
- self.sigDisplayObjects.emit(inspected_items,False)
-
+
+ self.sigDisplayObjects.emit(inspected_items, False)
+
@pyqtSlot(object)
- def setObject(self,cq_obj):
-
+ def setObject(self, cq_obj):
self.root.takeChildren()
-
+
# iterate through parent objects if they exist
- while getattr(cq_obj, 'parent', None):
- current_frame = CQStackItem(str(cq_obj.plane.origin),workplane=cq_obj)
+ while getattr(cq_obj, "parent", None):
+ current_frame = CQStackItem(str(cq_obj.plane.origin), workplane=cq_obj)
self.root.addChild(current_frame)
-
+
for obj in cq_obj.objects:
current_frame.addChild(CQChildItem(obj))
-
+
cq_obj = cq_obj.parent
-
-
diff --git a/cq_editor/widgets/debugger.py b/cq_editor/widgets/debugger.py
index 4ccd5e8..c040962 100644
--- a/cq_editor/widgets/debugger.py
+++ b/cq_editor/widgets/debugger.py
@@ -9,12 +9,13 @@ from PySide6 import QtCore
from PySide6.QtCore import (
Qt,
QObject,
- pyqtSlot,
- pyqtSignal,
+ Slot as pyqtSlot,
+ Signal as pyqtSignal,
QEventLoop,
QAbstractTableModel,
)
-from PySide6.QtWidgets import QAction, QTableView
+from PySide6.QtWidgets import QTableView
+from PySide6.QtGui import QAction
from logbook import info
from path import Path
from pyqtgraph.parametertree import Parameter
diff --git a/cq_editor/widgets/editor.py b/cq_editor/widgets/editor.py
index c8bdc80..a0ca916 100644
--- a/cq_editor/widgets/editor.py
+++ b/cq_editor/widgets/editor.py
@@ -2,9 +2,11 @@ import os
from modulefinder import ModuleFinder
from spyder.plugins.editor.widgets.codeeditor import CodeEditor
-from PySide6.QtCore import pyqtSignal, QFileSystemWatcher, QTimer
-from PySide6.QtWidgets import QAction, QFileDialog
-from PySide6.QtGui import QFontDatabase
+from PySide6.QtCore import Signal as pyqtSignal
+from PySide6.QtCore import QFileSystemWatcher, QTimerEvent, QTimer
+
+from PySide6.QtWidgets import QFileDialog
+from PySide6.QtGui import QFontDatabase, QAction
from path import Path
import sys
@@ -16,91 +18,104 @@ from ..utils import get_save_filename, get_open_filename, confirm
from ..icons import icon
-class Editor(CodeEditor,ComponentMixin):
- name = 'Code Editor'
+class Editor(CodeEditor, ComponentMixin):
+ name = "Code Editor"
# This signal is emitted whenever the currently-open file changes and
# autoreload is enabled.
triggerRerender = pyqtSignal(bool)
sigFilenameChanged = pyqtSignal(str)
- preferences = Parameter.create(name='Preferences',children=[
- {'name': 'Font size', 'type': 'int', 'value': 12},
- {'name': 'Autoreload', 'type': 'bool', 'value': False},
- {'name': 'Autoreload delay', 'type': 'int', 'value': 50},
- {'name': 'Autoreload: watch imported modules', 'type': 'bool', 'value': False},
- {'name': 'Line wrap', 'type': 'bool', 'value': False},
- {'name': 'Color scheme', 'type': 'list',
- 'values': ['Spyder','Monokai','Zenburn'], 'value': 'Spyder'}])
+ preferences = Parameter.create(
+ name="Preferences",
+ children=[
+ {"name": "Font size", "type": "int", "value": 12},
+ {"name": "Autoreload", "type": "bool", "value": False},
+ {"name": "Autoreload delay", "type": "int", "value": 50},
+ {
+ "name": "Autoreload: watch imported modules",
+ "type": "bool",
+ "value": False,
+ },
+ {"name": "Line wrap", "type": "bool", "value": False},
+ {
+ "name": "Color scheme",
+ "type": "list",
+ "values": ["Spyder", "Monokai", "Zenburn"],
+ "value": "Spyder",
+ },
+ ],
+ )
- EXTENSIONS = 'py'
-
- def __init__(self,parent=None):
+ EXTENSIONS = "py"
+ def __init__(self, parent=None):
self._watched_file = None
- super(Editor,self).__init__(parent)
+ super(Editor, self).__init__(parent)
ComponentMixin.__init__(self)
- self.setup_editor(linenumbers=True,
- markers=True,
- edge_line=False,
- tab_mode=False,
- show_blanks=True,
- font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
- language='Python',
- filename='')
+ self.setup_editor(
+ linenumbers=True,
+ markers=True,
+ edge_line=False,
+ tab_mode=False,
+ show_blanks=True,
+ font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
+ language="Python",
+ filename="",
+ )
- self._actions = \
- {'File' : [QAction(icon('new'),
- 'New',
- self,
- shortcut='ctrl+N',
- triggered=self.new),
- QAction(icon('open'),
- 'Open',
- self,
- shortcut='ctrl+O',
- triggered=self.open),
- QAction(icon('save'),
- 'Save',
- self,
- shortcut='ctrl+S',
- triggered=self.save),
- QAction(icon('save_as'),
- 'Save as',
- self,
- shortcut='ctrl+shift+S',
- triggered=self.save_as),
- QAction(icon('autoreload'),
- 'Automatic reload and preview',
- self,triggered=self.autoreload,
- checkable=True,
- checked=False,
- objectName='autoreload'),
- ]}
+ self._actions = {
+ "File": [
+ QAction(
+ icon("new"), "New", self, shortcut="ctrl+N", triggered=self.new
+ ),
+ QAction(
+ icon("open"), "Open", self, shortcut="ctrl+O", triggered=self.open
+ ),
+ QAction(
+ icon("save"), "Save", self, shortcut="ctrl+S", triggered=self.save
+ ),
+ QAction(
+ icon("save_as"),
+ "Save as",
+ self,
+ shortcut="ctrl+shift+S",
+ triggered=self.save_as,
+ ),
+ QAction(
+ icon("autoreload"),
+ "Automatic reload and preview",
+ self,
+ triggered=self.autoreload,
+ checkable=True,
+ checked=False,
+ objectName="autoreload",
+ ),
+ ]
+ }
for a in self._actions.values():
self.addActions(a)
-
self._fixContextMenu()
# autoreload support
self._file_watcher = QFileSystemWatcher(self)
# we wait for 50ms after a file change for the file to be written completely
self._file_watch_timer = QTimer(self)
- self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
+ self._file_watch_timer.setInterval(self.preferences["Autoreload delay"])
self._file_watch_timer.setSingleShot(True)
self._file_watcher.fileChanged.connect(
- lambda val: self._file_watch_timer.start())
+ lambda val: self._file_watch_timer.start()
+ )
self._file_watch_timer.timeout.connect(self._file_changed)
self.updatePreferences()
def _fixContextMenu(self):
-
menu = self.menu
menu.removeAction(self.run_cell_action)
@@ -108,68 +123,66 @@ class Editor(CodeEditor,ComponentMixin):
menu.removeAction(self.run_selection_action)
menu.removeAction(self.re_run_last_cell_action)
- def updatePreferences(self,*args):
-
- self.set_color_scheme(self.preferences['Color scheme'])
+ def updatePreferences(self, *args):
+ self.set_color_scheme(self.preferences["Color scheme"])
font = self.font()
- font.setPointSize(self.preferences['Font size'])
+ font.setPointSize(self.preferences["Font size"])
self.set_font(font)
- self.findChild(QAction, 'autoreload') \
- .setChecked(self.preferences['Autoreload'])
+ self.findChild(QAction, "autoreload").setChecked(self.preferences["Autoreload"])
- self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
+ self._file_watch_timer.setInterval(self.preferences["Autoreload delay"])
- self.toggle_wrap_mode(self.preferences['Line wrap'])
+ self.toggle_wrap_mode(self.preferences["Line wrap"])
self._clear_watched_paths()
self._watch_paths()
def confirm_discard(self):
-
if self.modified:
- rv = confirm(self,'Please confirm','Current document is not saved - do you want to continue?')
+ rv = confirm(
+ self,
+ "Please confirm",
+ "Current document is not saved - do you want to continue?",
+ )
else:
rv = True
return rv
def new(self):
+ if not self.confirm_discard():
+ return
- if not self.confirm_discard(): return
-
- self.set_text('')
- self.filename = ''
+ self.set_text("")
+ self.filename = ""
self.reset_modified()
def open(self):
-
- if not self.confirm_discard(): return
+ if not self.confirm_discard():
+ return
curr_dir = Path(self.filename).abspath().dirname()
fname = get_open_filename(self.EXTENSIONS, curr_dir)
- if fname != '':
+ if fname != "":
self.load_from_file(fname)
- def load_from_file(self,fname):
-
+ def load_from_file(self, fname):
self.set_text_from_file(fname)
self.filename = fname
self.reset_modified()
def save(self):
-
- if self._filename != '':
-
- if self.preferences['Autoreload']:
+ if self._filename != "":
+ if self.preferences["Autoreload"]:
self._file_watcher.blockSignals(True)
self._file_watch_timer.stop()
- with open(self._filename, 'w') as f:
+ with open(self._filename, "w") as f:
f.write(self.toPlainText())
- if self.preferences['Autoreload']:
+ if self.preferences["Autoreload"]:
self._file_watcher.blockSignals(False)
self.triggerRerender.emit(True)
@@ -179,20 +192,25 @@ class Editor(CodeEditor,ComponentMixin):
self.save_as()
def save_as(self):
-
fname = get_save_filename(self.EXTENSIONS)
- if fname != '':
- with open(fname,'w') as f:
+ if fname != "":
+ with open(fname, "w") as f:
f.write(self.toPlainText())
self.filename = fname
self.reset_modified()
def _update_filewatcher(self):
- if self._watched_file and (self._watched_file != self.filename or not self.preferences['Autoreload']):
+ if self._watched_file and (
+ self._watched_file != self.filename or not self.preferences["Autoreload"]
+ ):
self._clear_watched_paths()
self._watched_file = None
- if self.preferences['Autoreload'] and self.filename and self.filename != self._watched_file:
+ if (
+ self.preferences["Autoreload"]
+ and self.filename
+ and self.filename != self._watched_file
+ ):
self._watched_file = self._filename
self._watch_paths()
@@ -214,8 +232,8 @@ class Editor(CodeEditor,ComponentMixin):
def _watch_paths(self):
if Path(self._filename).exists():
self._file_watcher.addPath(self._filename)
- if self.preferences['Autoreload: watch imported modules']:
- module_paths = self.get_imported_module_paths(self._filename)
+ if self.preferences["Autoreload: watch imported modules"]:
+ module_paths = self.get_imported_module_paths(self._filename)
if module_paths:
self._file_watcher.addPaths(module_paths)
@@ -228,51 +246,45 @@ class Editor(CodeEditor,ComponentMixin):
# Turn autoreload on/off.
def autoreload(self, enabled):
- self.preferences['Autoreload'] = enabled
+ self.preferences["Autoreload"] = enabled
self._update_filewatcher()
def reset_modified(self):
-
self.document().setModified(False)
-
+
@property
def modified(self):
-
return self.document().isModified()
- def saveComponentState(self,store):
+ def saveComponentState(self, store):
+ if self.filename != "":
+ store.setValue(self.name + "/state", self.filename)
- if self.filename != '':
- store.setValue(self.name+'/state',self.filename)
+ def restoreComponentState(self, store):
+ filename = store.value(self.name + "/state")
- def restoreComponentState(self,store):
-
- filename = store.value(self.name+'/state')
-
- if filename and self.filename == '':
+ if filename and self.filename == "":
try:
self.load_from_file(filename)
except IOError:
- self._logger.warning(f'could not open {filename}')
-
+ self._logger.warning(f"could not open {filename}")
def get_imported_module_paths(self, module_path):
-
finder = ModuleFinder([os.path.dirname(module_path)])
imported_modules = []
try:
finder.run_script(module_path)
except SyntaxError as err:
- self._logger.warning(f'Syntax error in {module_path}: {err}')
+ self._logger.warning(f"Syntax error in {module_path}: {err}")
except Exception as err:
self._logger.warning(
- f'Cannot determine imported modules in {module_path}: {type(err).__name__} {err}'
+ f"Cannot determine imported modules in {module_path}: {type(err).__name__} {err}"
)
else:
for module_name, module in finder.modules.items():
- if module_name != '__main__':
- path = getattr(module, '__file__', None)
+ if module_name != "__main__":
+ path = getattr(module, "__file__", None)
if path is not None and os.path.isfile(path):
imported_modules.append(path)
@@ -280,7 +292,6 @@ class Editor(CodeEditor,ComponentMixin):
if __name__ == "__main__":
-
from PySide6.QtWidgets import QApplication
app = QApplication(sys.argv)
diff --git a/cq_editor/widgets/object_tree.py b/cq_editor/widgets/object_tree.py
index d0aa78b..3ebb20c 100644
--- a/cq_editor/widgets/object_tree.py
+++ b/cq_editor/widgets/object_tree.py
@@ -1,12 +1,12 @@
from PySide6.QtWidgets import (
QTreeWidget,
QTreeWidgetItem,
- QAction,
QMenu,
QWidget,
QAbstractItemView,
)
-from PySide6.QtCore import Qt, pyqtSlot, pyqtSignal
+from PySide6.QtGui import QAction
+from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal
from pyqtgraph.parametertree import Parameter, ParameterTree
from OCP.AIS import AIS_Line
@@ -114,7 +114,8 @@ class ObjectTree(QWidget, ComponentMixin):
],
)
- sigObjectsAdded = pyqtSignal([list], [list, bool])
+ sigObjectsAdded = pyqtSignal(list)
+ sigObjectsAdded2 = pyqtSignal(list, bool)
sigObjectsRemoved = pyqtSignal(list)
sigCQObjectSelected = pyqtSignal(object)
sigAISObjectsSelected = pyqtSignal(list)
diff --git a/cq_editor/widgets/occt_widget.py b/cq_editor/widgets/occt_widget.py
index 825dc0b..fe21bd0 100644
--- a/cq_editor/widgets/occt_widget.py
+++ b/cq_editor/widgets/occt_widget.py
@@ -2,7 +2,7 @@ from sys import platform
from PySide6.QtWidgets import QWidget, QApplication
-from PySide6.QtCore import pyqtSlot, pyqtSignal, Qt, QEvent
+from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal, Qt, QEvent
import OCP
@@ -15,161 +15,144 @@ from OCP.Quantity import Quantity_Color, Quantity_TOC_RGB as TOC_RGB
ZOOM_STEP = 0.9
-
+
class OCCTWidget(QWidget):
-
sigObjectSelected = pyqtSignal(list)
-
- def __init__(self,parent=None):
-
- super(OCCTWidget,self).__init__(parent)
-
+
+ def __init__(self, parent=None):
+ super(OCCTWidget, self).__init__(parent)
+
self.setAttribute(Qt.WA_NativeWindow)
self.setAttribute(Qt.WA_PaintOnScreen)
self.setAttribute(Qt.WA_NoSystemBackground)
-
+
self._initialized = False
self._needs_update = False
-
- #OCCT secific things
+
+ # OCCT secific things
self.display_connection = Aspect_DisplayConnection()
self.graphics_driver = OpenGl_GraphicDriver(self.display_connection)
-
+
self.viewer = V3d_Viewer(self.graphics_driver)
self.view = self.viewer.CreateView()
self.context = AIS_InteractiveContext(self.viewer)
-
- #Trihedorn, lights, etc
+
+ # Trihedorn, lights, etc
self.prepare_display()
-
+
def prepare_display(self):
-
view = self.view
-
+
params = view.ChangeRenderingParams()
params.NbMsaaSamples = 8
params.IsAntialiasingEnabled = True
-
+
view.TriedronDisplay(
- Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER,
- Quantity_Color(), 0.1)
-
- view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB))
+ Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER, Quantity_Color(), 0.1
+ )
+
+ view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB))
viewer = self.viewer
-
+
viewer.SetDefaultLights()
viewer.SetLightOn()
-
+
ctx = self.context
-
+
ctx.SetDisplayMode(AIS_DisplayMode.AIS_Shaded, True)
ctx.DefaultDrawer().SetFaceBoundaryDraw(True)
-
+
def wheelEvent(self, event):
-
delta = event.angleDelta().y()
- factor = ZOOM_STEP if delta<0 else 1/ZOOM_STEP
-
+ factor = ZOOM_STEP if delta < 0 else 1 / ZOOM_STEP
+
self.view.SetZoom(factor)
-
- def mousePressEvent(self,event):
-
+
+ def mousePressEvent(self, event):
pos = event.pos()
-
+
if event.button() == Qt.LeftButton:
self.view.StartRotation(pos.x(), pos.y())
elif event.button() == Qt.RightButton:
self.view.StartZoomAtPoint(pos.x(), pos.y())
-
+
self.old_pos = pos
-
- def mouseMoveEvent(self,event):
-
+
+ def mouseMoveEvent(self, event):
pos = event.pos()
- x,y = pos.x(),pos.y()
-
+ x, y = pos.x(), pos.y()
+
if event.buttons() == Qt.LeftButton:
- self.view.Rotation(x,y)
-
+ self.view.Rotation(x, y)
+
elif event.buttons() == Qt.MiddleButton:
- self.view.Pan(x - self.old_pos.x(),
- self.old_pos.y() - y, theToStart=True)
-
+ self.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True)
+
elif event.buttons() == Qt.RightButton:
- self.view.Pan(x - self.old_pos.x(),
- self.old_pos.y() - y, theToStart=True)
-
+ self.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True)
+
self.old_pos = pos
-
- def mouseReleaseEvent(self,event):
-
+
+ def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
pos = event.pos()
- x,y = pos.x(),pos.y()
-
- self.context.MoveTo(x,y,self.view,True)
-
+ x, y = pos.x(), pos.y()
+
+ self.context.MoveTo(x, y, self.view, True)
+
self._handle_selection()
-
+
def _handle_selection(self):
-
self.context.Select(True)
self.context.InitSelected()
-
+
selected = []
if self.context.HasSelectedShape():
selected.append(self.context.SelectedShape())
-
+
self.sigObjectSelected.emit(selected)
def paintEngine(self):
-
return None
-
+
def paintEvent(self, event):
-
if not self._initialized:
self._initialize()
else:
self.view.Redraw()
def showEvent(self, event):
-
- super(OCCTWidget,self).showEvent(event)
-
- def resizeEvent(self, event):
-
- super(OCCTWidget,self).resizeEvent(event)
-
- self.view.MustBeResized()
-
- def _initialize(self):
+ super(OCCTWidget, self).showEvent(event)
+ def resizeEvent(self, event):
+ super(OCCTWidget, self).resizeEvent(event)
+
+ self.view.MustBeResized()
+
+ def _initialize(self):
wins = {
- 'darwin' : self._get_window_osx,
- 'linux' : self._get_window_linux,
- 'win32': self._get_window_win
+ "darwin": self._get_window_osx,
+ "linux": self._get_window_linux,
+ "win32": self._get_window_win,
}
- self.view.SetWindow(wins.get(platform,self._get_window_linux)(self.winId()))
+ self.view.SetWindow(wins.get(platform, self._get_window_linux)(self.winId()))
self._initialized = True
-
- def _get_window_win(self,wid):
-
+
+ def _get_window_win(self, wid):
from OCP.WNT import WNT_Window
-
+
+ print(wid)
return WNT_Window(wid.ascapsule())
- def _get_window_linux(self,wid):
-
+ def _get_window_linux(self, wid):
from OCP.Xw import Xw_Window
-
- return Xw_Window(self.display_connection,int(wid))
-
- def _get_window_osx(self,wid):
-
+
+ return Xw_Window(self.display_connection, int(wid))
+
+ def _get_window_osx(self, wid):
from OCP.Cocoa import Cocoa_Window
-
+
return Cocoa_Window(wid.ascapsule())
diff --git a/cq_editor/widgets/traceback_viewer.py b/cq_editor/widgets/traceback_viewer.py
index e675af5..a8c04b3 100644
--- a/cq_editor/widgets/traceback_viewer.py
+++ b/cq_editor/widgets/traceback_viewer.py
@@ -1,97 +1,89 @@
from traceback import extract_tb, format_exception_only
-from PySide6.QtWidgets import (QWidget, QTreeWidget, QTreeWidgetItem, QAction,
- QLabel)
-from PySide6.QtCore import Qt, pyqtSlot, pyqtSignal
+from PySide6.QtWidgets import QWidget, QTreeWidget, QTreeWidgetItem, QLabel
+from PySide6.QtGui import QAction
+from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal
from ..mixins import ComponentMixin
from ..utils import layout
-class TracebackTree(QTreeWidget):
- name = 'Traceback Viewer'
-
- def __init__(self,parent):
-
- super(TracebackTree,self).__init__(parent)
+class TracebackTree(QTreeWidget):
+ name = "Traceback Viewer"
+
+ def __init__(self, parent):
+ super(TracebackTree, self).__init__(parent)
self.setHeaderHidden(False)
self.setItemsExpandable(False)
self.setRootIsDecorated(False)
self.setContextMenuPolicy(Qt.ActionsContextMenu)
-
+
self.setColumnCount(3)
- self.setHeaderLabels(['File','Line','Code'])
-
-
+ self.setHeaderLabels(["File", "Line", "Code"])
+
self.root = self.invisibleRootItem()
-class TracebackPane(QWidget,ComponentMixin):
-
+
+class TracebackPane(QWidget, ComponentMixin):
sigHighlightLine = pyqtSignal(int)
-
- def __init__(self,parent):
-
- super(TracebackPane,self).__init__(parent)
-
+
+ def __init__(self, parent):
+ super(TracebackPane, self).__init__(parent)
+
self.tree = TracebackTree(self)
self.current_exception = QLabel(self)
- self.current_exception.setStyleSheet(\
- "QLabel {color : red; }");
-
- layout(self,
- (self.current_exception,
- self.tree),
- self)
-
+ self.current_exception.setStyleSheet("QLabel {color : red; }")
+
+ layout(self, (self.current_exception, self.tree), self)
+
self.tree.currentItemChanged.connect(self.handleSelection)
-
- @pyqtSlot(object,str)
- def addTraceback(self,exc_info,code):
-
+
+ @pyqtSlot(object, str)
+ def addTraceback(self, exc_info, code):
self.tree.clear()
-
+
if exc_info:
- t,exc,tb = exc_info
-
+ t, exc, tb = exc_info
+
root = self.tree.root
code = code.splitlines()
- tb = [t for t in extract_tb(tb) if '' in t.filename] #ignore highest frames (debug, exec)
-
+ tb = [
+ t for t in extract_tb(tb) if "" in t.filename
+ ] # ignore highest frames (debug, exec)
+
for el in tb:
- #workaround of the traceback module
- if el.line == '':
- line = code[el.lineno-1].strip()
+ # workaround of the traceback module
+ if el.line == "":
+ line = code[el.lineno - 1].strip()
else:
line = el.line
-
- root.addChild(QTreeWidgetItem([el.filename,
- str(el.lineno),
- line]))
+
+ root.addChild(QTreeWidgetItem([el.filename, str(el.lineno), line]))
exc_name = t.__name__
exc_msg = str(exc)
- exc_msg = exc_msg.replace('<', '<').replace('>', '>') #replace <>
+ exc_msg = exc_msg.replace("<", "<").replace(">", ">") # replace <>
+
+ self.current_exception.setText("{}: {}".format(exc_name, exc_msg))
- self.current_exception.\
- setText('{}: {}'.format(exc_name,exc_msg))
-
# handle the special case of a SyntaxError
- if t is SyntaxError:
- root.addChild(QTreeWidgetItem(
- [exc.filename,
- str(exc.lineno),
- exc.text.strip() if exc.text else '']
- ))
+ if t is SyntaxError:
+ root.addChild(
+ QTreeWidgetItem(
+ [
+ exc.filename,
+ str(exc.lineno),
+ exc.text.strip() if exc.text else "",
+ ]
+ )
+ )
else:
- self.current_exception.setText('')
+ self.current_exception.setText("")
- @pyqtSlot(QTreeWidgetItem,QTreeWidgetItem)
- def handleSelection(self,item,*args):
-
+ @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
+ def handleSelection(self, item, *args):
if item:
- f,line = item.data(0,0),int(item.data(1,0))
-
- if '' in f:
+ f, line = item.data(0, 0), int(item.data(1, 0))
+
+ if "" in f:
self.sigHighlightLine.emit(line)
-
-
diff --git a/cq_editor/widgets/viewer.py b/cq_editor/widgets/viewer.py
index f3e4b51..35c1968 100644
--- a/cq_editor/widgets/viewer.py
+++ b/cq_editor/widgets/viewer.py
@@ -1,14 +1,21 @@
-from PySide6.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication, QAction
+from PySide6.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication
-from PySide6.QtCore import pyqtSlot, pyqtSignal
-from PySide6.QtGui import QIcon
+from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal
+from PySide6.QtGui import QIcon, QAction
-from OCP.Graphic3d import Graphic3d_Camera, Graphic3d_StereoMode, Graphic3d_NOM_JADE,\
- Graphic3d_MaterialAspect
-from OCP.AIS import AIS_Shaded,AIS_WireFrame, AIS_ColoredShape, AIS_Axis
+from OCP.Graphic3d import (
+ Graphic3d_Camera,
+ Graphic3d_StereoMode,
+ Graphic3d_NOM_JADE,
+ Graphic3d_MaterialAspect,
+)
+from OCP.AIS import AIS_Shaded, AIS_WireFrame, AIS_ColoredShape, AIS_Axis
from OCP.Aspect import Aspect_GDM_Lines, Aspect_GT_Rectangular
-from OCP.Quantity import Quantity_NOC_BLACK as BLACK, Quantity_TOC_RGB as TOC_RGB,\
- Quantity_Color
+from OCP.Quantity import (
+ Quantity_NOC_BLACK as BLACK,
+ Quantity_TOC_RGB as TOC_RGB,
+ Quantity_Color,
+)
from OCP.Geom import Geom_Axis1Placement
from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt, gp_Ax1
@@ -25,30 +32,66 @@ import qtawesome as qta
DEFAULT_EDGE_COLOR = Quantity_Color(BLACK)
DEFAULT_EDGE_WIDTH = 2
-class OCCViewer(QWidget,ComponentMixin):
- name = '3D Viewer'
+class OCCViewer(QWidget, ComponentMixin):
+ name = "3D Viewer"
- preferences = Parameter.create(name='Pref',children=[
- {'name': 'Fit automatically', 'type': 'bool', 'value': True},
- {'name': 'Use gradient', 'type': 'bool', 'value': False},
- {'name': 'Background color', 'type': 'color', 'value': (95,95,95)},
- {'name': 'Background color (aux)', 'type': 'color', 'value': (30,30,30)},
- {'name': 'Default object color', 'type': 'color', 'value': "#FF0"},
- {'name': 'Deviation', 'type': 'float', 'value': 1e-5, 'dec': True, 'step': 1},
- {'name': 'Angular deviation', 'type': 'float', 'value': 0.1, 'dec': True, 'step': 1},
- {'name': 'Projection Type', 'type': 'list', 'value': 'Orthographic',
- 'values': ['Orthographic', 'Perspective', 'Stereo', 'MonoLeftEye', 'MonoRightEye']},
- {'name': 'Stereo Mode', 'type': 'list', 'value': 'QuadBuffer',
- 'values': ['QuadBuffer', 'Anaglyph', 'RowInterlaced', 'ColumnInterlaced',
- 'ChessBoard', 'SideBySide', 'OverUnder']}])
- IMAGE_EXTENSIONS = 'png'
+ preferences = Parameter.create(
+ name="Pref",
+ children=[
+ {"name": "Fit automatically", "type": "bool", "value": True},
+ {"name": "Use gradient", "type": "bool", "value": False},
+ {"name": "Background color", "type": "color", "value": (95, 95, 95)},
+ {"name": "Background color (aux)", "type": "color", "value": (30, 30, 30)},
+ {"name": "Default object color", "type": "color", "value": "#FF0"},
+ {
+ "name": "Deviation",
+ "type": "float",
+ "value": 1e-5,
+ "dec": True,
+ "step": 1,
+ },
+ {
+ "name": "Angular deviation",
+ "type": "float",
+ "value": 0.1,
+ "dec": True,
+ "step": 1,
+ },
+ {
+ "name": "Projection Type",
+ "type": "list",
+ "value": "Orthographic",
+ "values": [
+ "Orthographic",
+ "Perspective",
+ "Stereo",
+ "MonoLeftEye",
+ "MonoRightEye",
+ ],
+ },
+ {
+ "name": "Stereo Mode",
+ "type": "list",
+ "value": "QuadBuffer",
+ "values": [
+ "QuadBuffer",
+ "Anaglyph",
+ "RowInterlaced",
+ "ColumnInterlaced",
+ "ChessBoard",
+ "SideBySide",
+ "OverUnder",
+ ],
+ },
+ ],
+ )
+ IMAGE_EXTENSIONS = "png"
sigObjectSelected = pyqtSignal(list)
- def __init__(self,parent=None):
-
- super(OCCViewer,self).__init__(parent)
+ def __init__(self, parent=None):
+ super(OCCViewer, self).__init__(parent)
ComponentMixin.__init__(self)
self.canvas = OCCTWidget()
@@ -56,15 +99,18 @@ class OCCViewer(QWidget,ComponentMixin):
self.create_actions(self)
- self.layout_ = layout(self,
- [self.canvas,],
- top_widget=self,
- margin=0)
- self.setup_default_drawer() #misspelled in original
+ self.layout_ = layout(
+ self,
+ [
+ self.canvas,
+ ],
+ top_widget=self,
+ margin=0,
+ )
+ self.setup_default_drawer() # misspelled in original
self.updatePreferences()
-
- def setup_default_drawer(self):
+ def setup_default_drawer(self):
# set the default color and material
material = Graphic3d_MaterialAspect(Graphic3d_NOM_JADE)
@@ -76,99 +122,129 @@ class OCCViewer(QWidget,ComponentMixin):
line_aspect = self.canvas.context.DefaultDrawer().FaceBoundaryAspect()
line_aspect.SetWidth(DEFAULT_EDGE_WIDTH)
line_aspect.SetColor(DEFAULT_EDGE_COLOR)
-
- def updatePreferences(self,*args):
- color1 = to_occ_color(self.preferences['Background color'])
- color2 = to_occ_color(self.preferences['Background color (aux)'])
+ def updatePreferences(self, *args):
+ color1 = to_occ_color(self.preferences["Background color"])
+ color2 = to_occ_color(self.preferences["Background color (aux)"])
- if not self.preferences['Use gradient']:
+ if not self.preferences["Use gradient"]:
color2 = color1
- self.canvas.view.SetBgGradientColors(color1,color2,theToUpdate=True)
-
+ self.canvas.view.SetBgGradientColors(color1, color2, theToUpdate=True)
+
self.canvas.update()
-
+
ctx = self.canvas.context
- ctx.SetDeviationCoefficient(self.preferences['Deviation'])
- ctx.SetDeviationAngle(self.preferences['Angular deviation'])
+ ctx.SetDeviationCoefficient(self.preferences["Deviation"])
+ ctx.SetDeviationAngle(self.preferences["Angular deviation"])
v = self._get_view()
camera = v.Camera()
- projection_type = self.preferences['Projection Type']
- camera.SetProjectionType(getattr(Graphic3d_Camera, f'Projection_{projection_type}',
- Graphic3d_Camera.Projection_Orthographic))
+ projection_type = self.preferences["Projection Type"]
+ camera.SetProjectionType(
+ getattr(
+ Graphic3d_Camera,
+ f"Projection_{projection_type}",
+ Graphic3d_Camera.Projection_Orthographic,
+ )
+ )
# onle relevant for stereo projection
- stereo_mode = self.preferences['Stereo Mode']
+ stereo_mode = self.preferences["Stereo Mode"]
params = v.ChangeRenderingParams()
- params.StereoMode = getattr(Graphic3d_StereoMode, f'Graphic3d_StereoMode_{stereo_mode}',
- Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer)
+ params.StereoMode = getattr(
+ Graphic3d_StereoMode,
+ f"Graphic3d_StereoMode_{stereo_mode}",
+ Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer,
+ )
- def create_actions(self,parent):
-
- self._actions = \
- {'View' : [QAction(qta.icon('fa.arrows-alt'),
- 'Fit (Shift+F1)',
- parent,
- shortcut='shift+F1',
- triggered=self.fit),
- QAction(QIcon(':/images/icons/isometric_view.svg'),
- 'Iso (Shift+F2)',
- parent,
- shortcut='shift+F2',
- triggered=self.iso_view),
- QAction(QIcon(':/images/icons/top_view.svg'),
- 'Top (Shift+F3)',
- parent,
- shortcut='shift+F3',
- triggered=self.top_view),
- QAction(QIcon(':/images/icons/bottom_view.svg'),
- 'Bottom (Shift+F4)',
- parent,
- shortcut='shift+F4',
- triggered=self.bottom_view),
- QAction(QIcon(':/images/icons/front_view.svg'),
- 'Front (Shift+F5)',
- parent,
- shortcut='shift+F5',
- triggered=self.front_view),
- QAction(QIcon(':/images/icons/back_view.svg'),
- 'Back (Shift+F6)',
- parent,
- shortcut='shift+F6',
- triggered=self.back_view),
- QAction(QIcon(':/images/icons/left_side_view.svg'),
- 'Left (Shift+F7)',
- parent,
- shortcut='shift+F7',
- triggered=self.left_view),
- QAction(QIcon(':/images/icons/right_side_view.svg'),
- 'Right (Shift+F8)',
- parent,
- shortcut='shift+F8',
- triggered=self.right_view),
- QAction(qta.icon('fa.square-o'),
- 'Wireframe (Shift+F9)',
- parent,
- shortcut='shift+F9',
- triggered=self.wireframe_view),
- QAction(qta.icon('fa.square'),
- 'Shaded (Shift+F10)',
- parent,
- shortcut='shift+F10',
- triggered=self.shaded_view)],
- 'Tools' : [QAction(icon('screenshot'),
- 'Screenshot',
- parent,
- triggered=self.save_screenshot)]}
+ def create_actions(self, parent):
+ self._actions = {
+ "View": [
+ QAction(
+ qta.icon("fa.arrows-alt"),
+ "Fit (Shift+F1)",
+ parent,
+ shortcut="shift+F1",
+ triggered=self.fit,
+ ),
+ QAction(
+ QIcon(":/images/icons/isometric_view.svg"),
+ "Iso (Shift+F2)",
+ parent,
+ shortcut="shift+F2",
+ triggered=self.iso_view,
+ ),
+ QAction(
+ QIcon(":/images/icons/top_view.svg"),
+ "Top (Shift+F3)",
+ parent,
+ shortcut="shift+F3",
+ triggered=self.top_view,
+ ),
+ QAction(
+ QIcon(":/images/icons/bottom_view.svg"),
+ "Bottom (Shift+F4)",
+ parent,
+ shortcut="shift+F4",
+ triggered=self.bottom_view,
+ ),
+ QAction(
+ QIcon(":/images/icons/front_view.svg"),
+ "Front (Shift+F5)",
+ parent,
+ shortcut="shift+F5",
+ triggered=self.front_view,
+ ),
+ QAction(
+ QIcon(":/images/icons/back_view.svg"),
+ "Back (Shift+F6)",
+ parent,
+ shortcut="shift+F6",
+ triggered=self.back_view,
+ ),
+ QAction(
+ QIcon(":/images/icons/left_side_view.svg"),
+ "Left (Shift+F7)",
+ parent,
+ shortcut="shift+F7",
+ triggered=self.left_view,
+ ),
+ QAction(
+ QIcon(":/images/icons/right_side_view.svg"),
+ "Right (Shift+F8)",
+ parent,
+ shortcut="shift+F8",
+ triggered=self.right_view,
+ ),
+ QAction(
+ qta.icon("fa.square-o"),
+ "Wireframe (Shift+F9)",
+ parent,
+ shortcut="shift+F9",
+ triggered=self.wireframe_view,
+ ),
+ QAction(
+ qta.icon("fa.square"),
+ "Shaded (Shift+F10)",
+ parent,
+ shortcut="shift+F10",
+ triggered=self.shaded_view,
+ ),
+ ],
+ "Tools": [
+ QAction(
+ icon("screenshot"),
+ "Screenshot",
+ parent,
+ triggered=self.save_screenshot,
+ )
+ ],
+ }
def toolbarActions(self):
-
- return self._actions['View']
-
+ return self._actions["View"]
def clear(self):
-
self.displayed_shapes = []
self.displayed_ais = []
self.canvas.context.EraseAll(True)
@@ -176,199 +252,167 @@ class OCCViewer(QWidget,ComponentMixin):
context.PurgeDisplay()
context.RemoveAll(True)
- def _display(self,shape):
-
+ def _display(self, shape):
ais = make_AIS(shape)
- self.canvas.context.Display(shape,True)
+ self.canvas.context.Display(shape, True)
self.displayed_shapes.append(shape)
self.displayed_ais.append(ais)
- #self.canvas._display.Repaint()
+ # self.canvas._display.Repaint()
@pyqtSlot(object)
- def display(self,ais):
-
+ def display(self, ais):
context = self._get_context()
- context.Display(ais,True)
+ context.Display(ais, True)
- if self.preferences['Fit automatically']: self.fit()
+ if self.preferences["Fit automatically"]:
+ self.fit()
@pyqtSlot(list)
- @pyqtSlot(list,bool)
- def display_many(self,ais_list,fit=None):
-
+ @pyqtSlot(list, bool)
+ def display_many(self, ais_list, fit=None):
context = self._get_context()
for ais in ais_list:
- context.Display(ais,True)
+ context.Display(ais, True)
- if self.preferences['Fit automatically'] and fit is None:
+ if self.preferences["Fit automatically"] and fit is None:
self.fit()
elif fit:
self.fit()
- @pyqtSlot(QTreeWidgetItem,int)
- def update_item(self,item,col):
-
+ @pyqtSlot(QTreeWidgetItem, int)
+ def update_item(self, item, col):
ctx = self._get_context()
if item.checkState(0):
- ctx.Display(item.ais,True)
+ ctx.Display(item.ais, True)
else:
- ctx.Erase(item.ais,True)
+ ctx.Erase(item.ais, True)
@pyqtSlot(list)
- def remove_items(self,ais_items):
-
+ def remove_items(self, ais_items):
ctx = self._get_context()
- for ais in ais_items: ctx.Erase(ais,True)
+ for ais in ais_items:
+ ctx.Erase(ais, True)
@pyqtSlot()
def redraw(self):
-
self._get_viewer().Redraw()
def fit(self):
-
self.canvas.view.FitAll()
def iso_view(self):
-
v = self._get_view()
- v.SetProj(1,-1,1)
+ v.SetProj(1, -1, 1)
v.SetTwist(0)
def bottom_view(self):
-
v = self._get_view()
- v.SetProj(0,0,-1)
+ v.SetProj(0, 0, -1)
v.SetTwist(0)
def top_view(self):
-
v = self._get_view()
- v.SetProj(0,0,1)
+ v.SetProj(0, 0, 1)
v.SetTwist(0)
def front_view(self):
-
v = self._get_view()
- v.SetProj(0,-1,0)
+ v.SetProj(0, -1, 0)
v.SetTwist(0)
def back_view(self):
-
v = self._get_view()
- v.SetProj(0,1,0)
+ v.SetProj(0, 1, 0)
v.SetTwist(0)
def left_view(self):
-
v = self._get_view()
- v.SetProj(-1,0,0)
+ v.SetProj(-1, 0, 0)
v.SetTwist(0)
def right_view(self):
-
v = self._get_view()
- v.SetProj(1,0,0)
+ v.SetProj(1, 0, 0)
v.SetTwist(0)
def shaded_view(self):
-
c = self._get_context()
c.SetDisplayMode(AIS_Shaded, True)
def wireframe_view(self):
-
c = self._get_context()
c.SetDisplayMode(AIS_WireFrame, True)
- def show_grid(self,
- step=1.,
- size=10.+1e-6,
- color1=(.7,.7,.7),
- color2=(0,0,0)):
-
+ def show_grid(
+ self, step=1.0, size=10.0 + 1e-6, color1=(0.7, 0.7, 0.7), color2=(0, 0, 0)
+ ):
viewer = self._get_viewer()
- viewer.ActivateGrid(Aspect_GT_Rectangular,
- Aspect_GDM_Lines)
+ viewer.ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines)
viewer.SetRectangularGridGraphicValues(size, size, 0)
viewer.SetRectangularGridValues(0, 0, step, step, 0)
grid = viewer.Grid()
- grid.SetColors(Quantity_Color(*color1,TOC_RGB),
- Quantity_Color(*color2,TOC_RGB))
+ grid.SetColors(
+ Quantity_Color(*color1, TOC_RGB), Quantity_Color(*color2, TOC_RGB)
+ )
def hide_grid(self):
-
viewer = self._get_viewer()
viewer.DeactivateGrid()
- @pyqtSlot(bool,float)
+ @pyqtSlot(bool, float)
@pyqtSlot(bool)
- def toggle_grid(self,
- value : bool,
- dim : float = 10.):
-
+ def toggle_grid(self, value: bool, dim: float = 10.0):
if value:
- self.show_grid(step=dim/20,size=dim+1e-9)
+ self.show_grid(step=dim / 20, size=dim + 1e-9)
else:
self.hide_grid()
@pyqtSlot(gp_Ax3)
- def set_grid_orientation(self,orientation : gp_Ax3):
-
+ def set_grid_orientation(self, orientation: gp_Ax3):
viewer = self._get_viewer()
viewer.SetPrivilegedPlane(orientation)
- def show_axis(self,origin = (0,0,0), direction=(0,0,1)):
-
- ax_placement = Geom_Axis1Placement(gp_Ax1(gp_Pnt(*origin),
- gp_Dir(*direction)))
+ def show_axis(self, origin=(0, 0, 0), direction=(0, 0, 1)):
+ ax_placement = Geom_Axis1Placement(gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction)))
ax = AIS_Axis(ax_placement)
self._display_ais(ax)
def save_screenshot(self):
-
fname = get_save_filename(self.IMAGE_EXTENSIONS)
- if fname != '':
- self._get_view().Dump(fname)
-
- def _display_ais(self,ais):
+ if fname != "":
+ self._get_view().Dump(fname)
+ def _display_ais(self, ais):
self._get_context().Display(ais)
-
def _get_view(self):
-
return self.canvas.view
def _get_viewer(self):
-
return self.canvas.viewer
def _get_context(self):
-
return self.canvas.context
@pyqtSlot(list)
- def handle_selection(self,obj):
-
+ def handle_selection(self, obj):
self.sigObjectSelected.emit(obj)
@pyqtSlot(list)
- def set_selected(self,ais):
-
+ def set_selected(self, ais):
ctx = self._get_context()
ctx.ClearSelected(False)
for obj in ais:
- ctx.AddOrRemoveSelected(obj,False)
+ ctx.AddOrRemoveSelected(obj, False)
self.redraw()
if __name__ == "__main__":
-
+ # pass
import sys
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
@@ -379,10 +423,10 @@ if __name__ == "__main__":
dlg.setFixedHeight(400)
dlg.setFixedWidth(600)
- layout(dlg,(viewer,),dlg)
+ layout(dlg, (viewer,), dlg)
dlg.show()
- box = BRepPrimAPI_MakeBox(20,20,30)
+ box = BRepPrimAPI_MakeBox(20, 20, 30)
box_ais = AIS_ColoredShape(box.Shape())
viewer.display(box_ais)