Switched extension UI to tree view dependent on the base objects.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>381</width>
|
||||
<height>552</height>
|
||||
<height>617</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -50,41 +50,38 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="extensions">
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
<widget class="QTreeView" name="extensions">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerHighlightSections">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Feature</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Direction</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonAdd">
|
||||
<widget class="QPushButton" name="buttonEnable">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
<string>Enable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRemove">
|
||||
<widget class="QPushButton" name="buttonDisable">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
<string>Disable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -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, [])
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user