mirror of
https://github.com/jdegenstein/jmwright-CQ-Editor.git
synced 2025-12-19 14:14:13 +01:00
388 lines
12 KiB
Python
388 lines
12 KiB
Python
import sys
|
|
|
|
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
|
|
|
|
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
|
|
|
|
# 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.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:
|
|
palette = QPalette()
|
|
palette.setColor(QPalette.Window, QColor(53, 53, 53))
|
|
palette.setColor(QPalette.WindowText, Qt.white)
|
|
palette.setColor(QPalette.Base, QColor(25, 25, 25))
|
|
palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
|
|
palette.setColor(QPalette.ToolTipBase, Qt.black)
|
|
palette.setColor(QPalette.ToolTipText, Qt.white)
|
|
palette.setColor(QPalette.Text, Qt.white)
|
|
palette.setColor(QPalette.Button, QColor(53, 53, 53))
|
|
palette.setColor(QPalette.ButtonText, Qt.white)
|
|
palette.setColor(QPalette.BrightText, Qt.red)
|
|
palette.setColor(QPalette.Link, QColor(42, 130, 218))
|
|
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"
|
|
|
|
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"].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["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"].sigShowPlane2[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,
|
|
"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)'
|
|
)
|
|
|
|
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
|