Files
jmwright-CQ-Editor/cq_editor/main_window.py
2022-09-16 13:52:44 -05:00

342 lines
12 KiB
Python

import sys
from PyQt5.QtWidgets import (QLabel, QMainWindow, QToolBar, QDockWidget, QAction)
import cadquery as cq
from .widgets.editor import Editor
from .widgets.viewer import OCCViewer
from .widgets.console import ConsoleWidget
from .widgets.object_tree import ObjectTree
from .widgets.traceback_viewer import TracebackPane
from .widgets.debugger import Debugger, LocalsView
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 .mixins import MainMixin
from .icons import icon
from .preferences import PreferencesWidget
class MainWindow(QMainWindow,MainMixin):
name = 'CQ-Editor'
org = 'CadQuery'
def __init__(self,parent=None, filename=None):
super(MainWindow,self).__init__(parent)
MainMixin.__init__(self)
self.setWindowIcon(icon('app'))
self.viewer = OCCViewer(self)
self.setCentralWidget(self.viewer.canvas)
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_console()
self.fill_dummy()
self.setup_logging()
self.restorePreferences()
self.restoreWindow()
if filename:
self.components['editor'].load_from_file(filename)
self.restoreComponentState()
def closeEvent(self,event):
self.saveWindow()
self.savePreferences()
self.saveComponentState()
if self.components['editor'].document().isModified():
rv = confirm(self, 'Confirm close', 'Close without saving?')
if rv:
event.accept()
super(MainWindow,self).closeEvent(event)
else:
event.ignore()
else:
super(MainWindow,self).closeEvent(event)
def prepare_panes(self):
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('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('debugger',Debugger(self))
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'))
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')
#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())
#global menu elements
menu_view.addSeparator()
for d in self.findChildren(QDockWidget):
menu_view.addAction(d.toggleViewAction())
menu_view.addSeparator()
for t in self.findChildren(QToolBar):
menu_view.addAction(t.toggleViewAction())
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('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))
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')
for c in self.components.values():
add_actions(self.toolbar,c.toolbarActions())
self.addToolBar(self.toolbar)
def prepare_statusbar(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['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['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'].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)
# 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)
def prepare_console(self):
console = self.components['console']
obj_tree = self.components['object_tree']
#application related items
console.push_vars({'self' : self})
#CQ related items
console.push_vars({'show' : obj_tree.addObject,
'show_object' : obj_tree.addObject,
'cq' : cq})
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)')
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._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))
sys.excepthook = handle_exception
def edit_preferences(self):
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',
)
def check_for_cq_updates(self):
check_gtihub_for_updates(self,cq)
def documentation(self):
open_url('https://github.com/CadQuery')
def cq_documentation(self):
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__":
pass