From 6dfe0b2d2c82006e04bb46bf10ba1e5a0677f5b3 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Wed, 26 Dec 2018 15:17:03 -0800 Subject: [PATCH] Switched extension UI to tree view dependent on the base objects. --- .../Resources/panels/PageOpPocketExtEdit.ui | 43 ++-- src/Mod/Path/PathScripts/PathPocketShape.py | 12 +- .../Path/PathScripts/PathPocketShapeGui.py | 236 +++++++++++------- .../PathScripts/PathSetupSheetOpPrototype.py | 2 + 4 files changed, 171 insertions(+), 122 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui index 5b0ff175d5..828bbb3e91 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui @@ -7,7 +7,7 @@ 0 0 381 - 552 + 617 @@ -50,41 +50,38 @@ - - - false + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::MultiSelection + + + true + + + true - - - Feature - - - - - Length - - - - - Direction - - - + - Add + Enable - + - Remove + Disable diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index 552cf2b047..f1dc0b5ba9 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -41,7 +41,7 @@ __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" __doc__ = "Class and implementation of shape based Pocket operation." -if True: +if False: PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) PathLog.trackModule(PathLog.thisModule()) else: @@ -52,6 +52,7 @@ def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) def endPoints(edgeOrWire): + '''endPoints(edgeOrWire) ... return the first and last point of the wire or the edge, assuming the argument is not a closed wire.''' if Part.Wire == type(edgeOrWire): edges = edgeOrWire.Edges pts = [e.valueAt(e.FirstParameter) for e in edgeOrWire.Edges] @@ -65,12 +66,14 @@ def endPoints(edgeOrWire): return [e.valueAt(edgeOrWire.FirstParameter), e.valueAt(edgeOrWire.LastParameter)] def includesPoint(p, pts): + '''includesPoint(p, pts) ... answer True if the collection of pts includes the point p''' for pt in pts: if PathGeom.pointsCoincide(p, pt): return True return False def selectOffsetWire(wire, wires, offset): + '''selectOffsetWire(wire, wires, offset) ... returns the Wire in wires which most likely is wire offset by offset''' startPoint = endPoints(wire)[0] + offset; closest = None for w in wires: @@ -82,11 +85,9 @@ def selectOffsetWire(wire, wires, offset): return closest[1] return None -d = [] def extendWire(wire, length, direction): - global d - d = [] + '''extendWire(wire, length, direction) ... return a closed Wire which extends wire by length into direction''' off2D = wire.makeOffset2D(length) endPts = endPoints(wire) edges = [e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)] @@ -163,7 +164,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): obj.UseOutline = False if not hasattr(obj, 'ExtensionLengthDefault'): obj.addProperty('App::PropertyDistance', 'ExtensionLengthDefault', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'Default length of extensions.')) - if not hasattr(obj, 'ExtensionFeatures'): + if not hasattr(obj, 'ExtensionFeature'): obj.addProperty('App::PropertyLinkSubListGlobal', 'ExtensionFeature', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of features to extend.')) if not hasattr(obj, 'ExtensionLength'): obj.addProperty('App::PropertyFloatList', 'ExtensionLength', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of extension lenght of corresponding feature.')) @@ -297,6 +298,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return extensions def setExtensions(self, obj, extensions): + PathLog.track(obj.Label, len(extensions)) features = {} for ext in extensions: subs = features.get(ext.obj, []) diff --git a/src/Mod/Path/PathScripts/PathPocketShapeGui.py b/src/Mod/Path/PathScripts/PathPocketShapeGui.py index 776c57d9e4..8a6e7843ed 100644 --- a/src/Mod/Path/PathScripts/PathPocketShapeGui.py +++ b/src/Mod/Path/PathScripts/PathPocketShapeGui.py @@ -43,7 +43,7 @@ __doc__ = "Pocket Shape operation page controller and command implementation." def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) -if True: +if False: PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) PathLog.trackModule(PathLog.thisModule()) else: @@ -56,20 +56,23 @@ def createExtensionSoSwitch(ext): crd = coin.SoCoordinate3() fce = coin.SoFaceSet() - wire = ext.getWire() - if wire: - polygon = [] - for p in wire.discretize(Deflection=0.01): - polygon.append((p.x, p.y, p.z)) - crd.point.setValues(polygon) + if not ext is None: + wire = ext.getWire() + if wire: + polygon = [] + for p in wire.discretize(Deflection=0.01): + polygon.append((p.x, p.y, p.z)) + crd.point.setValues(polygon) + else: + return None - mat.diffuseColor = (1.0, 0.0, 0.0) - mat.transparency = 0.5 + mat.diffuseColor = (1.0, 0.0, 0.0) + mat.transparency = 0.5 - sep.addChild(pos) - sep.addChild(mat) - sep.addChild(crd) - sep.addChild(fce) + sep.addChild(pos) + sep.addChild(mat) + sep.addChild(crd) + sep.addChild(fce) switch = coin.SoSwitch() switch.addChild(sep) @@ -78,11 +81,21 @@ def createExtensionSoSwitch(ext): return switch class _Extension(object): - def __init__(self, ext): - self.ext = ext - self.switch = createExtensionSoSwitch(ext) + def __init__(self, obj, base, face, edge): + self.base = base + self.face = face + self.edge = edge + if edge is None: + self.ext = None + else: + self.ext = obj.Proxy.createExtension(obj, base, edge) + self.switch = createExtensionSoSwitch(self.ext) self.root = self.switch + def isValid(self): + return not self.root is None + +Page = None class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): DataObject = QtCore.Qt.ItemDataRole.UserRole @@ -95,7 +108,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): } def initPage(self, obj): - self.setTitle("Pocket Extensions") + self.setTitle("Extensions") self.extensions = obj.Proxy.getExtensions(obj) self.defaultLength = PathGui.QuantitySpinBox(self.form.defaultLength, obj, 'ExtensionLengthDefault') @@ -107,132 +120,166 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.obj.ViewObject.RootNode.addChild(self.switch) self.switch.whichChild = coin.SO_SWITCH_ALL + self.model = QtGui.QStandardItemModel(self.form.extensions) + self.model.setHorizontalHeaderLabels(['Base', 'Extension']) + + global Page + Page = self + def cleanupPage(self, obj): self.obj.ViewObject.RootNode.removeChild(self.switch) def getForm(self): return FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketExtEdit.ui") + def forAllItemsCall(self, cb): + for modelRow in range(self.model.rowCount()): + model = self.model.item(modelRow, 0) + for featureRow in range(model.rowCount()): + feature = model.child(featureRow, 0); + for edgeRow in range(feature.rowCount()): + cb(feature.child(edgeRow, 0)) + def getFields(self, obj): - PathLog.track(obj.Label) + PathLog.track(obj.Label, self.model.rowCount(), self.model.columnCount()) self.defaultLength.updateProperty() - exts = [] - for row in range(self.form.extensions.rowCount()): - item = self.form.extensions.item(row, 0) - exts.append(item.data(self.DataObject).ext) - obj.Proxy.setExtensions(obj, exts) + extensions = [] + + def extractExtension(item): + ext = item.data(self.DataObject) + if ext and ext.edge and item.checkState() == QtCore.Qt.Checked: + extensions.append(ext.ext) + + self.forAllItemsCall(extractExtension) + + self.extensions = extensions + obj.Proxy.setExtensions(obj, extensions) def setFields(self, obj): PathLog.track(obj.Label) self.defaultLength.updateSpinBox() self.setExtensions(self.extensions) + def createItemForBaseModel(self, base, sub, edges, extensions): + ext = _Extension(self.obj, base, sub, None) + self.switch.addChild(ext.root) + item = QtGui.QStandardItem() + item.setData(sub, QtCore.Qt.EditRole) + item.setData(ext, self.DataObject) + item.setSelectable(False) + + for edge in base.Shape.getElement(sub).Edges: + for (e, label) in edges: + if edge.isSame(e): + ext0 = _Extension(self.obj, base, sub, label) + if ext0.isValid(): + self.switch.addChild(ext0.root) + item0 = QtGui.QStandardItem() + item0.setData(label, QtCore.Qt.EditRole) + item0.setData(ext0, self.DataObject) + item0.setCheckable(True) + for e in extensions: + PathLog.debug("%s.%s vs %s.%s" % (base.Label, label, e.obj.Label, e.sub)) + if e.obj == base and e.sub == label: + item0.setCheckState(QtCore.Qt.Checked) + break + item.appendRow([item0]) + break + + return item + def setExtensions(self, extensions): self.form.extensions.blockSignals(True) - for row in range(self.form.extensions.rowCount()): - self.switch.removeChild(self.form.extensions.item(row, 0).data(self.DataObject).root) - self.form.extensions.clearContents() - self.form.extensions.setRowCount(0) - for row, ext in enumerate(extensions): - PathLog.info("{}.{}".format(ext.obj.Label, ext.sub)) - self.form.extensions.insertRow(row) + self.forAllItemsCall(lambda item: self.switch.removeChild(item.data(self.DataObject).root)) + self.model.clear() - _ext = _Extension(ext) + for base in self.obj.Base: + edges = [(edge, "Edge%d" % (i + 1)) for i, edge in enumerate(base[0].Shape.Edges)] + baseItem = QtGui.QStandardItem() + baseItem.setData(base[0].Label, QtCore.Qt.EditRole) + baseItem.setSelectable(False) + for sub in sorted(base[1]): + baseItem.appendRow(self.createItemForBaseModel(base[0], sub, edges, extensions)) + self.model.appendRow(baseItem) - item0 = QtGui.QTableWidgetItem("{}.{}".format(ext.obj.Label, ext.sub)) - item0.setData(self.DataObject, _ext) - self.form.extensions.setItem(row, 0, item0) - - item1 = QtGui.QTableWidgetItem("{}".format(ext.length)) - item1.setData(self.DataObject, _ext) - item1.setFlags(item1.flags() & ~QtCore.Qt.ItemIsEnabled) - self.form.extensions.setItem(row, 1, item1) - - item2 = QtGui.QTableWidgetItem("{}".format(self.Direction[ext.direction])) - item2.setData(self.DataObject, _ext) - item2.setFlags(item2.flags() & ~QtCore.Qt.ItemIsEnabled) - self.form.extensions.setItem(row, 2, item2) - - self.switch.addChild(_ext.root) - - self.form.extensions.resizeColumnsToContents() - self.form.extensions.blockSignals(False) - self.extensions = extensions + self.form.extensions.setModel(self.model) + self.form.extensions.expandAll() + self.form.extensions.resizeColumnToContents(0) def updateData(self, obj, prop): if prop in ['ExtensionLengthDefault']: pass if prop in ['ExtensionFeature']: + pass + if prop in ['Base']: self.setExtensions(obj.Proxy.getExtensions(obj)) - def updateSelection(self, obj, sel): - if sel and sel[0].SubElementNames: - self.form.buttonAdd.setEnabled(True) - else: - self.form.buttonAdd.setEnabled(False) - - def itemSelectionChanged(self): - if 0 == self.form.extensions.rowCount(): + def selectionChanged(self): + if 0 == self.model.rowCount(): + PathLog.track('-') self.form.buttonClear.setEnabled(False) - self.form.buttonRemove.setEnabled(False) + self.form.buttonDisable.setEnabled(False) + self.form.buttonEnable.setEnabled(False) else: self.form.buttonClear.setEnabled(True) - if self.form.extensions.selectedItems(): - self.form.buttonRemove.setEnabled(True) + + if self.selectionModel.selectedIndexes(): + self.form.buttonDisable.setEnabled(True) + self.form.buttonEnable.setEnabled(True) else: - self.form.buttonRemove.setEnabled(False) + self.form.buttonDisable.setEnabled(False) + self.form.buttonEnable.setEnabled(False) FreeCADGui.Selection.clearSelection() - print("rowCount = %s" % self.form.extensions.rowCount()) - for row in range(self.form.extensions.rowCount()): - item = self.form.extensions.item(row, 0) + for row in range(self.model.rowCount()): + item = self.model.item(row, 0) ext = item.data(self.DataObject) - ext.switch.whichChild = coin.SO_SWITCH_NONE + if not ext is None: + ext.switch.whichChild = coin.SO_SWITCH_NONE - processed = [] - for item in self.form.extensions.selectedItems(): + for index in self.selectionModel.selectedIndexes(): + item = self.model.itemFromIndex(index) ext = item.data(self.DataObject) - if not ext in processed: - FreeCADGui.Selection.addSelection(ext.ext.obj, ext.ext.sub) + if not ext.face is None: + FreeCADGui.Selection.addSelection(ext.base, ext.face) + if not ext.edge is None: + PathLog.track(ext.base.Label, ext.edge) ext.switch.whichChild = coin.SO_SWITCH_ALL - processed.append(ext) - - def extensionsAdd(self): - extensions = self.extensions - for sel in FreeCADGui.Selection.getSelectionEx(): - for subname in sel.SubElementNames: - row = self.form.extensions.rowCount() - extensions.append(self.obj.Proxy.createExtension(self.obj, sel.Object, subname)) - self.obj.Proxy.setExtensions(self.obj, extensions) - self.setDirty() def extensionsClear(self): - self.obj.Proxy.setExtensions(self.obj, []) + self.forAllItemsCall(lambda item: item.setCheckState(QtCore.Qt.Unchecked)) self.setDirty() - def extensionsRemove(self): - extensions = self.extensions - for item in self.form.extensions.selectedItems(): - ext = item.data(self.DataObject).ext - if ext in extensions: - extensions.remove(ext) - self.obj.Proxy.setExtensions(self.obj, extensions) + def _extensionsSetState(self, state): + for index in self.selectionModel.selectedIndexes(): + item = self.model.itemFromIndex(index) + ext = item.data(self.DataObject) + if ext.edge: + item.setCheckState(state) self.setDirty() + def extensionsDisable(self): + self._extensionsSetState(QtCore.Qt.Unchecked) + + def extensionsEnable(self): + self._extensionsSetState(QtCore.Qt.Checked) + + def getSignalsForUpdate(self, obj): PathLog.track(obj.Label) return [self.form.defaultLength.editingFinished] def registerSignalHandlers(self, obj): - self.form.buttonAdd.clicked.connect(self.extensionsAdd) self.form.buttonClear.clicked.connect(self.extensionsClear) - self.form.buttonRemove.clicked.connect(self.extensionsRemove) + self.form.buttonDisable.clicked.connect(self.extensionsDisable) + self.form.buttonEnable.clicked.connect(self.extensionsEnable) - self.updateSelection(self.obj, FreeCADGui.Selection.getSelectionEx()) - self.form.extensions.itemSelectionChanged.connect(self.itemSelectionChanged) - self.itemSelectionChanged() + self.model.itemChanged.connect(lambda x: self.setDirty()) + + self.selectionModel = self.form.extensions.selectionModel() + self.selectionModel.selectionChanged.connect(self.selectionChanged) class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage): '''Page controller class for Pocket operation''' @@ -242,7 +289,8 @@ class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage): return PathPocketBaseGui.FeaturePocket | PathPocketBaseGui.FeatureOutline def taskPanelBaseLocationPage(self, obj, features): - self.extensionsPanel = TaskPanelExtensionPage(obj, features) + if not hasattr(self, 'extensionsPanel'): + self.extensionsPanel = TaskPanelExtensionPage(obj, features) return self.extensionsPanel def pageRegisterSignalHandlers(self): diff --git a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py index 8e2ffb5385..e55916bf36 100644 --- a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py +++ b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py @@ -142,7 +142,9 @@ class OpPrototype(object): 'App::PropertyEnumeration': PropertyEnumeration, 'App::PropertyFloat': PropertyFloat, 'App::PropertyFloatConstraint': Property, + 'App::PropertyFloatList': Property, 'App::PropertyInteger': PropertyInteger, + 'App::PropertyIntegerList': PropertyInteger, 'App::PropertyLength': PropertyLength, 'App::PropertyLink': Property, 'App::PropertyLinkList': Property,