- UI refinement, button position ui file as source no dirty drafting anymore
This commit is contained in:
Generated
+10
-3
@@ -6,9 +6,8 @@
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="8f0bafd6-58a0-4b20-aa2b-ddc3ba278873" name="Changes" comment="- sketch enhacements">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/fluency/geometry_occ/sketch.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/geometry_occ/sketch.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/gui.ui" beforeDir="false" afterPath="$PROJECT_DIR$/gui.ui" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/fluency/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/main.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/fluency/models/data_model.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/models/data_model.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/fluency/rendering/occ_renderer.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/fluency/rendering/occ_renderer.py" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
@@ -306,7 +305,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1783108151676</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="25" />
|
||||
<task id="LOCAL-00025" summary="- sketch enhacements">
|
||||
<option name="closed" value="true" />
|
||||
<created>1783159860774</created>
|
||||
<option name="number" value="00025" />
|
||||
<option name="presentableId" value="LOCAL-00025" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1783159860774</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="26" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# Fluency CAD — Agent Guide
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Fluency CAD 2.0** is a parametric CAD application built on **OpenCASCADE Technology (OCCT)** with a modern **pygfx-based 3D renderer**. It provides 2D sketching with SolveSpace constraint solving, boolean operations, STEP/IGES/STL import/export, and exact BRep geometry.
|
||||
|
||||
- Language: **Python 3.10+**
|
||||
- GUI: **PySide6** (Qt6)
|
||||
- Geometry Kernel: **OCP** (cadquery-ocp — OpenCASCADE Python bindings)
|
||||
- Constraint Solver: **python_solvespace**
|
||||
- Renderer: **pygfx** (WebGPU) + **OCCRenderer** (native OCC AIS display)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
src/fluency/
|
||||
├── __init__.py # Package entry (version, imports)
|
||||
├── main.py # Application entry point, MainWindow, Sketch2DWidget (~5000 lines)
|
||||
├── sketch_solver.py # SolveSpace constraint solver wrapper (legacy)
|
||||
├── geometry/
|
||||
│ └── base.py # Abstract interfaces: GeometryKernel, SketchInterface, data classes
|
||||
├── geometry_occ/
|
||||
│ ├── kernel.py # OCGeometryKernel (extrude, boolean, fillet, import/export, mesh)
|
||||
│ └── sketch.py # OCCSketch with SolveSpace integration (face detection, constraints)
|
||||
├── models/
|
||||
│ └── data_model.py # Project, Component, Sketch, Body dataclasses
|
||||
├── rendering/
|
||||
│ ├── base.py # Abstract Renderer interface
|
||||
│ ├── occ_renderer.py # OCC AIS renderer (preferred — smooth BRep display)
|
||||
│ └── pygfx_renderer.py # Legacy pygfx renderer
|
||||
├── utils/ # Utility modules
|
||||
└── widgets/ # Custom widgets
|
||||
tests/
|
||||
└── test_geometry.py # Comprehensive test suite (52+ tests)
|
||||
```
|
||||
|
||||
### Key Classes & Responsibilities
|
||||
|
||||
| Class | File | Purpose |
|
||||
|-------|------|---------|
|
||||
| `OCGeometryKernel` | `kernel.py` | OCC shape ops: extrude, boolean, fillet, mesh, import/export |
|
||||
| `OCCSketch` | `sketch.py` | 2D sketch with SolveSpace solver, face detection, workplane |
|
||||
| `OCCSketchEntity` | `sketch.py` | Entity (point/line/circle/arc) with solver handle, is_construction, is_external |
|
||||
| `Sketch2DWidget` | `main.py` | Qt widget for interactive 2D sketching (draw, snap, constrain) |
|
||||
| `MainWindow` | `main.py` | Main application window, toolbars, 3D viewer, operations |
|
||||
| `OCCRenderer` | `occ_renderer.py` | Native OCC AIS display (shaded + edges, face pick) |
|
||||
| `Sketch` | `data_model.py` | Data model: workplane, occ_sketch ref, source_body_id |
|
||||
| `Body` | `data_model.py` | 3D solid with geometry, visibility, render object |
|
||||
| `Component` | `data_model.py` | Container for sketches and bodies |
|
||||
| `Project` | `data_model.py` | Top-level container with kernel |
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
User draws in Sketch2DWidget
|
||||
→ OCCSketch entities created in solver
|
||||
→ Constraint solving (python_solvespace)
|
||||
→ OCCSketch.get_geometry() → detect_faces() → build_face_geometry()
|
||||
→ OCGeometryKernel.extrude() → BRepPrimAPI_MakePrism
|
||||
→ Boolean operations → Body added to Component
|
||||
→ OCCRenderer.add_shape() → AIS display
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Install editable
|
||||
pip install -e ".[dev]"
|
||||
|
||||
# Run app
|
||||
python -m fluency.main
|
||||
|
||||
# Run tests (52 tests)
|
||||
python -m pytest tests/test_geometry.py -v
|
||||
|
||||
# Run single test
|
||||
python -m pytest tests/test_geometry.py::TestOCCSketch::test_workplane_extrude_with_hole -xvs
|
||||
|
||||
# Quck geometry test (raw OCC, no Qt)
|
||||
python -c "from fluency.geometry_occ.sketch import OCCSketch; ..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Conventions
|
||||
|
||||
### General
|
||||
- Line length: **100 chars** (black/ruff config)
|
||||
- Target Python: **3.10+** (uses `from __future__ import annotations`, walrus, pattern matching)
|
||||
- Docstrings: Google/NumPy style preferred
|
||||
- Logging: `logger = logging.getLogger(__name__)` with `logging.DEBUG` level
|
||||
|
||||
### OCC / OCP
|
||||
- Always use `is not None` for OCP objects — `TopoDS_Shape.__bool__` can be falsy even for valid shapes
|
||||
- `BRepBuilderAPI_MakeFace.Add(wire)` expects a `TopoDS_Wire`. `wire.Reversed()` returns `TopoDS_Shape` → cast via `_TopoDS.Wire_s(wire.Reversed())`
|
||||
- Face normal direction: check `face.Orientation()` vs `TopAbs_REVERSED` — a REVERSED face's outward normal is the NEGATION of the surface axis
|
||||
- `TopoDS_Wire_s(shape)`, `TopoDS_Face_s(shape)` — use `_s` suffix from OCP for downcasts
|
||||
- Mesh: `BRepMesh_IncrementalMesh(shape, tol, False, 0.15, True)` — default deflection 0.15 rad for smooth curves
|
||||
|
||||
### Extrude / Cut Workflow
|
||||
- Snapshot `list(self._current_component.bodies.items())` **BEFORE** `add_body()` — the new body must not be in the target set
|
||||
- Cut targets the **source body** (`sketch._source_body_id` from face pick), not `bodies[0]`
|
||||
- The fix: apply boolean to **target** geometry, then remove tool body
|
||||
- Plain extrude with holes: inner wires must have **OPPOSITE** geometric winding to the outer wire (see `build_face_geometry` and `_loop_signed_area`)
|
||||
|
||||
### Sketch / Solvers
|
||||
- `python_solvespace` has NO remove API for entities/constraints. Deleting requires: drop from `_points`/`_lines`, prune `_constraint_log`, `_rebuild_solver()` (recreates entire system), `_rebuild_labels()`, re-solve
|
||||
- `_constraint_log`: each entry is `{"type": str, "ids": tuple[int,...], "params": tuple, "labels": set[str]}`
|
||||
- Constraint labels: stored on **point** entities for paintEvent rendering; rebuilt via `_rebuild_labels()`
|
||||
- Line constraints (`horizontal`/`vertical`/`parallel`/`perpendicular`) need the **line's** solver handle, not a point's. Use `_find_line_sketch_entity()` to get the correct handle
|
||||
- External entities (underlay): `is_external=True`, `is_construction=True`, fixed in solver (always `dragged`). Stored in `_external_entity_ids`, excluded from `_line_segments()`, `get_polygon_points()`, `get_closed_loops()`, `detect_faces()`, `get_geometry()`
|
||||
|
||||
### Face Detection
|
||||
- `get_closed_loops()`: uses snapped-coordinate graph (`_SNAP_TOL = 1e-4`) from line endpoint adjacency. Only accepts simple cycles (all nodes degree 2)
|
||||
- `detect_faces()`: even-odd nesting rule via `_loop_contains`. Even depth = outer boundary, odd = hole
|
||||
- `_loop_rep_point`: midpoint between centroid and first vertex. **Fragile** — can land inside a nested shape for certain geometries (e.g., a small hole near the centroid's direction from the first vertex)
|
||||
- `_loop_signed_area`: shoelace formula for polygons, `πr²` (positive = CCW) for circles
|
||||
|
||||
### Rendering
|
||||
- **OCCRenderer** is the main renderer (not pygfx). Uses `AIS_Shape`, `V3d_Viewer`, `AIS_InteractiveContext`
|
||||
- Face pick: `pick_planar_face(x, y)` → `MoveTo` → `DetectedShape` → `TopoDS_Face_s` → `BRepAdaptor_Surface` plane check
|
||||
- Highlight: `highlight_face(face)` creates a transparent AIS overlay; `clear_face_highlight()` removes it
|
||||
- Preview: `preview_shape(shape)` for live transparent extrude preview
|
||||
- Navigation: Left=orbit, Middle=pan, Wheel=zoom. **Right is RESERVED** — check user before reassigning
|
||||
|
||||
### Paint-Event Safety
|
||||
- Every constraint-tag rendering loop wraps each entry in `try/except` so a bad entry (dangling id, corrupted geometry) doesn't crash the entire paint event
|
||||
- `_point_world()` and `_entity_anchor()` return `None` (not raise) for malformed input
|
||||
|
||||
---
|
||||
|
||||
## Known Bugs & Fix Patterns
|
||||
|
||||
### 1. Hole Orientation in Extrusion (FIXED 2026-07-03)
|
||||
**Symptom**: Inner shapes (circle/triangle/slot) inside a rectangle become solid islands instead of holes when extruding, depending on drag direction.
|
||||
|
||||
**Root Cause**: `wire_loop` in `build_face_geometry` unconditionally reversed hole wires (`w.Reversed()`). When the outer polygon was CW-winding (e.g., dragging from top-left to bottom-right), the reversed inner had the SAME effective direction as the outer, making OCC treat it as solid.
|
||||
|
||||
**Fix**: Added `_loop_signed_area()` to compute geometric winding. Hole wires are only reversed when their natural winding matches the outer's (ensuring opposite winding for holes).
|
||||
|
||||
**Relevant code**: `sketch.py`, `build_face_geometry()` and `_loop_signed_area()`
|
||||
|
||||
### 2. _loop_rep_point Fragility (KNOWN)
|
||||
**Symptom**: Face detection fails when a nested shape contains the outer loop's representative point (midpoint between centroid and first vertex).
|
||||
|
||||
**Would-be fix**: Use a guaranteed-interior point (maximum inscribed circle center or perturbed centroid) instead of the centroid-first-vertex midpoint.
|
||||
|
||||
### 3. Extrude Cut / Target Selection (FIXED 2026-06-29)
|
||||
**Symptom**: Cut created a separate "cavity-shaped" body next to the original instead of modifying the target.
|
||||
|
||||
**Fix**: Boolean result stored on TARGET body geometry; tool body removed from component. Auto-target via `sketch._source_body_id`.
|
||||
|
||||
### 4. Workplane Preservation (FIXED 2026-06-29)
|
||||
**Symptom**: Sketch placed on a face lost its workplane after being added to component.
|
||||
|
||||
**Fix**: Copy `occ_sketch` workplane fields into `Sketch` dataclass BEFORE `apply_workplane()`.
|
||||
|
||||
---
|
||||
|
||||
## API Quirks
|
||||
|
||||
- **`QPoint(0,0)`**: falsy via `isNull()` in PySide6 → always use `is not None` for `Optional[QPoint]`
|
||||
- **`QMouseEvent`/`QPainterPath`**: live in `PySide6.QtGui` (NOT `QtCore`)
|
||||
- **`BRepBuilderAPI_MakeFace.Add()`**: needs `TopoDS_Wire`. `wire.Reversed()` returns `TopoDS_Shape` — cast via `TopoDS_Wire_s()`
|
||||
- **python_solvespace**: NO entity/constraint remove API — workaround via `_rebuild_solver()`. Parameters read via `solver.params(handle.params)` → returns `(x, y)` tuple
|
||||
- **OCGeometryKernel.extrude**: unwraps `OCCGeometryObject`, raw `TopoDS_Shape`, or cadquery `Workplane`. Always use `is not None` for the shape (not truthiness)
|
||||
- **Sketch._source_body_id**: dynamic attribute set on `Sketch` dataclass, set during face-pick flow
|
||||
- **`_get_shape(obj)`**: returns `obj.shape.wrapped` for `OCCGeometryObject`, `obj.shape` for raw shapes, `None` for empty. Use `is not None` guards everywhere
|
||||
|
||||
---
|
||||
|
||||
## Memory / Agent Context
|
||||
|
||||
This project has extensive Pi memory (hermes-memory) for:
|
||||
- `project="fluency"` with `target="failure"`: bugs, fixes, corrections, insights
|
||||
- `project="fluency"` with `target="memory"`: conventions, decisions, workflow patterns
|
||||
- Available skills: `fix-cad-app-pipeline`, `refactor-from-cadquery-to-ocp`
|
||||
|
||||
Key memory queries for debugging:
|
||||
- "hole orientation" → `_loop_signed_area` / `build_face_geometry` fix
|
||||
- "extrude cut auto-target" → cut/target body fix
|
||||
- "workplane preservation" → _add_sketch_to_component fix
|
||||
- "_loop_rep_point" → face detection fragility
|
||||
- "paint-event safety" → try/except per entry pattern
|
||||
- "solver rebuild" → delete workflow via _rebuild_solver
|
||||
- "face pick origin" → pick_planar_face face bbox centre
|
||||
|
||||
---
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
```python
|
||||
# Direct OCC test (no Qt)
|
||||
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakePolygon, BRepBuilderAPI_MakeFace
|
||||
from OCP.gp import gp_Pnt
|
||||
from OCP.BRepPrimAPI import BRepPrimAPI_MakePrism
|
||||
from OCP.GProp import GProp_GProps; from OCP.BRepGProp import BRepGProp
|
||||
|
||||
# Build test shape, extrude, verify volume
|
||||
mp = BRepBuilderAPI_MakePolygon(); ...
|
||||
g = GProp_GProps(); BRepGProp.VolumeProperties_s(shape, g)
|
||||
assert abs(g.Mass() - expected) < 0.1
|
||||
```
|
||||
|
||||
```python
|
||||
# Sketch-based test
|
||||
from fluency.geometry_occ.sketch import OCCSketch
|
||||
from fluency.geometry_occ.kernel import OCGeometryKernel
|
||||
|
||||
sk = OCCSketch()
|
||||
# ... add points, lines, circles ...
|
||||
sk.solve()
|
||||
geom = sk.get_geometry()
|
||||
solid = OCGeometryKernel().extrude(geom, 10.0)
|
||||
```
|
||||
@@ -0,0 +1,802 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'gui.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.10.2
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt)
|
||||
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
|
||||
QCursor, QFont, QFontDatabase, QGradient,
|
||||
QIcon, QImage, QKeySequence, QLinearGradient,
|
||||
QPainter, QPalette, QPixmap, QRadialGradient,
|
||||
QTransform)
|
||||
from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QGroupBox,
|
||||
QHBoxLayout, QLabel, QListWidget, QListWidgetItem,
|
||||
QMainWindow, QMenu, QMenuBar, QPushButton,
|
||||
QSizePolicy, QSpinBox, QStatusBar, QTabWidget,
|
||||
QTextEdit, QVBoxLayout, QWidget)
|
||||
|
||||
class Ui_fluencyCAD(object):
|
||||
def setupUi(self, fluencyCAD):
|
||||
if not fluencyCAD.objectName():
|
||||
fluencyCAD.setObjectName(u"fluencyCAD")
|
||||
fluencyCAD.resize(1941, 1155)
|
||||
self.actionNew_Project = QAction(fluencyCAD)
|
||||
self.actionNew_Project.setObjectName(u"actionNew_Project")
|
||||
self.actionLoad_Project = QAction(fluencyCAD)
|
||||
self.actionLoad_Project.setObjectName(u"actionLoad_Project")
|
||||
self.actionRecent = QAction(fluencyCAD)
|
||||
self.actionRecent.setObjectName(u"actionRecent")
|
||||
self.centralwidget = QWidget(fluencyCAD)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
self.gridLayout = QGridLayout(self.centralwidget)
|
||||
self.gridLayout.setObjectName(u"gridLayout")
|
||||
self.compos_in_assembly_box = QGroupBox(self.centralwidget)
|
||||
self.compos_in_assembly_box.setObjectName(u"compos_in_assembly_box")
|
||||
self.compos_in_assembly_box.setMinimumSize(QSize(0, 50))
|
||||
|
||||
self.gridLayout.addWidget(self.compos_in_assembly_box, 12, 2, 1, 1)
|
||||
|
||||
self.joint_tools = QGroupBox(self.centralwidget)
|
||||
self.joint_tools.setObjectName(u"joint_tools")
|
||||
self.joint_tools.setMinimumSize(QSize(0, 50))
|
||||
self.gridLayout_10 = QGridLayout(self.joint_tools)
|
||||
self.gridLayout_10.setObjectName(u"gridLayout_10")
|
||||
self.pb_add_connector = QPushButton(self.joint_tools)
|
||||
self.pb_add_connector.setObjectName(u"pb_add_connector")
|
||||
self.pb_add_connector.setMinimumSize(QSize(50, 50))
|
||||
self.pb_add_connector.setMaximumSize(QSize(50, 50))
|
||||
|
||||
self.gridLayout_10.addWidget(self.pb_add_connector, 0, 0, 1, 1)
|
||||
|
||||
self.pb_remove_connector = QPushButton(self.joint_tools)
|
||||
self.pb_remove_connector.setObjectName(u"pb_remove_connector")
|
||||
self.pb_remove_connector.setMinimumSize(QSize(50, 50))
|
||||
self.pb_remove_connector.setMaximumSize(QSize(50, 50))
|
||||
|
||||
self.gridLayout_10.addWidget(self.pb_remove_connector, 0, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.joint_tools, 12, 3, 1, 1)
|
||||
|
||||
self.groupBox_5 = QGroupBox(self.centralwidget)
|
||||
self.groupBox_5.setObjectName(u"groupBox_5")
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.groupBox_5.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox_5.setSizePolicy(sizePolicy)
|
||||
self.gridLayout_11 = QGridLayout(self.groupBox_5)
|
||||
self.gridLayout_11.setObjectName(u"gridLayout_11")
|
||||
self.gridLayout_11.setContentsMargins(12, 12, 12, 12)
|
||||
self.label = QLabel(self.groupBox_5)
|
||||
self.label.setObjectName(u"label")
|
||||
|
||||
self.gridLayout_11.addWidget(self.label, 5, 0, 1, 1)
|
||||
|
||||
self.pb_snap_vert = QPushButton(self.groupBox_5)
|
||||
self.pb_snap_vert.setObjectName(u"pb_snap_vert")
|
||||
self.pb_snap_vert.setCheckable(True)
|
||||
self.pb_snap_vert.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_11.addWidget(self.pb_snap_vert, 2, 1, 1, 1)
|
||||
|
||||
self.line_2 = QFrame(self.groupBox_5)
|
||||
self.line_2.setObjectName(u"line_2")
|
||||
self.line_2.setFrameShape(QFrame.Shape.HLine)
|
||||
self.line_2.setFrameShadow(QFrame.Shadow.Sunken)
|
||||
|
||||
self.gridLayout_11.addWidget(self.line_2, 4, 0, 1, 2)
|
||||
|
||||
self.label_2 = QLabel(self.groupBox_5)
|
||||
self.label_2.setObjectName(u"label_2")
|
||||
|
||||
self.gridLayout_11.addWidget(self.label_2, 5, 1, 1, 1)
|
||||
|
||||
self.spinbox_snap_distance = QSpinBox(self.groupBox_5)
|
||||
self.spinbox_snap_distance.setObjectName(u"spinbox_snap_distance")
|
||||
self.spinbox_snap_distance.setMaximum(30)
|
||||
self.spinbox_snap_distance.setValue(10)
|
||||
|
||||
self.gridLayout_11.addWidget(self.spinbox_snap_distance, 6, 0, 1, 1)
|
||||
|
||||
self.pushButton_7 = QPushButton(self.groupBox_5)
|
||||
self.pushButton_7.setObjectName(u"pushButton_7")
|
||||
self.pushButton_7.setCheckable(True)
|
||||
self.pushButton_7.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_11.addWidget(self.pushButton_7, 3, 0, 1, 1)
|
||||
|
||||
self.pb_snap_horiz = QPushButton(self.groupBox_5)
|
||||
self.pb_snap_horiz.setObjectName(u"pb_snap_horiz")
|
||||
self.pb_snap_horiz.setCheckable(True)
|
||||
self.pb_snap_horiz.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_11.addWidget(self.pb_snap_horiz, 2, 0, 1, 1)
|
||||
|
||||
self.spinbox_angle_steps = QSpinBox(self.groupBox_5)
|
||||
self.spinbox_angle_steps.setObjectName(u"spinbox_angle_steps")
|
||||
self.spinbox_angle_steps.setMaximum(180)
|
||||
self.spinbox_angle_steps.setValue(15)
|
||||
|
||||
self.gridLayout_11.addWidget(self.spinbox_angle_steps, 6, 1, 1, 1)
|
||||
|
||||
self.pushButton_8 = QPushButton(self.groupBox_5)
|
||||
self.pushButton_8.setObjectName(u"pushButton_8")
|
||||
self.pushButton_8.setCheckable(True)
|
||||
self.pushButton_8.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_11.addWidget(self.pushButton_8, 0, 0, 1, 1)
|
||||
|
||||
self.pb_snap_midp = QPushButton(self.groupBox_5)
|
||||
self.pb_snap_midp.setObjectName(u"pb_snap_midp")
|
||||
self.pb_snap_midp.setCheckable(True)
|
||||
self.pb_snap_midp.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_11.addWidget(self.pb_snap_midp, 0, 1, 1, 1)
|
||||
|
||||
self.pb_snap_angle = QPushButton(self.groupBox_5)
|
||||
self.pb_snap_angle.setObjectName(u"pb_snap_angle")
|
||||
self.pb_snap_angle.setCheckable(True)
|
||||
self.pb_snap_angle.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_11.addWidget(self.pb_snap_angle, 3, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox_5, 3, 0, 1, 1)
|
||||
|
||||
self.groupBox = QGroupBox(self.centralwidget)
|
||||
self.groupBox.setObjectName(u"groupBox")
|
||||
self.gridLayout_3 = QGridLayout(self.groupBox)
|
||||
self.gridLayout_3.setObjectName(u"gridLayout_3")
|
||||
self.pb_revop = QPushButton(self.groupBox)
|
||||
self.pb_revop.setObjectName(u"pb_revop")
|
||||
|
||||
self.gridLayout_3.addWidget(self.pb_revop, 2, 1, 1, 1)
|
||||
|
||||
self.pb_extrdop = QPushButton(self.groupBox)
|
||||
self.pb_extrdop.setObjectName(u"pb_extrdop")
|
||||
|
||||
self.gridLayout_3.addWidget(self.pb_extrdop, 0, 0, 1, 1)
|
||||
|
||||
self.pb_arrayop = QPushButton(self.groupBox)
|
||||
self.pb_arrayop.setObjectName(u"pb_arrayop")
|
||||
|
||||
self.gridLayout_3.addWidget(self.pb_arrayop, 2, 0, 1, 1)
|
||||
|
||||
self.pb_cutop = QPushButton(self.groupBox)
|
||||
self.pb_cutop.setObjectName(u"pb_cutop")
|
||||
|
||||
self.gridLayout_3.addWidget(self.pb_cutop, 0, 1, 1, 1)
|
||||
|
||||
self.pb_combop = QPushButton(self.groupBox)
|
||||
self.pb_combop.setObjectName(u"pb_combop")
|
||||
|
||||
self.gridLayout_3.addWidget(self.pb_combop, 1, 0, 1, 1)
|
||||
|
||||
self.pb_moveop = QPushButton(self.groupBox)
|
||||
self.pb_moveop.setObjectName(u"pb_moveop")
|
||||
|
||||
self.gridLayout_3.addWidget(self.pb_moveop, 1, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox, 0, 3, 1, 1, Qt.AlignTop)
|
||||
|
||||
self.compo_tool_box = QGroupBox(self.centralwidget)
|
||||
self.compo_tool_box.setObjectName(u"compo_tool_box")
|
||||
self.compo_tool_box.setMinimumSize(QSize(0, 50))
|
||||
self.gridLayout_9 = QGridLayout(self.compo_tool_box)
|
||||
self.gridLayout_9.setObjectName(u"gridLayout_9")
|
||||
self.pb_new_compo = QPushButton(self.compo_tool_box)
|
||||
self.pb_new_compo.setObjectName(u"pb_new_compo")
|
||||
self.pb_new_compo.setMinimumSize(QSize(50, 50))
|
||||
self.pb_new_compo.setMaximumSize(QSize(50, 50))
|
||||
|
||||
self.gridLayout_9.addWidget(self.pb_new_compo, 0, 0, 1, 1)
|
||||
|
||||
self.pb_del_compo = QPushButton(self.compo_tool_box)
|
||||
self.pb_del_compo.setObjectName(u"pb_del_compo")
|
||||
self.pb_del_compo.setEnabled(True)
|
||||
sizePolicy.setHeightForWidth(self.pb_del_compo.sizePolicy().hasHeightForWidth())
|
||||
self.pb_del_compo.setSizePolicy(sizePolicy)
|
||||
self.pb_del_compo.setMinimumSize(QSize(50, 50))
|
||||
self.pb_del_compo.setMaximumSize(QSize(50, 50))
|
||||
self.pb_del_compo.setLayoutDirection(Qt.LeftToRight)
|
||||
|
||||
self.gridLayout_9.addWidget(self.pb_del_compo, 0, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.compo_tool_box, 11, 0, 1, 1)
|
||||
|
||||
self.compo_box = QGroupBox(self.centralwidget)
|
||||
self.compo_box.setObjectName(u"compo_box")
|
||||
self.compo_box.setMinimumSize(QSize(0, 50))
|
||||
|
||||
self.gridLayout.addWidget(self.compo_box, 11, 1, 1, 2)
|
||||
|
||||
self.groupBox_9 = QGroupBox(self.centralwidget)
|
||||
self.groupBox_9.setObjectName(u"groupBox_9")
|
||||
self.groupBox_9.setMaximumSize(QSize(200, 16777215))
|
||||
self.gridLayout_7 = QGridLayout(self.groupBox_9)
|
||||
self.gridLayout_7.setObjectName(u"gridLayout_7")
|
||||
self.pb_origin_wp = QPushButton(self.groupBox_9)
|
||||
self.pb_origin_wp.setObjectName(u"pb_origin_wp")
|
||||
|
||||
self.gridLayout_7.addWidget(self.pb_origin_wp, 0, 0, 1, 1)
|
||||
|
||||
self.pb_origin_face = QPushButton(self.groupBox_9)
|
||||
self.pb_origin_face.setObjectName(u"pb_origin_face")
|
||||
self.pb_origin_face.setCheckable(True)
|
||||
|
||||
self.gridLayout_7.addWidget(self.pb_origin_face, 0, 1, 1, 1)
|
||||
|
||||
self.pb_flip_face = QPushButton(self.groupBox_9)
|
||||
self.pb_flip_face.setObjectName(u"pb_flip_face")
|
||||
|
||||
self.gridLayout_7.addWidget(self.pb_flip_face, 1, 0, 1, 1)
|
||||
|
||||
self.pb_underlay = QPushButton(self.groupBox_9)
|
||||
self.pb_underlay.setObjectName(u"pb_underlay")
|
||||
self.pb_underlay.setEnabled(False)
|
||||
self.pb_underlay.setCheckable(True)
|
||||
self.pb_underlay.setChecked(True)
|
||||
|
||||
self.gridLayout_7.addWidget(self.pb_underlay, 3, 0, 1, 1)
|
||||
|
||||
self.pb_clr_face = QPushButton(self.groupBox_9)
|
||||
self.pb_clr_face.setObjectName(u"pb_clr_face")
|
||||
self.pb_clr_face.setEnabled(False)
|
||||
|
||||
self.gridLayout_7.addWidget(self.pb_clr_face, 3, 1, 1, 1)
|
||||
|
||||
self.pb_to_sketch = QPushButton(self.groupBox_9)
|
||||
self.pb_to_sketch.setObjectName(u"pb_to_sketch")
|
||||
self.pb_to_sketch.setEnabled(False)
|
||||
|
||||
self.gridLayout_7.addWidget(self.pb_to_sketch, 4, 0, 1, 2)
|
||||
|
||||
self.pb_wp_new = QPushButton(self.groupBox_9)
|
||||
self.pb_wp_new.setObjectName(u"pb_wp_new")
|
||||
|
||||
self.gridLayout_7.addWidget(self.pb_wp_new, 1, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox_9, 0, 0, 1, 1)
|
||||
|
||||
self.groupBox_10 = QGroupBox(self.centralwidget)
|
||||
self.groupBox_10.setObjectName(u"groupBox_10")
|
||||
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
|
||||
sizePolicy1.setHorizontalStretch(0)
|
||||
sizePolicy1.setVerticalStretch(0)
|
||||
sizePolicy1.setHeightForWidth(self.groupBox_10.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox_10.setSizePolicy(sizePolicy1)
|
||||
self.groupBox_10.setMaximumSize(QSize(200, 16777215))
|
||||
self.verticalLayout_6 = QVBoxLayout(self.groupBox_10)
|
||||
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
|
||||
self.verticalLayout_6.setContentsMargins(5, 5, 5, 5)
|
||||
self.body_list = QListWidget(self.groupBox_10)
|
||||
self.body_list.setObjectName(u"body_list")
|
||||
self.body_list.setSelectionRectVisible(True)
|
||||
|
||||
self.verticalLayout_6.addWidget(self.body_list)
|
||||
|
||||
self.groupBox_8 = QGroupBox(self.groupBox_10)
|
||||
self.groupBox_8.setObjectName(u"groupBox_8")
|
||||
sizePolicy.setHeightForWidth(self.groupBox_8.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox_8.setSizePolicy(sizePolicy)
|
||||
self.groupBox_8.setMaximumSize(QSize(200, 16777215))
|
||||
self.gridLayout_8 = QGridLayout(self.groupBox_8)
|
||||
self.gridLayout_8.setObjectName(u"gridLayout_8")
|
||||
self.gridLayout_8.setContentsMargins(2, 2, 2, 2)
|
||||
self.pb_del_body = QPushButton(self.groupBox_8)
|
||||
self.pb_del_body.setObjectName(u"pb_del_body")
|
||||
|
||||
self.gridLayout_8.addWidget(self.pb_del_body, 0, 2, 1, 1)
|
||||
|
||||
self.pb_update_body = QPushButton(self.groupBox_8)
|
||||
self.pb_update_body.setObjectName(u"pb_update_body")
|
||||
|
||||
self.gridLayout_8.addWidget(self.pb_update_body, 0, 0, 1, 1)
|
||||
|
||||
self.pb_edt_sktch_3 = QPushButton(self.groupBox_8)
|
||||
self.pb_edt_sktch_3.setObjectName(u"pb_edt_sktch_3")
|
||||
|
||||
self.gridLayout_8.addWidget(self.pb_edt_sktch_3, 0, 1, 1, 1)
|
||||
|
||||
|
||||
self.verticalLayout_6.addWidget(self.groupBox_8)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox_10, 8, 3, 3, 1)
|
||||
|
||||
self.groupBox_11 = QGroupBox(self.centralwidget)
|
||||
self.groupBox_11.setObjectName(u"groupBox_11")
|
||||
sizePolicy1.setHeightForWidth(self.groupBox_11.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox_11.setSizePolicy(sizePolicy1)
|
||||
self.groupBox_11.setMaximumSize(QSize(200, 16777215))
|
||||
self.verticalLayout_7 = QVBoxLayout(self.groupBox_11)
|
||||
self.verticalLayout_7.setObjectName(u"verticalLayout_7")
|
||||
self.verticalLayout_7.setContentsMargins(5, 5, 5, 5)
|
||||
self.sketch_list = QListWidget(self.groupBox_11)
|
||||
self.sketch_list.setObjectName(u"sketch_list")
|
||||
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
sizePolicy2.setHorizontalStretch(0)
|
||||
sizePolicy2.setVerticalStretch(0)
|
||||
sizePolicy2.setHeightForWidth(self.sketch_list.sizePolicy().hasHeightForWidth())
|
||||
self.sketch_list.setSizePolicy(sizePolicy2)
|
||||
self.sketch_list.setSelectionRectVisible(True)
|
||||
|
||||
self.verticalLayout_7.addWidget(self.sketch_list)
|
||||
|
||||
self.groupBox_6 = QGroupBox(self.groupBox_11)
|
||||
self.groupBox_6.setObjectName(u"groupBox_6")
|
||||
sizePolicy.setHeightForWidth(self.groupBox_6.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox_6.setSizePolicy(sizePolicy)
|
||||
self.gridLayout_6 = QGridLayout(self.groupBox_6)
|
||||
self.gridLayout_6.setObjectName(u"gridLayout_6")
|
||||
self.gridLayout_6.setContentsMargins(2, 2, 2, 2)
|
||||
self.pb_edt_sktch = QPushButton(self.groupBox_6)
|
||||
self.pb_edt_sktch.setObjectName(u"pb_edt_sktch")
|
||||
|
||||
self.gridLayout_6.addWidget(self.pb_edt_sktch, 1, 1, 1, 1)
|
||||
|
||||
self.pb_nw_sktch = QPushButton(self.groupBox_6)
|
||||
self.pb_nw_sktch.setObjectName(u"pb_nw_sktch")
|
||||
|
||||
self.gridLayout_6.addWidget(self.pb_nw_sktch, 1, 0, 1, 1)
|
||||
|
||||
self.pb_del_sketch = QPushButton(self.groupBox_6)
|
||||
self.pb_del_sketch.setObjectName(u"pb_del_sketch")
|
||||
|
||||
self.gridLayout_6.addWidget(self.pb_del_sketch, 1, 2, 1, 1)
|
||||
|
||||
|
||||
self.verticalLayout_7.addWidget(self.groupBox_6)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox_11, 8, 0, 3, 1)
|
||||
|
||||
self.groupBox_4 = QGroupBox(self.centralwidget)
|
||||
self.groupBox_4.setObjectName(u"groupBox_4")
|
||||
self.verticalLayout_2 = QVBoxLayout(self.groupBox_4)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.pushButton_2 = QPushButton(self.groupBox_4)
|
||||
self.pushButton_2.setObjectName(u"pushButton_2")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.pushButton_2)
|
||||
|
||||
self.pb_export_step = QPushButton(self.groupBox_4)
|
||||
self.pb_export_step.setObjectName(u"pb_export_step")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.pb_export_step)
|
||||
|
||||
self.pb_export_iges = QPushButton(self.groupBox_4)
|
||||
self.pb_export_iges.setObjectName(u"pb_export_iges")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.pb_export_iges)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox_4, 2, 3, 1, 1)
|
||||
|
||||
self.assembly_box = QGroupBox(self.centralwidget)
|
||||
self.assembly_box.setObjectName(u"assembly_box")
|
||||
self.assembly_box.setMinimumSize(QSize(0, 50))
|
||||
|
||||
self.gridLayout.addWidget(self.assembly_box, 12, 1, 1, 1)
|
||||
|
||||
self.InputTab = QTabWidget(self.centralwidget)
|
||||
self.InputTab.setObjectName(u"InputTab")
|
||||
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||
sizePolicy3.setHorizontalStretch(0)
|
||||
sizePolicy3.setVerticalStretch(0)
|
||||
sizePolicy3.setHeightForWidth(self.InputTab.sizePolicy().hasHeightForWidth())
|
||||
self.InputTab.setSizePolicy(sizePolicy3)
|
||||
self.sketch_tab = QWidget()
|
||||
self.sketch_tab.setObjectName(u"sketch_tab")
|
||||
self.verticalLayout_4 = QVBoxLayout(self.sketch_tab)
|
||||
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
|
||||
self.InputTab.addTab(self.sketch_tab, "")
|
||||
self.code_tab = QWidget()
|
||||
self.code_tab.setObjectName(u"code_tab")
|
||||
self.verticalLayout = QVBoxLayout(self.code_tab)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.textEdit = QTextEdit(self.code_tab)
|
||||
self.textEdit.setObjectName(u"textEdit")
|
||||
|
||||
self.verticalLayout.addWidget(self.textEdit)
|
||||
|
||||
self.groupBox_7 = QGroupBox(self.code_tab)
|
||||
self.groupBox_7.setObjectName(u"groupBox_7")
|
||||
self.gridLayout_5 = QGridLayout(self.groupBox_7)
|
||||
self.gridLayout_5.setObjectName(u"gridLayout_5")
|
||||
self.pushButton_5 = QPushButton(self.groupBox_7)
|
||||
self.pushButton_5.setObjectName(u"pushButton_5")
|
||||
|
||||
self.gridLayout_5.addWidget(self.pushButton_5, 2, 0, 1, 1)
|
||||
|
||||
self.pushButton_4 = QPushButton(self.groupBox_7)
|
||||
self.pushButton_4.setObjectName(u"pushButton_4")
|
||||
|
||||
self.gridLayout_5.addWidget(self.pushButton_4, 2, 1, 1, 1)
|
||||
|
||||
self.pb_apply_code = QPushButton(self.groupBox_7)
|
||||
self.pb_apply_code.setObjectName(u"pb_apply_code")
|
||||
|
||||
self.gridLayout_5.addWidget(self.pb_apply_code, 1, 0, 1, 1)
|
||||
|
||||
self.pushButton = QPushButton(self.groupBox_7)
|
||||
self.pushButton.setObjectName(u"pushButton")
|
||||
|
||||
self.gridLayout_5.addWidget(self.pushButton, 1, 1, 1, 1)
|
||||
|
||||
|
||||
self.verticalLayout.addWidget(self.groupBox_7)
|
||||
|
||||
self.InputTab.addTab(self.code_tab, "")
|
||||
|
||||
self.gridLayout.addWidget(self.InputTab, 0, 1, 11, 1)
|
||||
|
||||
self.assembly_tools = QGroupBox(self.centralwidget)
|
||||
self.assembly_tools.setObjectName(u"assembly_tools")
|
||||
self.assembly_tools.setMinimumSize(QSize(0, 50))
|
||||
self.gridLayout_12 = QGridLayout(self.assembly_tools)
|
||||
self.gridLayout_12.setObjectName(u"gridLayout_12")
|
||||
self.pb_compo_to_assembly = QPushButton(self.assembly_tools)
|
||||
self.pb_compo_to_assembly.setObjectName(u"pb_compo_to_assembly")
|
||||
self.pb_compo_to_assembly.setMinimumSize(QSize(50, 50))
|
||||
self.pb_compo_to_assembly.setMaximumSize(QSize(50, 50))
|
||||
|
||||
self.gridLayout_12.addWidget(self.pb_compo_to_assembly, 0, 0, 1, 1)
|
||||
|
||||
self.pb_remove_compo_from_assembly = QPushButton(self.assembly_tools)
|
||||
self.pb_remove_compo_from_assembly.setObjectName(u"pb_remove_compo_from_assembly")
|
||||
self.pb_remove_compo_from_assembly.setEnabled(True)
|
||||
sizePolicy.setHeightForWidth(self.pb_remove_compo_from_assembly.sizePolicy().hasHeightForWidth())
|
||||
self.pb_remove_compo_from_assembly.setSizePolicy(sizePolicy)
|
||||
self.pb_remove_compo_from_assembly.setMinimumSize(QSize(50, 50))
|
||||
self.pb_remove_compo_from_assembly.setMaximumSize(QSize(50, 50))
|
||||
self.pb_remove_compo_from_assembly.setLayoutDirection(Qt.LeftToRight)
|
||||
|
||||
self.gridLayout_12.addWidget(self.pb_remove_compo_from_assembly, 0, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.assembly_tools, 12, 0, 1, 1)
|
||||
|
||||
self.gl_box = QGroupBox(self.centralwidget)
|
||||
self.gl_box.setObjectName(u"gl_box")
|
||||
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
sizePolicy4.setHorizontalStretch(0)
|
||||
sizePolicy4.setVerticalStretch(4)
|
||||
sizePolicy4.setHeightForWidth(self.gl_box.sizePolicy().hasHeightForWidth())
|
||||
self.gl_box.setSizePolicy(sizePolicy4)
|
||||
font = QFont()
|
||||
font.setPointSize(12)
|
||||
self.gl_box.setFont(font)
|
||||
self.horizontalLayout_4 = QHBoxLayout(self.gl_box)
|
||||
#ifndef Q_OS_MAC
|
||||
self.horizontalLayout_4.setSpacing(-1)
|
||||
#endif
|
||||
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
|
||||
self.horizontalLayout_4.setContentsMargins(12, -1, -1, -1)
|
||||
|
||||
self.gridLayout.addWidget(self.gl_box, 0, 2, 11, 1)
|
||||
|
||||
self.groupBox_3 = QGroupBox(self.centralwidget)
|
||||
self.groupBox_3.setObjectName(u"groupBox_3")
|
||||
sizePolicy.setHeightForWidth(self.groupBox_3.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox_3.setSizePolicy(sizePolicy)
|
||||
self.groupBox_3.setMaximumSize(QSize(200, 16777213))
|
||||
self.gridLayout_4 = QGridLayout(self.groupBox_3)
|
||||
self.gridLayout_4.setObjectName(u"gridLayout_4")
|
||||
self.pb_con_vert = QPushButton(self.groupBox_3)
|
||||
self.pb_con_vert.setObjectName(u"pb_con_vert")
|
||||
self.pb_con_vert.setCheckable(True)
|
||||
self.pb_con_vert.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_vert, 3, 1, 1, 1)
|
||||
|
||||
self.pb_con_mid = QPushButton(self.groupBox_3)
|
||||
self.pb_con_mid.setObjectName(u"pb_con_mid")
|
||||
self.pb_con_mid.setCheckable(True)
|
||||
self.pb_con_mid.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_mid, 2, 0, 1, 1)
|
||||
|
||||
self.pb_con_dist = QPushButton(self.groupBox_3)
|
||||
self.pb_con_dist.setObjectName(u"pb_con_dist")
|
||||
self.pb_con_dist.setCheckable(True)
|
||||
self.pb_con_dist.setAutoExclusive(False)
|
||||
self.pb_con_dist.setAutoRepeatDelay(297)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_dist, 4, 0, 1, 1)
|
||||
|
||||
self.pb_con_line = QPushButton(self.groupBox_3)
|
||||
self.pb_con_line.setObjectName(u"pb_con_line")
|
||||
self.pb_con_line.setCheckable(True)
|
||||
self.pb_con_line.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_line, 1, 1, 1, 1)
|
||||
|
||||
self.pb_con_perp = QPushButton(self.groupBox_3)
|
||||
self.pb_con_perp.setObjectName(u"pb_con_perp")
|
||||
self.pb_con_perp.setCheckable(True)
|
||||
self.pb_con_perp.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_perp, 2, 1, 1, 1)
|
||||
|
||||
self.pb_con_sym = QPushButton(self.groupBox_3)
|
||||
self.pb_con_sym.setObjectName(u"pb_con_sym")
|
||||
self.pb_con_sym.setCheckable(True)
|
||||
self.pb_con_sym.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_sym, 4, 1, 1, 1)
|
||||
|
||||
self.pb_con_horiz = QPushButton(self.groupBox_3)
|
||||
self.pb_con_horiz.setObjectName(u"pb_con_horiz")
|
||||
self.pb_con_horiz.setCheckable(True)
|
||||
self.pb_con_horiz.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_horiz, 3, 0, 1, 1)
|
||||
|
||||
self.pb_con_ptpt = QPushButton(self.groupBox_3)
|
||||
self.pb_con_ptpt.setObjectName(u"pb_con_ptpt")
|
||||
self.pb_con_ptpt.setCheckable(True)
|
||||
self.pb_con_ptpt.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.pb_con_ptpt, 1, 0, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox_3, 4, 0, 1, 1)
|
||||
|
||||
self.groupBox_2 = QGroupBox(self.centralwidget)
|
||||
self.groupBox_2.setObjectName(u"groupBox_2")
|
||||
sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox_2.setSizePolicy(sizePolicy)
|
||||
self.groupBox_2.setMaximumSize(QSize(200, 16777215))
|
||||
self.gridLayout_2 = QGridLayout(self.groupBox_2)
|
||||
self.gridLayout_2.setObjectName(u"gridLayout_2")
|
||||
self.gridLayout_2.setContentsMargins(10, -1, -1, -1)
|
||||
self.pb_arc_tool = QPushButton(self.groupBox_2)
|
||||
self.pb_arc_tool.setObjectName(u"pb_arc_tool")
|
||||
self.pb_arc_tool.setCheckable(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_arc_tool, 2, 0, 1, 1)
|
||||
|
||||
self.pb_rectool = QPushButton(self.groupBox_2)
|
||||
self.pb_rectool.setObjectName(u"pb_rectool")
|
||||
self.pb_rectool.setCheckable(True)
|
||||
self.pb_rectool.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_rectool, 0, 1, 1, 1)
|
||||
|
||||
self.pb_circtool = QPushButton(self.groupBox_2)
|
||||
self.pb_circtool.setObjectName(u"pb_circtool")
|
||||
self.pb_circtool.setCheckable(True)
|
||||
self.pb_circtool.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_circtool, 1, 0, 1, 1, Qt.AlignTop)
|
||||
|
||||
self.pb_enable_construct = QPushButton(self.groupBox_2)
|
||||
self.pb_enable_construct.setObjectName(u"pb_enable_construct")
|
||||
self.pb_enable_construct.setCheckable(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_enable_construct, 4, 0, 1, 1)
|
||||
|
||||
self.pb_enable_snap = QPushButton(self.groupBox_2)
|
||||
self.pb_enable_snap.setObjectName(u"pb_enable_snap")
|
||||
self.pb_enable_snap.setIconSize(QSize(13, 16))
|
||||
self.pb_enable_snap.setCheckable(True)
|
||||
self.pb_enable_snap.setChecked(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_enable_snap, 4, 1, 1, 1)
|
||||
|
||||
self.pb_linetool = QPushButton(self.groupBox_2)
|
||||
self.pb_linetool.setObjectName(u"pb_linetool")
|
||||
self.pb_linetool.setCheckable(True)
|
||||
self.pb_linetool.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_linetool, 0, 0, 1, 1)
|
||||
|
||||
self.pb_slotool = QPushButton(self.groupBox_2)
|
||||
self.pb_slotool.setObjectName(u"pb_slotool")
|
||||
self.pb_slotool.setCheckable(True)
|
||||
self.pb_slotool.setAutoExclusive(False)
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_slotool, 1, 1, 1, 1, Qt.AlignTop)
|
||||
|
||||
self.line = QFrame(self.groupBox_2)
|
||||
self.line.setObjectName(u"line")
|
||||
self.line.setFrameShape(QFrame.Shape.HLine)
|
||||
self.line.setFrameShadow(QFrame.Shadow.Sunken)
|
||||
|
||||
self.gridLayout_2.addWidget(self.line, 3, 0, 1, 2)
|
||||
|
||||
self.pb_offset_tool = QPushButton(self.groupBox_2)
|
||||
self.pb_offset_tool.setObjectName(u"pb_offset_tool")
|
||||
|
||||
self.gridLayout_2.addWidget(self.pb_offset_tool, 2, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.groupBox_2, 2, 0, 1, 1)
|
||||
|
||||
fluencyCAD.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QMenuBar(fluencyCAD)
|
||||
self.menubar.setObjectName(u"menubar")
|
||||
self.menubar.setGeometry(QRect(0, 0, 1941, 24))
|
||||
self.menuFile = QMenu(self.menubar)
|
||||
self.menuFile.setObjectName(u"menuFile")
|
||||
self.menuSettings = QMenu(self.menubar)
|
||||
self.menuSettings.setObjectName(u"menuSettings")
|
||||
fluencyCAD.setMenuBar(self.menubar)
|
||||
self.statusbar = QStatusBar(fluencyCAD)
|
||||
self.statusbar.setObjectName(u"statusbar")
|
||||
fluencyCAD.setStatusBar(self.statusbar)
|
||||
|
||||
self.menubar.addAction(self.menuFile.menuAction())
|
||||
self.menubar.addAction(self.menuSettings.menuAction())
|
||||
self.menuFile.addAction(self.actionNew_Project)
|
||||
self.menuFile.addAction(self.actionLoad_Project)
|
||||
self.menuFile.addAction(self.actionRecent)
|
||||
self.menuFile.addSeparator()
|
||||
|
||||
self.retranslateUi(fluencyCAD)
|
||||
|
||||
self.InputTab.setCurrentIndex(0)
|
||||
|
||||
|
||||
QMetaObject.connectSlotsByName(fluencyCAD)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, fluencyCAD):
|
||||
fluencyCAD.setWindowTitle(QCoreApplication.translate("fluencyCAD", u"fluencyCAD", None))
|
||||
self.actionNew_Project.setText(QCoreApplication.translate("fluencyCAD", u"New", None))
|
||||
self.actionLoad_Project.setText(QCoreApplication.translate("fluencyCAD", u"Load", None))
|
||||
self.actionRecent.setText(QCoreApplication.translate("fluencyCAD", u"Recent", None))
|
||||
self.compos_in_assembly_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Components in Assembly", None))
|
||||
self.joint_tools.setTitle(QCoreApplication.translate("fluencyCAD", u"Joint Tools", None))
|
||||
self.pb_add_connector.setText(QCoreApplication.translate("fluencyCAD", u"+ Cnct", None))
|
||||
self.pb_remove_connector.setText(QCoreApplication.translate("fluencyCAD", u"- Cnct", None))
|
||||
self.groupBox_5.setTitle(QCoreApplication.translate("fluencyCAD", u"Snapping Points", None))
|
||||
self.label.setText(QCoreApplication.translate("fluencyCAD", u"Snp Dst", None))
|
||||
self.pb_snap_vert.setText(QCoreApplication.translate("fluencyCAD", u"Vert", None))
|
||||
self.label_2.setText(QCoreApplication.translate("fluencyCAD", u"Angl Stps", None))
|
||||
self.spinbox_snap_distance.setSuffix(QCoreApplication.translate("fluencyCAD", u"mm", None))
|
||||
self.pushButton_7.setText(QCoreApplication.translate("fluencyCAD", u"Grid", None))
|
||||
self.pb_snap_horiz.setText(QCoreApplication.translate("fluencyCAD", u"Horiz", None))
|
||||
self.spinbox_angle_steps.setSuffix(QCoreApplication.translate("fluencyCAD", u"\u00b0", None))
|
||||
self.pushButton_8.setText(QCoreApplication.translate("fluencyCAD", u"Pnt", None))
|
||||
self.pb_snap_midp.setText(QCoreApplication.translate("fluencyCAD", u"MidP", None))
|
||||
self.pb_snap_angle.setText(QCoreApplication.translate("fluencyCAD", u"Angles", None))
|
||||
self.groupBox.setTitle(QCoreApplication.translate("fluencyCAD", u"Modify", None))
|
||||
self.pb_revop.setText(QCoreApplication.translate("fluencyCAD", u"Rev", None))
|
||||
self.pb_extrdop.setText(QCoreApplication.translate("fluencyCAD", u"Extrd", None))
|
||||
self.pb_arrayop.setText(QCoreApplication.translate("fluencyCAD", u"Arry", None))
|
||||
self.pb_cutop.setText(QCoreApplication.translate("fluencyCAD", u"Cut", None))
|
||||
self.pb_combop.setText(QCoreApplication.translate("fluencyCAD", u"Comb", None))
|
||||
self.pb_moveop.setText(QCoreApplication.translate("fluencyCAD", u"Mve", None))
|
||||
self.compo_tool_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Component Tools", None))
|
||||
self.pb_new_compo.setText(QCoreApplication.translate("fluencyCAD", u"New", None))
|
||||
self.pb_del_compo.setText(QCoreApplication.translate("fluencyCAD", u"Del", None))
|
||||
self.compo_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Components", None))
|
||||
self.groupBox_9.setTitle(QCoreApplication.translate("fluencyCAD", u"Workplanes", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_origin_wp.setToolTip(QCoreApplication.translate("fluencyCAD", u"<W>orking Plane at 0, 0, 0", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_origin_wp.setText(QCoreApplication.translate("fluencyCAD", u"WP Origin", None))
|
||||
#if QT_CONFIG(shortcut)
|
||||
self.pb_origin_wp.setShortcut(QCoreApplication.translate("fluencyCAD", u"W", None))
|
||||
#endif // QT_CONFIG(shortcut)
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_origin_face.setToolTip(QCoreApplication.translate("fluencyCAD", u"Working Plane >P<rojection at selected edges face", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_origin_face.setText(QCoreApplication.translate("fluencyCAD", u" WP Face", None))
|
||||
#if QT_CONFIG(shortcut)
|
||||
self.pb_origin_face.setShortcut(QCoreApplication.translate("fluencyCAD", u"P", None))
|
||||
#endif // QT_CONFIG(shortcut)
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_flip_face.setToolTip(QCoreApplication.translate("fluencyCAD", u"Flip >N<ormal of projected mesh.", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_flip_face.setText(QCoreApplication.translate("fluencyCAD", u"WP Flip", None))
|
||||
#if QT_CONFIG(shortcut)
|
||||
self.pb_flip_face.setShortcut(QCoreApplication.translate("fluencyCAD", u"N", None))
|
||||
#endif // QT_CONFIG(shortcut)
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_underlay.setToolTip(QCoreApplication.translate("fluencyCAD", u"Show / hide the construction lines projected from the source face", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_underlay.setText(QCoreApplication.translate("fluencyCAD", u"Underlay", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_clr_face.setToolTip(QCoreApplication.translate("fluencyCAD", u"Forget the picked source face (keep the workplane)", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_clr_face.setText(QCoreApplication.translate("fluencyCAD", u"ClrFace", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_to_sketch.setToolTip(QCoreApplication.translate("fluencyCAD", u"Convert projected construction lines into real sketch geometry", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_to_sketch.setText(QCoreApplication.translate("fluencyCAD", u"ToSketch", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_wp_new.setToolTip(QCoreApplication.translate("fluencyCAD", u"Create a new independent workplane (datum plane)", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_wp_new.setText(QCoreApplication.translate("fluencyCAD", u"WP New", None))
|
||||
#if QT_CONFIG(shortcut)
|
||||
self.pb_wp_new.setShortcut(QCoreApplication.translate("fluencyCAD", u"Shift+W", None))
|
||||
#endif // QT_CONFIG(shortcut)
|
||||
self.groupBox_10.setTitle(QCoreApplication.translate("fluencyCAD", u"Bodys / Operations", None))
|
||||
self.groupBox_8.setTitle(QCoreApplication.translate("fluencyCAD", u"Tools", None))
|
||||
self.pb_del_body.setText(QCoreApplication.translate("fluencyCAD", u"Del", None))
|
||||
self.pb_update_body.setText(QCoreApplication.translate("fluencyCAD", u"Upd", None))
|
||||
self.pb_edt_sktch_3.setText(QCoreApplication.translate("fluencyCAD", u"Nothing", None))
|
||||
self.groupBox_11.setTitle(QCoreApplication.translate("fluencyCAD", u"Sketch", None))
|
||||
self.groupBox_6.setTitle(QCoreApplication.translate("fluencyCAD", u"Tools", None))
|
||||
self.pb_edt_sktch.setText(QCoreApplication.translate("fluencyCAD", u"Edt", None))
|
||||
self.pb_nw_sktch.setText(QCoreApplication.translate("fluencyCAD", u"Add", None))
|
||||
self.pb_del_sketch.setText(QCoreApplication.translate("fluencyCAD", u"Del", None))
|
||||
self.groupBox_4.setTitle(QCoreApplication.translate("fluencyCAD", u"Export", None))
|
||||
self.pushButton_2.setText(QCoreApplication.translate("fluencyCAD", u"STL", None))
|
||||
self.pb_export_step.setText(QCoreApplication.translate("fluencyCAD", u"STEP", None))
|
||||
self.pb_export_iges.setText(QCoreApplication.translate("fluencyCAD", u"IGES", None))
|
||||
self.assembly_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Assembly", None))
|
||||
self.InputTab.setTabText(self.InputTab.indexOf(self.sketch_tab), QCoreApplication.translate("fluencyCAD", u"Sketch", None))
|
||||
self.groupBox_7.setTitle(QCoreApplication.translate("fluencyCAD", u"Executive", None))
|
||||
self.pushButton_5.setText(QCoreApplication.translate("fluencyCAD", u"Load Code", None))
|
||||
self.pushButton_4.setText(QCoreApplication.translate("fluencyCAD", u"Save code", None))
|
||||
self.pb_apply_code.setText(QCoreApplication.translate("fluencyCAD", u"Apply Code", None))
|
||||
self.pushButton.setText(QCoreApplication.translate("fluencyCAD", u"Delete Code", None))
|
||||
self.InputTab.setTabText(self.InputTab.indexOf(self.code_tab), QCoreApplication.translate("fluencyCAD", u"Code", None))
|
||||
self.assembly_tools.setTitle(QCoreApplication.translate("fluencyCAD", u"Assembly Tools", None))
|
||||
self.pb_compo_to_assembly.setText(QCoreApplication.translate("fluencyCAD", u"Add", None))
|
||||
self.pb_remove_compo_from_assembly.setText(QCoreApplication.translate("fluencyCAD", u"Rem", None))
|
||||
self.gl_box.setTitle(QCoreApplication.translate("fluencyCAD", u"Model Viewer", None))
|
||||
self.groupBox_3.setTitle(QCoreApplication.translate("fluencyCAD", u"Constrain", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_con_vert.setToolTip(QCoreApplication.translate("fluencyCAD", u"Vertical Constrain", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_con_vert.setText(QCoreApplication.translate("fluencyCAD", u"Vert", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_con_mid.setToolTip(QCoreApplication.translate("fluencyCAD", u"Point to Middle Point Constrain", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_con_mid.setText(QCoreApplication.translate("fluencyCAD", u"Pt_Mid_L", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_con_dist.setToolTip(QCoreApplication.translate("fluencyCAD", u"Dimension of Line of Distance from Point to Line", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_con_dist.setText(QCoreApplication.translate("fluencyCAD", u"Distnce", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_con_line.setToolTip(QCoreApplication.translate("fluencyCAD", u"Point to Line Constrain", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_con_line.setText(QCoreApplication.translate("fluencyCAD", u"Pt_Lne", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_con_perp.setToolTip(QCoreApplication.translate("fluencyCAD", u"Constrain Line perpendicular to another line.", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_con_perp.setText(QCoreApplication.translate("fluencyCAD", u"Perp_Lne", None))
|
||||
self.pb_con_sym.setText(QCoreApplication.translate("fluencyCAD", u"Symetrc", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_con_horiz.setToolTip(QCoreApplication.translate("fluencyCAD", u"Horizontal Constrain ", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_con_horiz.setText(QCoreApplication.translate("fluencyCAD", u"Horiz", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_con_ptpt.setToolTip(QCoreApplication.translate("fluencyCAD", u"Poin to Point Constrain", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_con_ptpt.setText(QCoreApplication.translate("fluencyCAD", u"Pt_Pt", None))
|
||||
self.groupBox_2.setTitle(QCoreApplication.translate("fluencyCAD", u"Drawing", None))
|
||||
self.pb_arc_tool.setText(QCoreApplication.translate("fluencyCAD", u"Arc", None))
|
||||
self.pb_rectool.setText(QCoreApplication.translate("fluencyCAD", u"Rctgl", None))
|
||||
self.pb_circtool.setText(QCoreApplication.translate("fluencyCAD", u"Circle", None))
|
||||
self.pb_enable_construct.setText(QCoreApplication.translate("fluencyCAD", u"Cstrct", None))
|
||||
self.pb_enable_snap.setText(QCoreApplication.translate("fluencyCAD", u"Snap", None))
|
||||
self.pb_linetool.setText(QCoreApplication.translate("fluencyCAD", u"Line", None))
|
||||
#if QT_CONFIG(shortcut)
|
||||
self.pb_linetool.setShortcut(QCoreApplication.translate("fluencyCAD", u"S", None))
|
||||
#endif // QT_CONFIG(shortcut)
|
||||
self.pb_slotool.setText(QCoreApplication.translate("fluencyCAD", u"Slot", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.pb_offset_tool.setToolTip(QCoreApplication.translate("fluencyCAD", u"Offset selected sketch face (duplicate + offset boundary)", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.pb_offset_tool.setText(QCoreApplication.translate("fluencyCAD", u"Offst", None))
|
||||
self.menuFile.setTitle(QCoreApplication.translate("fluencyCAD", u"File", None))
|
||||
self.menuSettings.setTitle(QCoreApplication.translate("fluencyCAD", u"Settings", None))
|
||||
# retranslateUi
|
||||
|
||||
+108
-362
@@ -73,6 +73,7 @@ from fluency.geometry_occ.sketch import OCCSketch, OCCSketchEntity
|
||||
from fluency.geometry.base import Point2D, Point3D
|
||||
from fluency.rendering.occ_renderer import OCCRenderer
|
||||
from fluency.models.data_model import Project, Component, Sketch, Body
|
||||
from gui_ui import Ui_fluencyCAD
|
||||
|
||||
|
||||
def _project_face_to_uv(
|
||||
@@ -689,6 +690,7 @@ class WorkplaneOrientationDialog(QDialog):
|
||||
x = np.array([1.0, 0.0, 0.0])
|
||||
self._normal = tuple(float(v) for v in n)
|
||||
self._x_dir = tuple(float(v) for v in x)
|
||||
|
||||
else:
|
||||
btn = self._preset_group.checkedButton()
|
||||
if btn is not None:
|
||||
@@ -3992,11 +3994,15 @@ class MainWindow(QMainWindow):
|
||||
self._create_menus()
|
||||
self._create_central_widget()
|
||||
self._create_dock_widgets()
|
||||
self._setup_ui_aliases()
|
||||
|
||||
logger.info("Ready")
|
||||
|
||||
def _create_menus(self):
|
||||
# Remove UI-built menu actions to avoid duplication
|
||||
menubar = self.menuBar()
|
||||
for action in menubar.actions():
|
||||
menubar.removeAction(action)
|
||||
|
||||
file_menu = menubar.addMenu("&File")
|
||||
|
||||
@@ -4047,377 +4053,123 @@ class MainWindow(QMainWindow):
|
||||
help_menu.addAction("About", self._show_about)
|
||||
|
||||
def _create_central_widget(self):
|
||||
"""
|
||||
Recreates the classic grid-based layout from the original fluencyCAD.
|
||||
"""Load the compiled UI file and add programmatic custom widgets."""
|
||||
self._ui = Ui_fluencyCAD()
|
||||
self._ui.setupUi(self)
|
||||
|
||||
Grid layout:
|
||||
Col 0 (≤200px) Col 1 (expand) Col 2 (expand) Col 3 (≤200px)
|
||||
r0: Workplanes InputTab Model Viewer Modify
|
||||
r1: Drawing
|
||||
r2: Constrain
|
||||
r3: Snaps (tab) r6: Export
|
||||
r4: (Snaps cont) r7-8: Bodies
|
||||
r5: (Snaps cont)
|
||||
r6-8: Sketch List
|
||||
r9: Comp Tools Comp Buttons (span 2) Assembly Tools
|
||||
"""
|
||||
central = QWidget()
|
||||
self.setCentralWidget(central)
|
||||
# Keep a reference to the grid for panel-focus management.
|
||||
self._grid = self._ui.gridLayout
|
||||
|
||||
grid = QGridLayout(central)
|
||||
grid.setContentsMargins(5, 5, 5, 5)
|
||||
grid.setSpacing(4)
|
||||
grid.setColumnStretch(0, 0) # left column fixed
|
||||
# Sketch (col 1) and 3D viewer (col 2) share the remaining width.
|
||||
# The split is hover-driven: entering the sketch enlarges it, entering
|
||||
# the 3D viewer grows it to 2/3 (sketch shrinks to 1/3). Resting = 1:1.
|
||||
grid.setColumnStretch(1, 1)
|
||||
grid.setColumnStretch(2, 1)
|
||||
grid.setColumnStretch(3, 0) # right column fixed
|
||||
self._grid = grid
|
||||
# -- Add programmatic custom widgets to their placeholder locations --
|
||||
|
||||
# ---- Row 0, Col 0: Workplanes ----
|
||||
wp_group = QGroupBox("Workplanes")
|
||||
wp_group.setMaximumWidth(200)
|
||||
wp_layout = QGridLayout(wp_group)
|
||||
self._btn_wp_origin = QPushButton("WP Origin")
|
||||
self._btn_wp_origin.setToolTip("Working Plane at 0, 0, 0")
|
||||
self._btn_wp_origin.setShortcut("W")
|
||||
wp_layout.addWidget(self._btn_wp_origin, 0, 0)
|
||||
self._btn_wp_face = QPushButton("WP Face")
|
||||
self._btn_wp_face.setToolTip("Working Plane from selected face")
|
||||
self._btn_wp_face.setShortcut("P")
|
||||
self._btn_wp_face.setCheckable(True)
|
||||
wp_layout.addWidget(self._btn_wp_face, 0, 1)
|
||||
self._btn_wp_flip = QPushButton("WP Flip")
|
||||
self._btn_wp_flip.setToolTip("Flip normal direction")
|
||||
self._btn_wp_flip.setShortcut("N")
|
||||
wp_layout.addWidget(self._btn_wp_flip, 1, 0)
|
||||
self._btn_wp_move = QPushButton("WP Mve")
|
||||
self._btn_wp_move.setToolTip("Move workplane")
|
||||
self._btn_wp_move.setShortcut("M")
|
||||
wp_layout.addWidget(self._btn_wp_move, 1, 1)
|
||||
# ── New Workplane button ──
|
||||
self._btn_wp_new = QPushButton("WP New")
|
||||
self._btn_wp_new.setToolTip("Create a new independent workplane (datum plane)")
|
||||
self._btn_wp_new.setShortcut("Shift+W")
|
||||
wp_layout.addWidget(self._btn_wp_new, 2, 0, 1, 2)
|
||||
# Underlay: show / hide the construction lines projected from the
|
||||
# source face. Off by default is meaningless (no face picked yet)
|
||||
# so it auto-unchecks when the user clears the source face.
|
||||
self._btn_underlay = QPushButton("Underlay")
|
||||
self._btn_underlay.setToolTip(
|
||||
"Show / hide the construction lines projected from the source face"
|
||||
)
|
||||
self._btn_underlay.setCheckable(True)
|
||||
self._btn_underlay.setChecked(True)
|
||||
self._btn_underlay.setEnabled(False) # becomes enabled after WP Face
|
||||
wp_layout.addWidget(self._btn_underlay, 3, 0)
|
||||
# ClrFace: forget the picked source face. Removes the underlay
|
||||
# construction-line entities and leaves the workplane + sketch
|
||||
# intact, so the user can keep drawing on a free-standing sketch.
|
||||
self._btn_clr_face = QPushButton("ClrFace")
|
||||
self._btn_clr_face.setToolTip("Forget the picked source face (keep the workplane)")
|
||||
self._btn_clr_face.setEnabled(False) # becomes enabled after WP Face
|
||||
wp_layout.addWidget(self._btn_clr_face, 3, 1)
|
||||
# ToSketch: convert the underlay (projected construction lines)
|
||||
# into regular non-construction sketch geometry so the user can
|
||||
# offset, modify, or extrude the projected shape directly without
|
||||
# having to redraw it manually.
|
||||
self._btn_to_sketch = QPushButton("ToSketch")
|
||||
self._btn_to_sketch.setToolTip(
|
||||
"Convert projected construction lines into real sketch geometry"
|
||||
)
|
||||
self._btn_to_sketch.setEnabled(False)
|
||||
wp_layout.addWidget(self._btn_to_sketch, 4, 0, 1, 2)
|
||||
grid.addWidget(wp_group, 0, 0)
|
||||
|
||||
# ---- Row 1, Col 0: Drawing ----
|
||||
draw_group = QGroupBox("Drawing")
|
||||
draw_group.setMaximumWidth(200)
|
||||
draw_layout = QGridLayout(draw_group)
|
||||
self._btn_line = QPushButton("Line")
|
||||
self._btn_line.setCheckable(True)
|
||||
self._btn_line.setShortcut("S")
|
||||
draw_layout.addWidget(self._btn_line, 0, 0)
|
||||
self._btn_rect = QPushButton("Rctgl")
|
||||
self._btn_rect.setCheckable(True)
|
||||
draw_layout.addWidget(self._btn_rect, 0, 1)
|
||||
self._btn_circle = QPushButton("Circle")
|
||||
self._btn_circle.setCheckable(True)
|
||||
draw_layout.addWidget(self._btn_circle, 1, 0)
|
||||
self._btn_slot = QPushButton("Slot")
|
||||
self._btn_slot.setCheckable(True)
|
||||
draw_layout.addWidget(self._btn_slot, 1, 1)
|
||||
self._btn_move_sketch = QPushButton("Move")
|
||||
self._btn_move_sketch.setCheckable(True)
|
||||
self._btn_move_sketch.setToolTip("Move a drawn element (drag a point/line/rectangle/circle).")
|
||||
self._btn_move_sketch.setShortcut("Q")
|
||||
draw_layout.addWidget(self._btn_move_sketch, 1, 2, 1, 1)
|
||||
self._btn_arc = QPushButton("Arc")
|
||||
self._btn_arc.setCheckable(True)
|
||||
draw_layout.addWidget(self._btn_arc, 2, 0)
|
||||
# separator (shifted right to leave column 0 for Arc)
|
||||
sep = QFrame()
|
||||
sep.setFrameShape(QFrame.HLine)
|
||||
sep.setFrameShadow(QFrame.Sunken)
|
||||
draw_layout.addWidget(sep, 2, 1, 1, 2)
|
||||
self._btn_construct = QPushButton("Cstrct")
|
||||
self._btn_construct.setCheckable(True)
|
||||
draw_layout.addWidget(self._btn_construct, 3, 0)
|
||||
self._btn_snap = QPushButton("Snap")
|
||||
self._btn_snap.setCheckable(True)
|
||||
self._btn_snap.setChecked(True)
|
||||
draw_layout.addWidget(self._btn_snap, 3, 1)
|
||||
self._btn_offset = QPushButton("Offst")
|
||||
self._btn_offset.setToolTip("Offset selected sketch face (duplicate + offset boundary)")
|
||||
draw_layout.addWidget(self._btn_offset, 4, 0, 1, 2)
|
||||
grid.addWidget(draw_group, 1, 0)
|
||||
|
||||
# ---- Row 2, Col 0: Constrain ----
|
||||
con_group = QGroupBox("Constrain")
|
||||
con_group.setMaximumWidth(200)
|
||||
con_layout = QGridLayout(con_group)
|
||||
self._btn_con_ptpt = QPushButton("Pt_Pt")
|
||||
self._btn_con_ptpt.setCheckable(True)
|
||||
self._btn_con_ptpt.setToolTip("Point to Point Coincident")
|
||||
con_layout.addWidget(self._btn_con_ptpt, 0, 0)
|
||||
self._btn_con_ptline = QPushButton("Pt_Lne")
|
||||
self._btn_con_ptline.setCheckable(True)
|
||||
self._btn_con_ptline.setToolTip("Point to Line Distance")
|
||||
con_layout.addWidget(self._btn_con_ptline, 0, 1)
|
||||
self._btn_con_mid = QPushButton("Pt_Mid_L")
|
||||
self._btn_con_mid.setCheckable(True)
|
||||
self._btn_con_mid.setToolTip("Point to Midpoint")
|
||||
con_layout.addWidget(self._btn_con_mid, 1, 0)
|
||||
self._btn_con_perp = QPushButton("Perp_Lne")
|
||||
self._btn_con_perp.setCheckable(True)
|
||||
self._btn_con_perp.setToolTip("Perpendicular Constraint")
|
||||
con_layout.addWidget(self._btn_con_perp, 1, 1)
|
||||
self._btn_con_horiz = QPushButton("Horiz")
|
||||
self._btn_con_horiz.setCheckable(True)
|
||||
self._btn_con_horiz.setToolTip("Horizontal Constraint")
|
||||
con_layout.addWidget(self._btn_con_horiz, 2, 0)
|
||||
self._btn_con_vert = QPushButton("Vert")
|
||||
self._btn_con_vert.setCheckable(True)
|
||||
self._btn_con_vert.setToolTip("Vertical Constraint")
|
||||
con_layout.addWidget(self._btn_con_vert, 2, 1)
|
||||
self._btn_con_dist = QPushButton("Distnce")
|
||||
self._btn_con_dist.setCheckable(True)
|
||||
self._btn_con_dist.setToolTip("Distance Constraint")
|
||||
con_layout.addWidget(self._btn_con_dist, 3, 0)
|
||||
self._btn_con_sym = QPushButton("Symetrc")
|
||||
self._btn_con_sym.setCheckable(True)
|
||||
con_layout.addWidget(self._btn_con_sym, 3, 1)
|
||||
grid.addWidget(con_group, 2, 0)
|
||||
|
||||
# ---- Row 3-5, Col 0: Snaps tab ----
|
||||
self._snaps_tab = QTabWidget()
|
||||
self._snaps_tab.setMaximumWidth(200)
|
||||
self._snaps_tab.setTabPosition(QTabWidget.TabPosition.South)
|
||||
snaps_tab1 = QWidget()
|
||||
snaps_layout = QVBoxLayout(snaps_tab1)
|
||||
snap_group = QGroupBox("Snapping Points")
|
||||
snap_grid = QGridLayout(snap_group)
|
||||
snap_grid.setContentsMargins(2, 2, 2, 2)
|
||||
self._btn_snap_point = QPushButton("Pnt")
|
||||
self._btn_snap_point.setCheckable(True)
|
||||
self._btn_snap_point.setChecked(True)
|
||||
snap_grid.addWidget(self._btn_snap_point, 0, 0)
|
||||
self._btn_snap_mid = QPushButton("MidP")
|
||||
self._btn_snap_mid.setCheckable(True)
|
||||
snap_grid.addWidget(self._btn_snap_mid, 0, 1)
|
||||
self._btn_snap_horiz = QPushButton("Horiz")
|
||||
self._btn_snap_horiz.setCheckable(True)
|
||||
snap_grid.addWidget(self._btn_snap_horiz, 1, 0)
|
||||
self._btn_snap_vert = QPushButton("Vert")
|
||||
self._btn_snap_vert.setCheckable(True)
|
||||
snap_grid.addWidget(self._btn_snap_vert, 1, 1)
|
||||
self._btn_snap_angle = QPushButton("Angles")
|
||||
self._btn_snap_angle.setCheckable(True)
|
||||
snap_grid.addWidget(self._btn_snap_angle, 2, 0)
|
||||
self._btn_snap_grid = QPushButton("Grid")
|
||||
self._btn_snap_grid.setCheckable(True)
|
||||
snap_grid.addWidget(self._btn_snap_grid, 2, 1)
|
||||
# separator
|
||||
sep2 = QFrame()
|
||||
sep2.setFrameShape(QFrame.HLine)
|
||||
sep2.setFrameShadow(QFrame.Sunken)
|
||||
snap_grid.addWidget(sep2, 3, 0, 1, 2)
|
||||
snap_grid.addWidget(QLabel("Snp Dst"), 4, 0)
|
||||
snap_grid.addWidget(QLabel("Angl Stps"), 4, 1)
|
||||
self._spin_snap_dist = QSpinBox()
|
||||
self._spin_snap_dist.setRange(1, 30)
|
||||
self._spin_snap_dist.setValue(10)
|
||||
self._spin_snap_dist.setSuffix("px")
|
||||
snap_grid.addWidget(self._spin_snap_dist, 5, 0)
|
||||
self._spin_angle = QSpinBox()
|
||||
self._spin_angle.setRange(1, 180)
|
||||
self._spin_angle.setValue(15)
|
||||
self._spin_angle.setSuffix("°")
|
||||
snap_grid.addWidget(self._spin_angle, 5, 1)
|
||||
snaps_layout.addWidget(snap_group)
|
||||
self._snaps_tab.addTab(snaps_tab1, "Setg 1")
|
||||
self._snaps_tab.addTab(QWidget(), "Setg 2")
|
||||
grid.addWidget(self._snaps_tab, 3, 0)
|
||||
# snaps tab spans rows 3-5
|
||||
grid.setRowStretch(3, 0)
|
||||
grid.setRowStretch(4, 1)
|
||||
grid.setRowStretch(5, 1)
|
||||
# Build row 6-8 spacer for left column
|
||||
# Reserve space so the sketch list reaches the bottom
|
||||
grid.setRowMinimumHeight(6, 0)
|
||||
grid.setRowMinimumHeight(7, 0)
|
||||
grid.setRowMinimumHeight(8, 0)
|
||||
|
||||
# ---- Row 6-8, Col 0: Sketch List ----
|
||||
sk_list_group = QGroupBox("Sketch")
|
||||
sk_list_group.setMaximumWidth(200)
|
||||
sk_list_layout = QVBoxLayout(sk_list_group)
|
||||
self._sketch_list = QListWidget()
|
||||
self._sketch_list.setSelectionRectVisible(True)
|
||||
sk_list_layout.addWidget(self._sketch_list)
|
||||
sk_tools = QGroupBox("Tools")
|
||||
sk_tools_grid = QGridLayout(sk_tools)
|
||||
sk_tools_grid.setContentsMargins(2, 2, 2, 2)
|
||||
self._btn_add_sketch = QPushButton("Add")
|
||||
sk_tools_grid.addWidget(self._btn_add_sketch, 0, 0)
|
||||
self._btn_edit_sketch = QPushButton("Edt")
|
||||
sk_tools_grid.addWidget(self._btn_edit_sketch, 0, 1)
|
||||
self._btn_del_sketch = QPushButton("Del")
|
||||
sk_tools_grid.addWidget(self._btn_del_sketch, 0, 2)
|
||||
sk_list_layout.addWidget(sk_tools)
|
||||
grid.addWidget(sk_list_group, 6, 0, 3, 1)
|
||||
|
||||
# ---- Row 0-8, Col 1: Input Tabs ----
|
||||
self._input_tabs = QTabWidget()
|
||||
sketch_tab = QWidget()
|
||||
sketch_tab_layout = QVBoxLayout(sketch_tab)
|
||||
sketch_tab_layout.setContentsMargins(0, 0, 0, 0)
|
||||
# Sketch2DWidget goes in the sketch tab’s QVBoxLayout.
|
||||
self._sketch_widget = Sketch2DWidget()
|
||||
sketch_tab_layout.addWidget(self._sketch_widget)
|
||||
self._input_tabs.addTab(sketch_tab, "Sketch")
|
||||
code_tab = QWidget()
|
||||
code_tab_layout = QVBoxLayout(code_tab)
|
||||
self._code_edit = QTextEdit()
|
||||
self._ui.sketch_tab.layout().addWidget(self._sketch_widget)
|
||||
|
||||
# Viewer3DWidget goes in the gl_box QHBoxLayout.
|
||||
self._viewer_3d = Viewer3DWidget()
|
||||
self._ui.gl_box.layout().addWidget(self._viewer_3d)
|
||||
|
||||
# Code editor — use the UI’s textEdit with our custom font.
|
||||
self._code_edit = self._ui.textEdit
|
||||
self._code_edit.setFont(QFont("Monaco", 10))
|
||||
self._code_edit.setPlaceholderText("# Enter Python code here...")
|
||||
code_tab_layout.addWidget(self._code_edit)
|
||||
code_tools = QHBoxLayout()
|
||||
self._btn_apply_code = QPushButton("Apply Code")
|
||||
self._btn_load_code = QPushButton("Load Code")
|
||||
self._btn_save_code = QPushButton("Save Code")
|
||||
self._btn_del_code = QPushButton("Delete Code")
|
||||
code_tools.addWidget(self._btn_apply_code)
|
||||
code_tools.addWidget(self._btn_load_code)
|
||||
code_tools.addWidget(self._btn_save_code)
|
||||
code_tools.addWidget(self._btn_del_code)
|
||||
code_tab_layout.addLayout(code_tools)
|
||||
self._input_tabs.addTab(code_tab, "Code")
|
||||
grid.addWidget(self._input_tabs, 0, 1, 9, 1)
|
||||
|
||||
# ---- Row 0-8, Col 2: Model Viewer ----
|
||||
viewer_group = QGroupBox("Model Viewer")
|
||||
viewer_layout = QVBoxLayout(viewer_group)
|
||||
viewer_layout.setContentsMargins(5, 5, 5, 5)
|
||||
self._viewer_3d = Viewer3DWidget()
|
||||
viewer_layout.addWidget(self._viewer_3d)
|
||||
grid.addWidget(viewer_group, 0, 2, 9, 1)
|
||||
|
||||
# Focus split is toggled by Spacebar / the Layout button
|
||||
# (see _toggle_panel_focus).
|
||||
self._panel_focus: str = "equal" # "equal" | "sketch" | "viewer"
|
||||
|
||||
# ---- Row 0, Col 3: Modify ----
|
||||
modify_group = QGroupBox("Modify")
|
||||
modify_group.setMaximumWidth(200)
|
||||
modify_layout = QGridLayout(modify_group)
|
||||
self._btn_extrude = QPushButton("Extrd")
|
||||
self._btn_extrude.setToolTip("Extrude sketch")
|
||||
modify_layout.addWidget(self._btn_extrude, 0, 0)
|
||||
self._btn_cut = QPushButton("Cut")
|
||||
self._btn_cut.setToolTip("Boolean cut")
|
||||
modify_layout.addWidget(self._btn_cut, 0, 1)
|
||||
self._btn_combine = QPushButton("Comb")
|
||||
self._btn_combine.setToolTip("Boolean union")
|
||||
modify_layout.addWidget(self._btn_combine, 1, 0)
|
||||
self._btn_move = QPushButton("Mve")
|
||||
self._btn_move.setToolTip("Move body")
|
||||
modify_layout.addWidget(self._btn_move, 1, 1)
|
||||
self._btn_revolve = QPushButton("Rev")
|
||||
self._btn_revolve.setToolTip("Revolve sketch")
|
||||
modify_layout.addWidget(self._btn_revolve, 2, 0)
|
||||
self._btn_array = QPushButton("Arry")
|
||||
self._btn_array.setToolTip("Pattern array")
|
||||
modify_layout.addWidget(self._btn_array, 2, 1)
|
||||
grid.addWidget(modify_group, 0, 3, 1, 1, Qt.AlignTop)
|
||||
|
||||
# ---- Row 6, Col 3: Export ----
|
||||
export_group = QGroupBox("Export")
|
||||
export_group.setMaximumWidth(200)
|
||||
export_layout = QVBoxLayout(export_group)
|
||||
self._btn_export_stl = QPushButton("STL")
|
||||
export_layout.addWidget(self._btn_export_stl)
|
||||
self._btn_export_step = QPushButton("STEP")
|
||||
export_layout.addWidget(self._btn_export_step)
|
||||
self._btn_export_iges = QPushButton("IGES")
|
||||
export_layout.addWidget(self._btn_export_iges)
|
||||
grid.addWidget(export_group, 6, 3)
|
||||
|
||||
# ---- Row 7-8, Col 3: Bodies / Operations ----
|
||||
body_group = QGroupBox("Bodys / Operations")
|
||||
body_group.setMaximumWidth(200)
|
||||
body_layout = QVBoxLayout(body_group)
|
||||
self._body_list = QListWidget()
|
||||
self._body_list.setSelectionRectVisible(True)
|
||||
body_layout.addWidget(self._body_list)
|
||||
body_tools_grid = QGridLayout()
|
||||
body_tools_grid.setContentsMargins(2, 2, 2, 2)
|
||||
self._btn_update_body = QPushButton("Upd")
|
||||
body_tools_grid.addWidget(self._btn_update_body, 0, 0)
|
||||
self._btn_edit_sketch_3 = QPushButton("Nothing")
|
||||
body_tools_grid.addWidget(self._btn_edit_sketch_3, 0, 1)
|
||||
self._btn_del_body = QPushButton("Del")
|
||||
body_tools_grid.addWidget(self._btn_del_body, 0, 2)
|
||||
body_layout.addLayout(body_tools_grid)
|
||||
grid.addWidget(body_group, 7, 3, 2, 1)
|
||||
|
||||
# ---- Row 9, Col 0: Component Tools ----
|
||||
comp_tool_group = QGroupBox("Component Tools")
|
||||
comp_tool_group.setMinimumHeight(50)
|
||||
comp_tool_layout = QHBoxLayout(comp_tool_group)
|
||||
self._btn_new_compo = QPushButton("New")
|
||||
self._btn_new_compo.setFixedSize(QSize(50, 50))
|
||||
comp_tool_layout.addWidget(self._btn_new_compo)
|
||||
self._btn_del_compo = QPushButton("Del")
|
||||
self._btn_del_compo.setFixedSize(QSize(50, 50))
|
||||
comp_tool_layout.addWidget(self._btn_del_compo)
|
||||
grid.addWidget(comp_tool_group, 9, 0)
|
||||
|
||||
# ---- Row 9, Col 1-2: Components (button box) ----
|
||||
compo_group = QGroupBox("Components")
|
||||
compo_group.setMinimumHeight(50)
|
||||
compo_layout = QHBoxLayout(compo_group)
|
||||
# Component buttons (dynamically generated per component, not in UI).
|
||||
self._component_box = QWidget()
|
||||
self._component_box_layout = QHBoxLayout(self._component_box)
|
||||
self._component_box_layout.setAlignment(Qt.AlignLeft)
|
||||
self._component_group = QButtonGroup(self)
|
||||
self._component_group.setExclusive(True)
|
||||
# Add to the Components group box from the UI.
|
||||
compo_layout = self._ui.compo_box.layout()
|
||||
if compo_layout is None:
|
||||
compo_layout = QHBoxLayout(self._ui.compo_box)
|
||||
compo_layout.setContentsMargins(0, 0, 0, 0)
|
||||
compo_layout.addWidget(self._component_box)
|
||||
compo_layout.addStretch()
|
||||
grid.addWidget(compo_group, 9, 1, 1, 2)
|
||||
|
||||
# ---- Row 9, Col 3: Assembly Tools ----
|
||||
assy_group = QGroupBox("Assembly Tools")
|
||||
assy_group.setMinimumHeight(50)
|
||||
assy_layout = QHBoxLayout(assy_group)
|
||||
self._btn_add_connector = QPushButton("+ Cnct")
|
||||
self._btn_add_connector.setFixedSize(QSize(50, 50))
|
||||
assy_layout.addWidget(self._btn_add_connector)
|
||||
self._btn_del_connector = QPushButton("- Cnct")
|
||||
self._btn_del_connector.setFixedSize(QSize(50, 50))
|
||||
assy_layout.addWidget(self._btn_del_connector)
|
||||
grid.addWidget(assy_group, 9, 3)
|
||||
# Panel-focus mode (equal | sketch | viewer).
|
||||
self._panel_focus: str = "equal"
|
||||
|
||||
def _setup_ui_aliases(self):
|
||||
"""Create _btn_* aliases pointing to the UI-loaded widgets.
|
||||
|
||||
The rest of the application references widgets via ``self._btn_*``
|
||||
names. This method maps those to the ``pb_*`` / ``pushButton_*``
|
||||
names created by the compiled UI file so existing signal connections
|
||||
and mode-switching code continues to work unchanged.
|
||||
"""
|
||||
ui = self._ui
|
||||
# ── Workplanes ──
|
||||
self._btn_wp_origin = ui.pb_origin_wp
|
||||
self._btn_wp_face = ui.pb_origin_face
|
||||
self._btn_wp_flip = ui.pb_flip_face
|
||||
self._btn_wp_new = ui.pb_wp_new
|
||||
self._btn_underlay = ui.pb_underlay
|
||||
self._btn_clr_face = ui.pb_clr_face
|
||||
self._btn_to_sketch = ui.pb_to_sketch
|
||||
# ── Drawing ──
|
||||
self._btn_line = ui.pb_linetool
|
||||
self._btn_rect = ui.pb_rectool
|
||||
self._btn_circle = ui.pb_circtool
|
||||
self._btn_slot = ui.pb_slotool
|
||||
self._btn_arc = ui.pb_arc_tool
|
||||
self._btn_construct = ui.pb_enable_construct
|
||||
self._btn_snap = ui.pb_enable_snap
|
||||
self._btn_offset = ui.pb_offset_tool
|
||||
# ── Constrain ──
|
||||
self._btn_con_ptpt = ui.pb_con_ptpt
|
||||
self._btn_con_ptline = ui.pb_con_line
|
||||
self._btn_con_mid = ui.pb_con_mid
|
||||
self._btn_con_perp = ui.pb_con_perp
|
||||
self._btn_con_horiz = ui.pb_con_horiz
|
||||
self._btn_con_vert = ui.pb_con_vert
|
||||
self._btn_con_dist = ui.pb_con_dist
|
||||
self._btn_con_sym = ui.pb_con_sym
|
||||
# ── Snaps ──
|
||||
self._btn_snap_point = ui.pushButton_8
|
||||
self._btn_snap_mid = ui.pb_snap_midp
|
||||
self._btn_snap_horiz = ui.pb_snap_horiz
|
||||
self._btn_snap_vert = ui.pb_snap_vert
|
||||
self._btn_snap_angle = ui.pb_snap_angle
|
||||
self._btn_snap_grid = ui.pushButton_7
|
||||
self._spin_snap_dist = ui.spinbox_snap_distance
|
||||
self._spin_angle = ui.spinbox_angle_steps
|
||||
# ── Modify ──
|
||||
self._btn_extrude = ui.pb_extrdop
|
||||
self._btn_cut = ui.pb_cutop
|
||||
self._btn_combine = ui.pb_combop
|
||||
self._btn_move = ui.pb_moveop
|
||||
self._btn_revolve = ui.pb_revop
|
||||
self._btn_array = ui.pb_arrayop
|
||||
# ── Export ──
|
||||
self._btn_export_stl = ui.pushButton_2
|
||||
self._btn_export_step = ui.pb_export_step
|
||||
self._btn_export_iges = ui.pb_export_iges
|
||||
# ── Sketch list tools ──
|
||||
self._btn_add_sketch = ui.pb_nw_sktch
|
||||
self._btn_edit_sketch = ui.pb_edt_sktch
|
||||
self._btn_del_sketch = ui.pb_del_sketch
|
||||
# ── Body tools ──
|
||||
self._btn_update_body = ui.pb_update_body
|
||||
self._btn_edit_sketch_3 = ui.pb_edt_sktch_3
|
||||
self._btn_del_body = ui.pb_del_body
|
||||
# ── Component tools ──
|
||||
self._btn_new_compo = ui.pb_new_compo
|
||||
self._btn_del_compo = ui.pb_del_compo
|
||||
# ── Assembly / Connector ──
|
||||
self._btn_add_connector = ui.pb_add_connector
|
||||
self._btn_del_connector = ui.pb_remove_connector
|
||||
# ── Code tab ──
|
||||
self._btn_apply_code = ui.pb_apply_code
|
||||
self._btn_load_code = ui.pushButton_5
|
||||
self._btn_save_code = ui.pushButton_4
|
||||
self._btn_del_code = ui.pushButton
|
||||
# ── List views & tabs ──
|
||||
self._sketch_list = ui.sketch_list
|
||||
self._body_list = ui.body_list
|
||||
self._input_tabs = ui.InputTab
|
||||
|
||||
def _toggle_panel_focus(self):
|
||||
"""Cycle the sketch/viewer split: equal → sketch → viewer → equal.
|
||||
@@ -4468,7 +4220,6 @@ class MainWindow(QMainWindow):
|
||||
self._btn_circle.clicked.connect(lambda: self._set_sketch_mode("circle"))
|
||||
self._btn_arc.clicked.connect(lambda: self._set_sketch_mode("arc"))
|
||||
self._btn_slot.clicked.connect(lambda: self._set_sketch_mode("slot"))
|
||||
self._btn_move_sketch.clicked.connect(lambda: self._set_sketch_mode("select"))
|
||||
self._btn_construct.clicked.connect(self._on_construct_change)
|
||||
|
||||
self._btn_con_ptpt.clicked.connect(lambda: self._set_sketch_mode("constrain_coincident"))
|
||||
@@ -4535,7 +4286,6 @@ class MainWindow(QMainWindow):
|
||||
self._btn_wp_origin.clicked.connect(self._new_sketch_origin)
|
||||
self._btn_wp_new.clicked.connect(self._new_workplane)
|
||||
self._btn_wp_flip.clicked.connect(self._flip_workplane)
|
||||
self._btn_wp_move.clicked.connect(self._move_workplane)
|
||||
# Underlay show/hide, ClrFace, and ToSketch — all stay in sync
|
||||
# with the source face state managed by set_source_face /
|
||||
# clear_source_face / _project_body_to_active_wp.
|
||||
@@ -4564,7 +4314,6 @@ class MainWindow(QMainWindow):
|
||||
self._btn_circle,
|
||||
self._btn_arc,
|
||||
self._btn_slot,
|
||||
self._btn_move_sketch,
|
||||
self._btn_con_ptpt,
|
||||
self._btn_con_ptline,
|
||||
self._btn_con_horiz,
|
||||
@@ -4587,8 +4336,6 @@ class MainWindow(QMainWindow):
|
||||
self._btn_arc.setChecked(True)
|
||||
elif mode == "slot":
|
||||
self._btn_slot.setChecked(True)
|
||||
elif mode == "select":
|
||||
self._btn_move_sketch.setChecked(True)
|
||||
elif mode.startswith("constrain_"):
|
||||
if mode == "constrain_coincident":
|
||||
self._btn_con_ptpt.setChecked(True)
|
||||
@@ -4607,7 +4354,6 @@ class MainWindow(QMainWindow):
|
||||
self._btn_circle,
|
||||
self._btn_arc,
|
||||
self._btn_slot,
|
||||
self._btn_move_sketch,
|
||||
self._btn_con_ptpt,
|
||||
self._btn_con_ptline,
|
||||
self._btn_con_horiz,
|
||||
|
||||
@@ -1013,7 +1013,7 @@ class OCCRenderer(Renderer):
|
||||
# • Left button drag → orbit (rotate around target)
|
||||
# • Middle button drag → pan
|
||||
# • Right button → (reserved for future use)
|
||||
# • Wheel → zoom toward cursor
|
||||
# • Wheel → zoom centered on viewport
|
||||
# • Double-click left → fit all (handled by the widget)
|
||||
|
||||
def _qt_buttons(self, event) -> Any:
|
||||
@@ -1079,20 +1079,19 @@ class OCCRenderer(Renderer):
|
||||
self._nav_mode = None
|
||||
|
||||
def handle_wheel(self, event) -> None:
|
||||
"""Zoom toward the cursor on scroll."""
|
||||
"""Zoom centered on the viewport (body midpoint) on scroll.
|
||||
|
||||
Uses the view's scale factor so the zoom is always centered
|
||||
on the viewport centre — the body never drifts to an edge.
|
||||
"""
|
||||
if self._view is None:
|
||||
return
|
||||
# Qt6: QWheelEvent has no .x()/.y(); use position().toPoint().
|
||||
pos = event.position().toPoint() if hasattr(event, "position") else event.pos()
|
||||
x, y = pos.x(), pos.y()
|
||||
delta = event.angleDelta().y()
|
||||
if delta == 0:
|
||||
return
|
||||
# ZoomAtPoint(startX, startY, endX, endY): move the cursor anchor
|
||||
# by a few pixels to drive a smooth zoom centered on the pointer.
|
||||
step = 30 if delta > 0 else -30
|
||||
self._view.ZoomAtPoint(x, y, x, y - step)
|
||||
self._view.ZFitAll()
|
||||
|
||||
factor = 1.15 if delta > 0 else 1.0 / 1.15
|
||||
self._view.SetScale(self._view.Scale() * factor)
|
||||
|
||||
def handle_resize(self, w: int, h: int) -> None:
|
||||
"""Resize the OCC view when the widget is resized."""
|
||||
|
||||
Reference in New Issue
Block a user