Skip to content
4 changes: 4 additions & 0 deletions cq_editor/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import signal
import sys
import argparse

Expand All @@ -18,7 +19,10 @@

args = parser.parse_args(app.arguments()[1:])

signal.signal(signal.SIGINT, signal.SIG_DFL) # handle Ctrl+C

Check warning on line 22 in cq_editor/__main__.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/__main__.py#L22

Added line #L22 was not covered by tests

win = MainWindow(filename=args.filename if args.filename else None)

win.show()
sys.exit(app.exec_())

Expand Down
12 changes: 11 additions & 1 deletion cq_editor/main_window.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys

from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtWidgets import (QLabel, QMainWindow, QToolBar, QDockWidget, QAction)
from logbook import Logger
import cadquery as cq
Expand Down Expand Up @@ -30,6 +31,9 @@
super(MainWindow,self).__init__(parent)
MainMixin.__init__(self)

self.toolbar = None
self.status_label = None

self.setWindowIcon(icon('app'))

# Windows workaround - makes the correct task bar icon show up.
Expand Down Expand Up @@ -199,7 +203,13 @@

def prepare_toolbar(self):

self.toolbar = QToolBar('Main toolbar',self,objectName='Main toolbar')
self.toolbar = QToolBar('Main toolbar', self, objectName='Main toolbar')

p = self.toolbar.palette()
if p.color(QPalette.Background).lightnessF() < 0.5: # dark theme is active
p.setColor(QPalette.Button, QColor(120, 120, 120))
p.setColor(QPalette.Background, QColor(170, 170, 170))
self.toolbar.setPalette(p)

Check warning on line 212 in cq_editor/main_window.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/main_window.py#L210-L212

Added lines #L210 - L212 were not covered by tests

for c in self.components.values():
add_actions(self.toolbar,c.toolbarActions())
Expand Down
97 changes: 57 additions & 40 deletions cq_editor/widgets/occt_widget.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,55 @@
import math
from sys import platform


from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QEvent

import OCP

from OCP.AIS import AIS_InteractiveContext, AIS_DisplayMode
from OCP.Aspect import Aspect_DisplayConnection, Aspect_TypeOfTriedronPosition
from OCP.OpenGl import OpenGl_GraphicDriver
from OCP.V3d import V3d_Viewer
from OCP.AIS import AIS_InteractiveContext, AIS_DisplayMode
from OCP.Quantity import Quantity_Color
from OCP.V3d import V3d_Viewer
from OCP.gp import gp_Trsf, gp_Ax1, gp_Dir
from PyQt5.QtCore import pyqtSignal, Qt, QPoint
from PyQt5.QtWidgets import QWidget


ZOOM_STEP = 0.9


class OCCTWidget(QWidget):

sigObjectSelected = pyqtSignal(list)

def __init__(self,parent=None):
def __init__(self, parent=None, *,
turntable_rotation: bool = True,
rotate_step: float = 0.008,
zoom_step: float = 0.1):

super(OCCTWidget,self).__init__(parent)
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
self._old_pos = QPoint(0, 0)
self._rotate_step = rotate_step
self._zoom_step = zoom_step
self._turntable_rotation = turntable_rotation

#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 set_turntable_rotation(self, new_value: bool):
if self._turntable_rotation != new_value:
self._turntable_rotation = new_value

Check warning on line 49 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L49

Added line #L49 was not covered by tests
if self._turntable_rotation:
self.view.SetUp(0, 0, 1)

Check warning on line 51 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L51

Added line #L51 was not covered by tests

def prepare_display(self):

view = self.view
Expand All @@ -65,49 +73,58 @@
ctx.DefaultDrawer().SetFaceBoundaryDraw(True)

def wheelEvent(self, event):

delta = event.angleDelta().y()
factor = ZOOM_STEP if delta<0 else 1/ZOOM_STEP


# dividing by 120 gets number of notches on a typical scroll wheel.
# See QWheelEvent documentation
delta_notches = event.angleDelta().y() / 120
direction = math.copysign(1, delta_notches)
factor = (1 + self._zoom_step * direction) ** abs(delta_notches)

Check warning on line 81 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L79-L81

Added lines #L79 - L81 were not covered by tests

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())
if not self._turntable_rotation:
self.view.StartRotation(pos.x(), pos.y())

Check warning on line 91 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L91

Added line #L91 was not covered by tests
elif event.button() == Qt.RightButton:
self.view.StartZoomAtPoint(pos.x(), pos.y())

self.old_pos = pos
def mouseMoveEvent(self,event):
self._old_pos = pos

def mouseMoveEvent(self, event):

pos = event.pos()
x,y = pos.x(),pos.y()
x, y = pos.x(), pos.y()

Check warning on line 100 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L100

Added line #L100 was not covered by tests

if event.buttons() == Qt.LeftButton:
self.view.Rotation(x,y)

if self._turntable_rotation:
delta_x, delta_y = x - self._old_pos.x(), y - self._old_pos.y()
cam = self.view.Camera()
z_rotation = gp_Trsf()
z_rotation.SetRotation(gp_Ax1(cam.Center(), gp_Dir(0, 0, 1)), -delta_x * self._rotate_step)
cam.Transform(z_rotation)
self.view.Rotate(0, -delta_y * self._rotate_step, 0)

Check warning on line 109 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L104-L109

Added lines #L104 - L109 were not covered by tests
else:
self.view.Rotation(x, y)

Check warning on line 111 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L111

Added line #L111 was not covered by tests

elif event.buttons() == Qt.MiddleButton:
self.view.Pan(x - self.old_pos.x(),
self.old_pos.y() - y, theToStart=True)
delta_x, delta_y = x - self._old_pos.x(), y - self._old_pos.y()
self.view.Pan(delta_x, -delta_y, theToStart=True)

Check warning on line 115 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L114-L115

Added lines #L114 - L115 were not covered by tests

elif event.buttons() == Qt.RightButton:
self.view.ZoomAtPoint(self.old_pos.x(), y,
x, self.old_pos.y())
self.view.ZoomAtPoint(self._old_pos.x(), y,

Check warning on line 118 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L118

Added line #L118 was not covered by tests
x, self._old_pos.y())

self.old_pos = pos
self._old_pos = pos

Check warning on line 121 in cq_editor/widgets/occt_widget.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/occt_widget.py#L121

Added line #L121 was not covered by tests

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)

self.context.MoveTo(pos.x(), pos.y(), self.view, True)
self._handle_selection()

def _handle_selection(self):
Expand Down
12 changes: 11 additions & 1 deletion cq_editor/widgets/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@
'values': ['Orthographic', 'Perspective', 'Stereo', 'MonoLeftEye', 'MonoRightEye']},
{'name': 'Stereo Mode', 'type': 'list', 'value': 'QuadBuffer',
'values': ['QuadBuffer', 'Anaglyph', 'RowInterlaced', 'ColumnInterlaced',
'ChessBoard', 'SideBySide', 'OverUnder']}])
'ChessBoard', 'SideBySide', 'OverUnder']},
{'name': 'Orbit Method', 'type': 'list', 'value': 'Turntable', 'values': ['Turntable', 'Trackball']},
])
IMAGE_EXTENSIONS = 'png'

sigObjectSelected = pyqtSignal(list)
Expand Down Expand Up @@ -89,6 +91,14 @@
color2 = color1
self.canvas.view.SetBgGradientColors(color1,color2,theToUpdate=True)

orbit_method = self.preferences['Orbit Method']
if orbit_method == 'Turntable':
self.canvas.set_turntable_rotation(True)
elif orbit_method == 'Trackball':
self.canvas.set_turntable_rotation(False)

Check warning on line 98 in cq_editor/widgets/viewer.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/viewer.py#L98

Added line #L98 was not covered by tests
else:
raise ValueError(orbit_method)

Check warning on line 100 in cq_editor/widgets/viewer.py

View check run for this annotation

Codecov / codecov/patch

cq_editor/widgets/viewer.py#L100

Added line #L100 was not covered by tests

self.canvas.update()

ctx = self.canvas.context
Expand Down