Showing preview only (3,805K chars total). Download the full file or copy to clipboard to get everything.
Repository: kbwbe/A2plus
Branch: master
Commit: ae69e4d52fe2
Files: 67
Total size: 3.6 MB
Directory structure:
gitextract_7vrix2pg/
├── .gitignore
├── A2plus.qrc
├── CD_A2plusupdater.py
├── CD_CheckConstraints.py
├── CD_ConstraintViewer.py
├── CD_FeatureLabels.py
├── CD_OneButton.py
├── GuiA2p/
│ └── Resources/
│ ├── compile_resources_pack.py
│ ├── resources-a2p.rcc
│ ├── resources.rcc
│ └── ui/
│ └── a2p_prefs.ui
├── Init.py
├── InitGui.py
├── LICENSE
├── README.md
├── __init__.py
├── a2p_BoM.py
├── a2p_ConstraintCommands.py
├── a2p_ConstraintDialog.py
├── a2p_MuxAssembly.py
├── a2p_Resources3.py
├── a2p_Resources3_Qt6.py
├── a2p_constraintServices.py
├── a2p_constraints.py
├── a2p_convertPart.py
├── a2p_dependencies.py
├── a2p_fcdocumentreader.py
├── a2p_importedPart_class.py
├── a2p_importpart.py
├── a2p_lcs_support.py
├── a2p_libDOF.py
├── a2p_observers.py
├── a2p_partinformation.py
├── a2p_partlistglobals.py
├── a2p_recursiveUpdatePlanner.py
├── a2p_rigid.py
├── a2p_searchConstraintConflicts.py
├── a2p_simpleXMLreader.py
├── a2p_solversystem.py
├── a2p_topomapper.py
├── a2p_translateUtils.py
├── a2p_viewProviderProxies.py
├── a2plib.py
├── compileA2pResources.py
├── crowdin.yml
├── package.xml
└── translations/
├── A2plus.ts
├── A2plus_de.qm
├── A2plus_de.ts
├── A2plus_es-AR.qm
├── A2plus_es-AR.ts
├── A2plus_es-ES.qm
├── A2plus_es-ES.ts
├── A2plus_fr.qm
├── A2plus_fr.ts
├── A2plus_it.qm
├── A2plus_it.ts
├── A2plus_pt-br.qm
├── A2plus_pt-br.ts
├── A2plus_pt-pt.qm
├── A2plus_pt-pt.ts
├── A2plus_ru.qm
├── A2plus_ru.ts
├── A2plus_zh-CN.qm
├── A2plus_zh-CN.ts
├── README.md
└── update_ts.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# file types to ignore
*.pyc
*.obj
*.lib
*.pch
*.vcproj
*.exp
*.dep
*.bak
*.manifest
qrc_*.cpp
BuildLog.htm
cmake_install.cmake
*~
CMakeFiles/
*qrc.depends
ui_*.h
moc_*.cpp
Makefile
CMakeCache.txt
config.h
install_manifest.txt
/bin/
/ALL_BUILD.dir/
/doc/
/lib/
/Mod/
/ZERO_CHECK.dir/
/build/
/src/Tools/offlinedoc/localwiki/
/src/Tools/offlinedoc/*.txt
OpenSCAD_rc.py
.subuser-dev
/\.idea/
.tags
tags
================================================
FILE: A2plus.qrc
================================================
<RCC>
<qresource prefix="/">
<file>icons/a2p_SetRelativePathes.svg</file>
<file>icons/a2p_CenterOfMassConstraint.svg</file>
<file>icons/a2p_RecursiveUpdate.svg</file>
<file>icons/a2p_Asm.svg</file>
<file>icons/a2p_Obj.svg</file>
<file>icons/a2p_EditConstraint.svg</file>
<file>icons/a2p_DefineConstraints.svg</file>
<file>icons/a2p_AngleConstraint.svg</file>
<file>icons/a2p_AutoSolve.svg</file>
<file>icons/a2p_AxialConstraint.svg</file>
<file>icons/a2p_AxisParallelConstraint.svg</file>
<file>icons/a2p_AxisPlaneParallelConstraint.svg</file>
<file>icons/a2p_CheckAssembly.svg</file>
<file>icons/a2p_CircularEdgeConstraint.svg</file>
<file>icons/a2p_DegreesOfFreedomAnimation.svg</file>
<file>icons/a2p_DeleteConnections.svg</file>
<file>icons/a2p_DuplicatePart.svg</file>
<file>icons/a2p_EditPart.svg</file>
<file>icons/a2p_EditUndo.svg</file>
<file>icons/a2p_FlipConstraint.svg</file>
<file>icons/a2p_Help.svg</file>
<file>icons/a2p_ImportPart.svg</file>
<file>icons/a2p_ConvertPart.svg</file>
<file>icons/a2p_ImportPart_Update.svg</file>
<file>icons/a2p_Isolate_Element.svg</file>
<file>icons/a2p_LockRotation.svg</file>
<file>icons/a2p_MovePart.svg</file>
<file>icons/a2p_PartialProcessing.svg</file>
<file>icons/a2p_PartsInfo.svg</file>
<file>icons/a2p_PartsList.svg</file>
<file>icons/a2p_PartsList_CutListOptimizer.svg</file>
<file>icons/a2p_Pause.svg</file>
<file>icons/a2p_PlaneCoincidentConstraint.svg</file>
<file>icons/a2p_PlanesParallelConstraint.svg</file>
<file>icons/a2p_Play.svg</file>
<file>icons/a2p_PointIdentity.svg</file>
<file>icons/a2p_PointOnLineConstraint.svg</file>
<file>icons/a2p_PointOnPlaneConstraint.svg</file>
<file>icons/a2p_SimpleAssemblyShape.svg</file>
<file>icons/a2p_Solver.svg</file>
<file>icons/a2p_SphericalSurfaceConstraint.svg</file>
<file>icons/a2p_Stop.svg</file>
<file>icons/a2p_ToggleTransparency.svg</file>
<file>icons/a2p_ToggleAutoSolve.svg</file>
<file>icons/a2p_TogglePartial.svg</file>
<file>icons/a2p_Treeview.svg</file>
<file>icons/a2p_RepairTree.svg</file>
<file>icons/a2p_ViewConnection.svg</file>
<file>icons/a2p_Workbench.svg</file>
<file>icons/preferences-a2plus.svg</file>
</qresource>
</RCC>
================================================
FILE: CD_A2plusupdater.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2020 Dan Miel *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""
This is to be used in conjunction with A2plus Assembly Workbench.
It records the features which are linked to the constraints and searches
for mating features after the part is replaced in the assembly.
"""
import os
from PySide import QtCore, QtGui
try:
from PySide import QtUiTools
except ImportError:
pass
import FreeCAD
import FreeCADGui
import a2plib
import a2p_importpart
import CD_ConstraintViewer
from a2p_translateUtils import translate
class globaluseclass:
def __init__(self):
self.roundto = 5
self.partlabel = ''
self.partname = ''
self.notfoundfeatures = []
self.foundfeatures = []
self.alldicts = {}
self.clist = []
self.partobj = None
self.cylfaces = []
self.notcylfaces = []
self.partlog = []
def resetvars(self):
self.partlabel = ''
self.partname = ''
self.notfoundfeatures = []
self.foundfeatures = []
self.alldicts = {}
self.clist = []
self.partobj = None
self.cylfaces = []
self.notcylfaces = []
self.repaired = 0
g = globaluseclass()
class sideFuncs1():
def __init__(self):
pass
def opendoccheck(self):
doc = FreeCAD.activeDocument()
if doc is None:
mApp(
translate(
"A2plus",
"A file must be selected to start this selector.\n"
"Please open a file and try again."
)
)
return('Nostart')
return()
sideFuncs = sideFuncs1()
class classFuncs():
def __init__(self):
pass
def selectfiles(self):
ret = sideFuncs.opendoccheck()
if ret == 'Nostart':
return('No')
partslist = FreeCADGui.Selection.getSelection()
if len(partslist) == 0:
mApp(
translate(
"A2plus",
"No parts were selected to update.\n"
"Select one part and try again."
)
)
return('No')
if len(partslist) > 1:
mApp(
translate(
"A2plus",
"I have limited the number of parts that can be updated to 1.\n"
"Select one part and try again."
)
)
return('No')
statusform.show()
statusform.txtboxstatus.setText(translate("A2plus", "Updating Assembly."))
statusform.update()
for num in range(0, len(partslist)):
partobj = partslist[num]
self.firstrun(partobj)
self.secondrun(False)
def firstrun(self, partobj):
g.resetvars() # reset Variables
g.partobj = partobj
g.partlabel = partobj.Label
partname = partobj.Name
g.partname = partobj.Name
g.shape1 = partobj.Shape
self.getfeatstomove()
FreeCADGui.updateGui()
return(partname)
def secondrun(self, newpart=False):
doc = FreeCAD.activeDocument()
if newpart is False:
savedAutoSolveState = a2plib.getAutoSolveState()
a2plib.setAutoSolve(False)
a2p_importpart.updateImportedParts(doc, True)
a2plib.setAutoSolve(savedAutoSolveState)
newobj = g.partobj
FreeCADGui.updateGui()
g.shape2 = newobj.Shape
getfacelists()
self.runpostchange()
doc.recompute()
FreeCADGui.updateGui()
statusform.Closeme()
def runpostchange(self):
doc = FreeCAD.activeDocument()
self.findfeats_attempt1()
doc.recompute()
FreeCADGui.updateGui()
doc = FreeCAD.activeDocument()
FreeCADGui.updateGui()
clist = []
for e in g.notfoundfeatures:
cobj = FreeCAD.ActiveDocument.getObject(e[0])
clist.append(cobj)
if len(clist) != 0:
CD_ConstraintViewer.form1.show()
CD_ConstraintViewer.form1.loadtable(clist)
else:
mApp(translate("A2plus", "Update complete.") + " " + translate("A2plus", "All surfaces found"))
print(translate("A2plus", "Update complete:"))
print(translate("A2plus", "Total constraints - {}").format(str(len(g.clist))))
print(translate("A2plus", "Repaired constraints - {}").format((str(g.repaired))))
print(translate("A2plus", "Features not found - {}").format(str(len(g.notfoundfeatures))))
def getfeatstomove(self):
doc = FreeCAD.activeDocument()
clist = selectforpart(g.partlabel)
g.clist = clist
featname = ''
di = {}
for cobj in clist:
""" get feature info before update."""
partname = g.partname
featname = ''
subElement = ""
subElement = ""
subobj1 = doc.getObject(cobj.Object1)
subobj2 = doc.getObject(cobj.Object2)
frompart = [g.partlabel, g.partname]
for i in range(0, len(frompart)):
partname = frompart[i]
if subobj1.Label == partname:
subElement = "SubElement1"
featname = cobj.SubElement1
if subobj2.Label == partname:
subElement = "SubElement2"
featname = cobj.SubElement2
if featname != '':
break
direction = 'N'
if hasattr(cobj, 'directionConstraint'):
direction = cobj.directionConstraint
""" dict is basic info for constraint
these next functions adds info for the subelements"""
if 'Face' in featname:
""" add face info """
facenum = int(featname[4:])
di = self.getfacebynum(facenum-1, g.shape1)
if 'Edge' in featname:
""" add edge info """
num = int(featname[4:])
num = num - 1
di = self.getedgebynum(num, g.shape1)
if 'V' in featname:
""" add Vertex info """
num = int(featname[6:])
num = num - 1
di = self.getvertexbynum(num, g.shape1)
d = {'Name': cobj.Name,
'cname': cobj.Name,
'featname': featname,
'subElement': subElement,
'dir': direction,
'newname': ''
}
d.update(di)
g.alldicts[cobj.Name] = d # Save the info to a larger dictionary
def getfacebynum(self, facenum, shape):
"""Get face info."""
face = shape.Faces[facenum]
area = rondnum(face.Area)
facepoints = []
center = -1
faceedges = face.Edges
# numofpoints = len(face.Vertexes)
for f0 in face.Vertexes: # Search the Vertexes of the face
point = FreeCAD.Vector(f0.Point.x, f0.Point.y, f0.Point.z)
x, y, z = point
loc = rondlist([x, y, z])
facepoints.append(loc)
# volume = rondnum(face.Volume)
radius = -1
surftype = face.Surface
surfstr = str(surftype)
if 'Cylinder' in surfstr:
surfstr = 'Cylinder'
radius = rondnum(surftype.Radius)
center = rondlist(face.Edges[0].CenterOfMass)
if 'Plane' in surfstr:
surfstr = 'Plane'
featdict = {
'surftype': surfstr,
'area': area,
'facepoints': facepoints,
'center': center,
'radius': radius,
'edges': faceedges
}
return(featdict)
def getedgebynum(self, num, shape):
pnt2 = None
edge = shape.Edges[num]
length = rondnum(edge.Length)
center = edge.CenterOfMass
center = rondlist(center)
pnt1 = edge.Vertexes[0] # Basepoints
x1 = pnt1.Point.x
y1 = pnt1.Point.y
z1 = pnt1.Point.z
startpoint = rondlist([x1, y1, z1])
try:
pnt2 = edge.Vertexes[1] # Basepoints
x2 = pnt2.Point.x
y2 = pnt2.Point.y
z2 = pnt2.Point.z
endpoint = [x2, y2, z2]
endpoint = rondlist([x2, y2, z2])
except:
endpoint = ["-", "-", "-"]
radius = -1
vector = None
curvetype = ''
center = -1
tstr = str(edge.Curve)
if 'Line' in tstr:
curvetype = 'line'
if 'Circle' in tstr:
curvetype = 'circle'
radius = rondnum(edge.Curve.Radius)
center = rondlist(edge.CenterOfMass)
if 'Spline' in tstr:
curvetype = 'spline' # A2 is not using these
d = {
'curvetype': curvetype,
'obj': edge,
'length': length,
'startpoint': startpoint,
'center': center,
'endpoint': endpoint,
'radius': radius,
'vector': vector
}
return(d)
def getvertexbynum(self, num, shape):
v = shape.Vertexes[num]
x = v.Point.x
y = v.Point.y
z = v.Point.z
xyz = rondlist([x, y, z])
return({'xyz': xyz})
# post functions***********************************
def findfeats_attempt1(self):
"""Try to find features after the update."""
doc = FreeCAD.activeDocument()
for k, d in g.alldicts.items():
newfeat = ''
featname = d.get('featname')
if featname in g.foundfeatures:
newfeat = g.dOldNew.get(featname)
else:
if 'Face' in featname:
newfeat = self.findnewface_attempt1(d)
if 'Edge' in featname:
newfeat = self.findnewedge_attempt1(d)
if 'Vertex' in featname:
newfeat = self.findnewvertex_attempt1(d)
if newfeat == '' or newfeat == 'No':
g.notfoundfeatures.append([d.get('Name'), d])
pass
else:
if newfeat in g.foundfeatures is False:
g.foundfeatures.append(newfeat)
g.dOldNew[featname] = newfeat
self.swapfeature(newfeat, d)
doc.recompute()
if len(g.notfoundfeatures) > 0:
self.findfeats_attempt2()
def swapfeature(self, newfeat, d):
"""Add the new feature to the constraint."""
cname = d.get('cname')
g.partlog.append('Found ' + newfeat)
cobj = FreeCAD.ActiveDocument.getObject(cname)
mobj = FreeCAD.ActiveDocument.getObject(cname+'_mirror')
SubElement = d.get('subElement')
if SubElement == 'SubElement1':
if cobj.SubElement1 != newfeat:
cobj.SubElement1 = newfeat
mobj.SubElement1 = newfeat
g.repaired = g.repaired + 1
if SubElement == 'SubElement2':
if cobj.SubElement2 != newfeat:
cobj.SubElement2 = newfeat
mobj.SubElement2 = newfeat
g.repaired = g.repaired + 1
direction = d.get('dir')
if hasattr(cobj, 'directionConstraint'):
cobj.directionConstraint = direction
if hasattr(mobj, 'directionConstraint'):
mobj.directionConstraint = direction
return
# If not found on first attempt try again
def findfeats_attempt2(self):
newfeat = ''
notfoundtemp = g.notfoundfeatures
g.notfoundfeatures = []
for ea in notfoundtemp:
d = ea[1]
featname = d.get('featname')
if featname in g.foundfeatures:
newfeat = g.dOldNew.get(featname)
else:
if 'Face' in featname:
newfeat = self.findnewface_attempt2(d)
if 'Edge' in featname:
newfeat = self.findnewedge_attempt2(d)
if newfeat == 'No' or newfeat == '':
g.notfoundfeatures.append([d.get('Name'), d])
newfeat = 'None'
else:
if newfeat in g.foundfeatures is False:
g.foundfeatures.append(newfeat)
g.dOldNew[featname] = newfeat
self.swapfeature(newfeat, d)
def findnewface_attempt1(self, d):
# First attempt to find a face. Perfect fit is area the same and all points the same
face = ''
if d.get('surftype') == 'Cylinder':
face = self.findCylinderattempt1(d)
else:
for num in range(0, len(g.shape2.Faces)):
testd = self.getfacebynum(num, g.shape2)
if testd.get('surftype') != 'Cylinder':
if d.get('area') == testd.get('area') and d.get('facepoints') == testd.get('facepoints'):
face = 'Face' + str(num + 1)
break
return(face)
def findnewface_attempt2(self, dict_):
""" second attempt ignores area; looks for points(perhaps this should be first
if holes are added area would change)"""
face = ''
if dict_.get('surftype') == 'Cylinder':
face = self.findCylinderattempt2(dict_)
else:
for num in range(0, len(g.shape2.Faces)):
testdict_ = self.getfacebynum(num, g.shape2)
if dict_.get('surftype') != 'Cylinder':
points = dict_.get('facepoints')
testpoints = testdict_.get('facepoints')
if len(points) < len(testpoints):
list1 = points
list2 = testpoints
else:
list1 = testpoints
list2 = points
match = 0
for vert in list1:
if vert in list2:
match = match+1
if match == len(list1):
face = 'Face' + str(num + 1)
break
if face == '':
dedges = dict_.get('edges')
edge = dedges[0]
pnt1 = edge.Vertexes[0] # Basepoints
x = pnt1.Point.x
y = pnt1.Point.y
z = pnt1.Point.z
dlist = [rondnum(edge.Length), x, y, z]
for num in range(0, len(g.shape2.Faces)):
testdict_ = self.getfacebynum(num, g.shape2)
if dict_.get('surftype') != 'Cylinder':
ed = testdict_.get('edges')
for e in ed:
pnt1 = e.Vertexes[0] # Basepoints
x = pnt1.Point.x
y = pnt1.Point.y
z = pnt1.Point.z
tlist = [e.Length, x, y, z]
if dlist == tlist:
face = 'Face' + str(num + 1)
break
return(face)
def findCylinderattempt1(self, dict_):
face = ''
for num in g.cylfaces:
testdict = self.getfacebynum(num, g.shape2)
if dict_.get('facepoints') == testdict.get('facepoints') and\
dict_.get('radius') == testdict.get('radius'):
face = 'Face' + str(num + 1)
break
return(face)
def findCylinderattempt2(self, dict_):
# First attempt to find a face. Perfect fit is area = same all points = same
face = ''
ver1 = dict_.get('center')
for num in g.cylfaces:
testdict_ = self.getfacebynum(num, g.shape2)
ver2 = testdict_.get('center')
if ver1 == ver2:
face = 'Face' + str(num + 1)
break
return(face)
def findnewedge_attempt1(self, dict_):
edge = ''
if dict_.get('curvetype') == 'circle':
for num in range(0, len(g.shape2.Edges)):
testdict_ = self.getedgebynum(num, g.shape2)
if dict_.get('radius') == testdict_.get('radius') and dict_.get('center') == testdict_.get('center'):
edge = 'Edge' + str(num + 1)
break
else:
for num in g.notcylfaces:
testdict_ = self.getedgebynum(num, g.shape2)
if dict_.get('length') == testdict_.get('length')\
and dict_.get('center') == testdict_.get('center'):
edge = 'Edge' + str(num + 1)
break
return(edge)
def findnewedge_attempt2(self, dict_):
edge = ''
if dict_.get('curvetype') == 'circle':
center1 = dict_.get('center')
for num in range(0, len(g.shape2.Edges)):
testdict_ = self.getedgebynum(num, g.shape2)
center2 = testdict_.get('center')
if center1 == center2:
edge = 'Edge' + str(num + 1)
break
for num in range(0, len(g.shape2.Edges)):
testdict_ = self.getedgebynum(num, g.shape2)
if dict_.get('curvetype') == 'circle':
if testdict_.get('curvetype') == 'circle':
if dict_.get('startpoint') == testdict_.get('startpoint'):
edge = 'Edge' + str(num + 1)
break
return(edge)
def findnewvertex_attempt1(self, dict_):
vertex = ''
for num in range(0, len(g.shape2.Vertexes)):
test = self.getvertexbynum(num, g.shape2)
if dict_.get('xyz') == test.get('xyz'):
vertex = 'Vertex' + str(num + 1)
return(vertex)
funcs = classFuncs()
def getfacelists():
g.cylfaces = []
g.notcylfaces = []
for num in range(0, len(g.shape2.Faces)):
if str(g.shape2.Faces[num].Surface) == '<Cylinder object>':
g.cylfaces.append(num)
elif str(g.shape2.Faces[num].Surface) == '<Plane object>':
g.notcylfaces.append(num)
def selectforpart(partlabel, selectType='std'):
# find the constraints for the part selected
doc = FreeCAD.activeDocument()
clist = []
pnamelist = []
pnamelist.append(partlabel)
for obj in FreeCAD.ActiveDocument.Objects: # Select constraints
if 'ConstraintInfo' in obj.Content:
if '_mirror' not in obj.Name:
subobj1 = doc.getObject(obj.Object1)
subobj2 = doc.getObject(obj.Object2)
part1name = subobj1.Label
part2name = subobj2.Label
if selectType == 'betweenonly':
clist.append(obj)
else:
if part1name in pnamelist or part2name in pnamelist:
clist.append(obj)
return(clist)
def rondnum(num, rndto=g.roundto, mmorin='mm'):
# round a number to digits in global
# left in mm for accuracy.
rn = round(num, rndto)
if mmorin == 'in':
rn = rn/25.4
return(rn)
def rondlist(inpList, inch=False):
x = inpList[0]
y = inpList[1]
z = inpList[2]
x = rondnum(x)
y = rondnum(y)
z = rondnum(z)
if inch:
x = x/25.4
y = y/25.4
z = z/25.4
return([x, y, z])
class mApp(QtGui.QWidget):
# for error messages
def __init__(self, msg, msgtype='ok'):
super().__init__()
self.initUI(msg)
def initUI(self, msg, msgtype='ok'):
self.setGeometry(800, 300, 300, 400)
if msgtype == 'ok':
buttonReply = QtGui.QMessageBox.question(self, translate("A2plus", "Information"), msg, QtGui.QMessageBox.Ok | QtGui.QMessageBox.Ok)
if msgtype == 'yn':
buttonReply = QtGui.QMessageBox.question(self, translate("A2plus", "Information"), msg, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
if buttonReply == QtGui.QMessageBox.Yes:
return('y')
else:
return('n')
class formReport(QtGui.QDialog):
""" Form shows while updating edited parts. """
def __init__(self, name):
self.name = name
super(formReport, self).__init__()
self.setWindowTitle(translate("A2plus", "Constraint Checker"))
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setGeometry(300, 100, 300, 200) # xy, wh
self.setStyleSheet("font: 10pt arial MS")
self.txtboxstatus = QtGui.QTextEdit(self)
self.txtboxstatus.move(5, 30)
self.txtboxstatus.setFixedWidth(250)
self.txtboxstatus.setFixedHeight(60)
self.lblviewlabel = QtGui.QLabel(self)
self.lblviewlabel.setText(translate("A2plus", "Status"))
self.lblviewlabel.move(5, 5)
self.lblviewlabel.setFixedWidth(250)
self.lblviewlabel.setFixedHeight(20)
self.lblviewlabel.setStyleSheet("font: 13pt arial MS")
def showme(self, msg):
print(translate("A2plus", "Showing editing part"))
self.show()
def Closeme(self):
self.close()
def closeEvent(self, event):
self.close()
statusform = formReport('statusform')
class rnp_Update_A2pParts:
def Activated(self):
# funcs.runinorder()
funcs.selectfiles()
def Deactivated(self):
"""This function is executed when the workbench is deactivated"""
FreeCADGui.Selection.clearSelection()
return
def GetResources(self):
mypath = os.path.dirname(__file__)
return {
'Pixmap': mypath + "/icons/a2p_Update.svg",
'MenuText': translate("A2plus", "Updates parts from the A2plus workbench that has been modified"),
'ToolTip': translate("A2plus", "Updates the A2plus assembly when parts are modified.\n"
"To update the assembly, select the part that you have modified and press the icon.\n"
"When the update has finished run the A2plus solver to verify if there are broken constraints.\n"
"This is an attempt to reduce the number of broken constraints caused\n"
"when modifying a part from FreeCAD A2plus assembly workbench. This records the\n"
"constraints mating surfaces immediately before the update and tries to\n"
"reconnect them after the update.\n"
"If this fails you can undo this update by using the undo button\n"
"and running the standard A2plus updater.")
}
FreeCADGui.addCommand('rnp_Update_A2pParts', rnp_Update_A2pParts())
# =============================================================================
================================================
FILE: CD_CheckConstraints.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2020 Dan Miel *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
# This is to be used with A2plus Assembly WorkBench
# Tries to find constraints that are conflicting with each other.
import os
from PySide import QtGui, QtCore
# from PySide.QtGui import *
import FreeCAD
import FreeCADGui
import a2p_solversystem
import a2p_constraintServices
import CD_ConstraintViewer
translate = FreeCAD.Qt.translate
class globaluseclass:
def __init__(self):
self.checkingnum = 0
self.roundto = 3
# self.labelexist = False
# self.movedconsts = []
# self.allErrors = {}
self.errorList = []
self.conflicterror = False
g = globaluseclass()
class mApp(QtGui.QWidget):
# for error messages
def __init__(self, msg):
super().__init__()
self.title = translate("A2plus", "Information")
self.initUI(msg)
def initUI(self, msg, msgtype='ok'):
self.setWindowTitle(self.title)
self.setGeometry(100, 100, 320, 200)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
if msgtype == 'ok':
buttonReply = QtGui.QMessageBox.question(self, translate("A2plus", "Information"), msg, QtGui.QMessageBox.Ok | QtGui.QMessageBox.Ok)
if msgtype == 'yn':
buttonReply = QtGui.QMessageBox.question(self, translate("A2plus", "Information"), msg, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
if buttonReply == QtGui.QMessageBox.Yes:
pass
# print('Yes clicked.')
else:
pass
# print('No clicked.')
self.show()
class formMain(QtGui.QMainWindow):
def __init__(self, name):
self.name = name
super(formMain, self).__init__()
self.setWindowTitle(translate("A2plus", "Constraint Checker"))
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setGeometry(300, 100, 600, 140)
self.setStyleSheet("font:11pt arial MS")
self.txtboxReport = QtGui.QTextEdit(self)
self.txtboxReport.setGeometry(5, 50, 650, 90) # x, y, w, h
self.lblviewlabel = QtGui.QLabel(self)
self.lblviewlabel.setText(translate("A2plus", "To view the constraints, press 'Open Viewer'"))
self.lblviewlabel.move(5, 5)
self.lblviewlabel.setFixedWidth(350)
self.lblviewlabel.setFixedHeight(20)
self.lblviewlabel.setStyleSheet("font: 13pt arial MS")
self.btnOpenViewer = QtGui.QPushButton(self)
self.btnOpenViewer.move(365, 5)
self.btnOpenViewer.setFixedWidth(100)
self.btnOpenViewer.setFixedHeight(28)
self.btnOpenViewer.setToolTip(translate("A2plus", "View the listed constraints in the the Constraint Viewer."))
self.btnOpenViewer.setText(translate("A2plus", "Open Viewer"))
self.btnOpenViewer.clicked.connect(lambda: self.openViewer())
self.btnCloseForm = QtGui.QPushButton(self)
self.btnCloseForm.move(475, 5)
self.btnCloseForm.setFixedWidth(100)
self.btnCloseForm.setFixedHeight(28)
self.btnCloseForm.setToolTip(translate("A2plus", "Close this form."))
self.btnCloseForm.setText(translate("A2plus", "Close"))
self.btnCloseForm.clicked.connect(lambda: self.Closeme())
def openViewer(self):
CD_ConstraintViewer.form1.loadtable(g.errorList)
# clist = []
# doc = FreeCAD.activeDocument()
# for (k, v) in g.allErrors.items():
# cobj = doc.getObject(k)
# clist.append(cobj)
# CD_ConstraintViewer.form1.show()
# CD_ConstraintViewer.form1.loadtable(clist)
def resizeEvent(self, event):
# resize table
formx = self.width()
formy = self.height()
self.txtboxReport.resize(formx - 20, formy - 60)
def showme(self, msg):
self.txtboxReport.setText(msg)
self.show()
def Closeme(self):
self.close()
def closeEvent(self, event):
form1.Closeme()
self.close()
form1 = formMain('form1')
class classCheckConstraints():
def __init__(self):
self.name = None
self.dir_errors = []
self.rigids = []
self.floaters = []
def startcheck(self):
''' Check for opened file '''
if FreeCAD.activeDocument() is None:
msg = translate("A2plus", "A A2plus file must be opened to start this checker") + "\n" + translate("A2plus", "Please open a file and try again")
mApp(msg)
return
''' Getting rigids for a check '''
doc = FreeCAD.activeDocument()
ss = a2p_solversystem.SolverSystem()
ss.loadSystem(doc)
ss.assignParentship(doc)
rigids = ss.rigids
for e in rigids: # get rigid part
if e.disatanceFromFixed is None:
self.floaters.append(e.label)
self.rigids.append(e.label)
constraints = self.getallconstraints()
if len(constraints) == 0:
mApp(translate("A2plus", "Cannot find any constraints in this file."))
return()
statusform.showme(translate("A2plus", "Checking constraints"))
self.dir_errors = a2p_constraintServices.reAdjustConstraintDirections(FreeCAD.activeDocument())
print(self.dir_errors)
self.checkformovement(constraints, True)
if len(g.errorList) != 0:
form1.openViewer()
# msg = ''
# for e in g.allErrors:
# line = str(g.allErrors.get(e))
# msg = msg + line + '\n'
# form1.showme(msg)
else:
FreeCAD.Console.PrintMessage("")
FreeCAD.Console.PrintMessage(translate("A2plus", "No constraint errors found") + "\n")
statusform.Closeme()
def checkformovement(self, constraintlist, putPartBack=True):
doc = FreeCAD.activeDocument()
g.errorList = []
self.Bothpartsfixed = False
for checkingnum in range(0, len(constraintlist)):
self.errortype = ''
self.p1fix = False
self.p2fix = False
self.setfix = 0
cobj = constraintlist[checkingnum]
statusform.setWindowTitle(translate("A2plus", "Checking {} of {}").format(str(checkingnum), str(len(constraintlist))))
subobj1 = cobj.getPropertyByName('Object1')
subobj2 = cobj.getPropertyByName('Object2')
part1 = doc.getObject(subobj1) # Save Position and fixed
part2 = doc.getObject(subobj2)
''' Get if part is fixed '''
if hasattr(part1, "fixedPosition"):
self.p1fix = part1.fixedPosition
if hasattr(part2, "fixedPosition"):
self.p2fix = part2.fixedPosition
if cobj.Name in self.dir_errors:
errortype = 'Feature Missing'
self.addError(cobj, errortype)
continue
if self.p1fix and self.p2fix:
""" If both are fixed report and skip solving"""
self.addError(cobj, 'Both fixed')
continue
if part1.Label in self.floaters and part2.Label in self.floaters:
# If both parts are in floaters list report as Floaters
self.addError(cobj, 'Floating parts')
continue
if self.p1fix is False and self.p2fix is False:
# If neither part is fixed, fix part 1
if part1.Label in self.rigids:
part1.fixedPosition = True
self.setfix = 1
else:
part2.fixedPosition = True
self.setfix = 2
preBasePt1 = part1.Placement.Base
preBasePt2 = part2.Placement.Base
preRotPt1 = part1.Placement.Rotation.Axis
preRotPt2 = part2.Placement.Rotation.Axis
preAnglePt1 = part1.Placement.Rotation.Angle
preAnglePt2 = part2.Placement.Rotation.Angle
a2p_solversystem.solveConstraints(FreeCAD.activeDocument(), None, False, [cobj], showFailMessage=False) # solve a single constraint
if self.setfix == 1:
part1.fixedPosition = self.p1fix
if self.setfix == 2:
part2.fixedPosition = self.p2fix
self.setfix = 0
# Recording location after move
postBasePt1 = part1.Placement.Base # Round vectors to 4 places
postBasePt2 = part2.Placement.Base
''' Checking if part moved '''
v1 = FreeCAD.Vector(rondlist(preBasePt1))
v2 = FreeCAD.Vector(rondlist(postBasePt1))
v3 = FreeCAD.Vector(rondlist(preBasePt2))
v4 = FreeCAD.Vector(rondlist(postBasePt2))
if v1 != v2 or v3 != v4:
self.errortype = 'Conflict. '
self.addError(cobj, self.errortype)
errortype = ''
if putPartBack:
# Places part back in original location if putPartBack is True
part1.Placement.Base = preBasePt1
part1.Placement.Rotation.Axis = preRotPt1
part1.Placement.Rotation.Angle = preAnglePt1
part2.Placement.Base = preBasePt2
part2.Placement.Rotation.Axis = preRotPt2
part2.Placement.Rotation.Angle = preAnglePt2
def addError(self, cobj, errortype):
g.errorList.append([cobj, errortype])
def getallconstraints(self):
doc = FreeCAD.activeDocument()
constraints = []
for obj in doc.Objects:
if 'ConstraintInfo' in obj.Content:
if 'mirror' not in obj.Name:
constraints.append(obj)
return(constraints)
CheckConstraints = classCheckConstraints()
class formReport(QtGui.QDialog):
""" Form shows while updating edited parts. """
def __init__(self, name):
self.name = name
super(formReport, self).__init__()
self.setWindowTitle(translate("A2plus", "Checking Constraints"))
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setGeometry(300, 100, 300, 40) # x, y, w, h
self.setStyleSheet("font: 10pt arial MS")
def showme(self, msg):
self.setWindowTitle(msg)
self.show()
def Closeme(self):
self.close()
def closeEvent(self, event):
self.close()
statusform = formReport('statusform')
def rondlist(list, inch=False):
x = list[0]
y = list[1]
z = list[2]
x = rondnum(x)
y = rondnum(y)
z = rondnum(z)
if inch:
x = x/25.4
y = y/25.4
z = z/25.4
return([x, y, z])
def rondnum(num, mmorin='mm'):
# round a number to digits in global
# left in mm for accuracy.
rn = round(num, g.roundto)
if mmorin == 'in':
rn = rn / 25.4
return(rn)
class rnp_Constraint_Checker:
def Activated(self):
CheckConstraints.startcheck()
def onDeleteConstraint(self):
pass
def Deactivated():
pass
# This function is executed when the workbench is deactivated
def GetResources(self):
mypath = os.path.dirname(__file__)
return {
'Pixmap': mypath + "/icons/CD_ConstraintChecker.svg",
'MenuText': translate("A2plus", "Checks constraints"),
'ToolTip': translate("A2plus", "This checks all constraints. After checking it will list all constraints that it found problems with.") + "\n" +
translate("A2plus", "The list can then be opened in the Constraint viewer.")
}
FreeCADGui.addCommand('rnp_Constraint_Checker', rnp_Constraint_Checker())
================================================
FILE: CD_ConstraintViewer.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2020 Dan Miel *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
# This is to be used with A2plus Assembly WorkBench
# Tries to find constraints that are conflicting with each other.
import os
import sys
import subprocess
from PySide import QtCore, QtGui
try:
from PySide import QtUiTools
except ImportError:
pass
import FreeCAD
import FreeCADGui
import a2plib
import a2p_solversystem
import CD_CheckConstraints
import CD_FeatureLabels
translate = FreeCAD.Qt.translate
class globaluseclass:
def __init__(self):
self.checkingnum = 0
self.roundto = 4
self.labelexist = False
g = globaluseclass()
class mApp(QtGui.QWidget):
# for error messages
def __init__(self, msg, msgtype='ok'):
super().__init__()
self.initUI(msg)
def initUI(self, msg, msgtype='ok'):
self.setGeometry(100, 200, 320, 200)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
if msgtype == 'ok':
buttonReply = QtGui.QMessageBox.question(
self,
translate("A2plus", "Information"),
msg,
QtGui.QMessageBox.Ok | QtGui.QMessageBox.Ok
)
if msgtype == 'yn':
buttonReply = QtGui.QMessageBox.question(
self,
translate("A2plus", "Information"),
msg,
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.No
)
if buttonReply == QtGui.QMessageBox.Yes:
pass
# print('Yes clicked.')
else:
pass
# print('No clicked.')
self.show()
class ShowPartProperties(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.drt()
self.oldcell = ''
def drt(self):
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setGeometry(100, 50, 1024, 280) # x, y, w, h
self.setWindowTitle(translate("A2plus", "Constraint Viewer"))
self.setStyleSheet("font: 11pt arial MS")
bar = QtGui.QMenuBar(self)
labelMenu = bar.addMenu(translate("A2plus", "Labels"))
labelMenu.addAction(translate("A2plus", "Open Dialog"))
labelMenu.addAction(translate("A2plus", "Delete labels"))
labelMenu.triggered[QtGui.QAction].connect(self.process_menus)
infoMenu = bar.addMenu(translate("A2plus", "Info"))
infoMenu.addAction(translate("A2plus", "Places of accuracy = {}").format(str(g.roundto)))
infoMenu.triggered[QtGui.QAction].connect(self.process_menus)
helpMenu = bar.addMenu(translate("A2plus", "Help"))
helpMenu.addAction(translate("A2plus", "Open Help"))
helpMenu.triggered[QtGui.QAction].connect(self.process_menus)
""" Main Table """
self.tm = QtGui.QTableWidget(self)
self.tm.setGeometry(10, 120, 650, 50) # xy,wh
self.tm.setWindowTitle(translate("A2plus", "Broken Constraints"))
self.tm.setEditTriggers(QtGui.QTableWidget.NoEditTriggers)
self.tm.setRowCount(0)
self.tm.setColumnCount(11)
self.tm.setMouseTracking(True)
self.tm.cellClicked.connect(self.cell_was_clicked)
self.tm.setHorizontalHeaderLabels([translate("A2plus", "Direction"),
translate("A2plus", "Suppress"),
'Run',
'Constraint name',
'Prt1 feat',
'Prt2 feat',
'Part1',
'Part2',
'P1 Fixed',
'P2 Fixed',
'Problem'
]
)
self.tm.horizontalHeader().sectionClicked.connect(self.fun)
""" Creating function buttons """
self.btns = []
btnLabels = [
[translate("A2plus", "Import from part"), translate("A2plus", "Select a part and import \nall of the constraints for that part")],
[translate("A2plus", "Import from Tree"), translate("A2plus", "Copy selected constraints from the Tree")]
]
self.createButtonColumn(5, btnLabels)
btnLabels = [
[translate("A2plus", "Clear Table"), translate("A2plus", "Clear the table")],
[translate("A2plus", "Attach to"), translate("A2plus", "Select the feature to change in table.\nSelect surface to change to.")]
]
self.createButtonColumn(145, btnLabels)
btnLabels = [
[translate("A2plus", "Clear Tree"), translate("A2plus", "Remove search color from tree.")],
[translate("A2plus", "Find in Tree"), translate("A2plus", "Finds the constraint in the tree\nfor the select row in table.")]
]
self.createButtonColumn(285, btnLabels)
btnLabels = [
[translate("A2plus", "Std Solver"), translate("A2plus", "Same as the solver above.")],
[translate("A2plus", "Find w label"), translate("A2plus", "Press to toggle a label for selected feature.")]
]
self.createButtonColumn(425, btnLabels)
btnLabels = [
[translate("A2plus", "Close"), translate("A2plus", "Close this window")],
]
self.createButtonColumn(565, btnLabels)
def createButtonColumn(self, xloc, btnLabels):
for row in range(0, len(btnLabels)):
btny = 30 + (28*row)
self.btn = QtGui.QPushButton(str(btnLabels[row][0]), self)
self.btn.move(xloc, btny)
self.btn.setFixedWidth(140)
self.btn.setFixedHeight(25)
self.btn.setToolTip(btnLabels[row][1])
self.btn.released.connect(self.button_pushed) # pressed
self.btns.append(self.btn)
def button_pushed(self):
index = self.btns.index(self.sender())
buttext = self.btns[index].text()
if buttext == translate("A2plus", "Import from part"):
conflicts.selectforpart()
if buttext == translate("A2plus", "Import from Tree"):
conflicts.selectforTree()
if translate("A2plus", "Clear Table") in buttext:
self.clearTable()
if buttext == translate("A2plus", "Attach to"):
""" attaches leg to selected surface"""
sidefuncs.swapselectedleg()
if translate("A2plus", "Clear Tree") in buttext:
search.reset1()
if translate("A2plus", "Find in Tree") in buttext:
searchterm = lastclc.cname
search.startsearch(searchterm, 0)
if buttext == translate("A2plus", "Std Solver"):
self.stdSolve()
if buttext == translate("A2plus", "Find w label"):
""" createlabel for single part """
if g.labelexist:
CD_FeatureLabels.labels.deletelabels()
g.labelexist = False
return
fname = lastclc.text
if lastclc.column == 4:
pname = self.tm.item(lastclc.row, 7).text()
elif lastclc.column == 5:
pname = self.tm.item(lastclc.row, 9).text()
else:
mApp(translate("A2plus", "A part feature must be selected in the table"))
return
sels = FreeCAD.ActiveDocument.getObjectsByLabel(pname)
for e in sels:
try:
partobj = e # line is used to check if part is selected
except:
mApp(translate("A2plus", "The table has lost focus.\nPlease reselect in the table."))
return
s = FreeCADGui.Selection.getSelectionEx()[0]
try:
ent = s.SubObjects[0]
except:
mApp(translate("A2plus", "The selected text in the table is not a proper feature name:\n{} {}").format(fname, pname))
return
CD_FeatureLabels.labels.labelForTable(ent, fname)
g.labelexist = True
if buttext == translate("A2plus", "Find Constraint"):
search.startsearch(lastclc.cname, 0)
if buttext == translate("A2plus", "Close"):
self.Closeme()
def clearTable(self):
self.tm.setRowCount(0)
def process_menus(self, q):
""" process the menu according to the button text"""
if q.text() == translate("A2plus", "Open Dialog"):
CD_FeatureLabels.form1.showme()
if q.text() == translate("A2plus", "Delete labels"):
CD_FeatureLabels.labels.deletelabels()
if q.text() == translate("A2plus", "Open Help"):
# File name may be translated, e.g. to "CD_Помощь для инструмента Диагностики.pdf" (this file must be present)
pdf_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), translate("A2plus", "CD_Help for Diagnostic tools.pdf"))
# pdf_file = a2plib.get_module_path() + "\CD_Help for Diagnostic tools.pdf"
# For Linux Mint 21 64-bit with XFCE
if sys.platform in ['linux', 'linux2', 'darwin', 'cygwin']:
import webbrowser
webbrowser.open_new_tab(pdf_file)
# For Windows 10 Pro 64-bit
elif sys.platform == 'win32':
subprocess.Popen([pdf_file], shell=True)
# For others OS
else:
print("Found platform %s, OS %s" % (sys.platform, os.name))
def process_misc_menus(self, q):
menutext = q.text()
if menutext == "Solve without error checking":
conflicts.solveNOerrorchecking()
def stdSolve(self):
doc = FreeCAD.activeDocument()
a2p_solversystem.solveConstraints(doc)
def fun4(self, Ncol):
self.tm = self.tm.sort_values(self.tm.headers[Ncol], ascending=QtGui.AscendingOrder)
def fun(self, i):
# click in column header to sort column
self.tm.sortByColumn(i)
def loadtable(self, TempList):
self.showme()
ConstraintList = []
try:
test = str(len(TempList[0])) # if this fail array is only one column
ConstraintList = TempList
except:
for e in TempList: # Add second column to array if needed
ConstraintList.append([e, 'None'])
# fill the table with information from a list of constraints
self.tm.setRowCount(0)
doc = FreeCAD.activeDocument()
row = 0
for objects in reversed(ConstraintList):
object = objects[0]
problemstr = objects[1]
try:
cname = object.Name
constraint = doc.getObject(cname)
except:
continue
ob1 = doc.getObject(constraint.Object1)
if hasattr(ob1, 'fixedPosition') is False:
fixed1 = 'N'
else:
fixed1 = str(ob1.fixedPosition)
ob2 = doc.getObject(constraint.Object2)
if hasattr(ob2, 'fixedPosition') is False:
fixed2 = 'N'
else:
ob2 = doc.getObject(constraint.Object2)
fixed2 = str(ob2.fixedPosition)
part1 = doc.getObject(constraint.Object1)
part2 = doc.getObject(constraint.Object2)
if hasattr(constraint, "directionConstraint"):
direction = constraint.directionConstraint
else:
direction = 'None'
self.tm.insertRow(0)
fn1 = constraint.SubElement1
fn2 = constraint.SubElement2
if len(fn1) == 0:
fn1 = 'None'
if len(fn2) == 0:
fn2 = 'None'
direction = QtGui.QTableWidgetItem(direction)
sup = QtGui.QTableWidgetItem(str(constraint.Suppressed))
run = QtGui.QTableWidgetItem(str('Run'))
name = QtGui.QTableWidgetItem(cname)
fixed1 = QtGui.QTableWidgetItem(fixed1)
Part1 = QtGui.QTableWidgetItem(part1.Label)
fname1 = QtGui.QTableWidgetItem(fn1)
fixed2 = QtGui.QTableWidgetItem(fixed2)
Part2 = QtGui.QTableWidgetItem(part2.Label)
fname2 = QtGui.QTableWidgetItem(fn2)
problem = QtGui.QTableWidgetItem(str(problemstr))
self.tm.setItem(0, 0, direction)
self.tm.setItem(0, 1, sup)
self.tm.setItem(0, 2, run)
self.tm.setItem(0, 3, name)
self.tm.setItem(0, 4, fname1)
self.tm.setItem(0, 5, fname2)
self.tm.setItem(0, 6, Part1)
self.tm.setItem(0, 7, Part2)
self.tm.setItem(0, 8, fixed1)
self.tm.setItem(0, 9, fixed2)
self.tm.setItem(0, 10, problem)
if self.tm.item(0, 4).text() == 'None':
self.tm.item(0, 4).setBackground(QtGui.QBrush(QtGui.QColor('yellow')))
if self.tm.item(0, 5).text() == 'None':
self.tm.item(0, 5).setBackground(QtGui.QBrush(QtGui.QColor('yellow')))
row = row+1
header = self.tm.horizontalHeader()
header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
self.current_hover = [0, 0]
self.hoveronoff(True)
self.oldcell = self.tm.item(2, 1)
self.tm.current_hover = [0, 0]
for row in range(self.tm.rowCount()):
self.tm.setRowHeight(row, 15)
def hoveronoff(self, val):
self.tm.setMouseTracking(val)
def cell_was_clicked(self, row, column):
header = self.tm.horizontalHeaderItem(column).text()
item = self.tm.item(row, 3)
lastclc.cellpicked(row, column)
cname = item.text()
try:
constraint = FreeCAD.ActiveDocument.getObject(cname)
partobj1 = FreeCAD.ActiveDocument.getObject(constraint.Object1)
partobj2 = FreeCAD.ActiveDocument.getObject(constraint.Object2)
except:
mApp(translate("A2plus", "Constraint is not in file. Was it deleted?"))
return
FreeCADGui.Selection.clearSelection()
if header == 'Run':
conflicts.checkforfixandsolve([constraint])
FreeCADGui.Selection.addSelection(partobj1, constraint.SubElement1)
FreeCADGui.Selection.addSelection(partobj2, constraint.SubElement2)
if header == 'Constraint name':
FreeCADGui.Selection.addSelection(partobj1, constraint.SubElement1)
FreeCADGui.Selection.addSelection(partobj2, constraint.SubElement2)
if header == 'Prt1 feat':
FreeCADGui.Selection.addSelection(partobj1, constraint.SubElement1)
g.lastclickedFeat = FreeCADGui.Selection.getSelection()
if header == 'Prt2 feat':
FreeCADGui.Selection.addSelection(partobj2, constraint.SubElement2)
g.lastclickedFeat = FreeCADGui.Selection.getSelection()
FreeCADGui.Selection.setPreselection
if header == 'Part1':
FreeCADGui.Selection.addSelection(partobj1)
if header == 'Part2':
FreeCADGui.Selection.addSelection(partobj2)
if header == translate("A2plus", "Suppress"):
if constraint.Suppressed is False:
constraint.Suppressed = True
else:
constraint.Suppressed = False
tx = str(constraint.Suppressed)
item2 = self.tm.item(row, column)
item2.setText(tx)
if header == translate("A2plus", "Direction"):
item2 = self.tm.item(row, column)
if item2.text() != 'None':
direction = constraint.directionConstraint
if direction == 'opposed':
newdir = 'aligned'
else:
newdir = 'opposed'
constraint.directionConstraint = newdir
direction = constraint.directionConstraint
item2 = self.tm.item(row, column)
# item2.setText(direction[0])
item2.setText(direction)
conflicts.checkforfixandsolve([constraint])
def showme(self):
if FreeCADGui.activeDocument() is None:
mApp(translate("A2plus", "A file must be opened to start this selector.\nPlease open a file and try again"))
return()
self.clearTable()
self.show()
lastclc.clear
def Closeme(self):
# close window and ensure that obsever is off
selObv.SelObserverOFF()
self.close()
def closeEvent(self, event):
selObv.SelObserverOFF()
form1.Closeme()
self.close()
def resizeEvent(self, event):
""" resize table """
formx = self.width()
formy = self.height()
self.tm.resize(formx - 20, formy - 120)
form1 = ShowPartProperties()
class classconflictreport():
def __init__(self):
self.name = None
def selectforTree(self):
doc = FreeCAD.activeDocument()
clist = []
sels = FreeCADGui.Selection.getSelectionEx()
if len(sels) == 0:
form1.clearTable()
mApp(translate("A2plus", "Nothing was selected in the Tree."))
return
for sel in sels:
cname = sel.Object.Name
cname = cname.replace('_mirror', '')
cobj = doc.getObject(cname)
if 'ConstraintInfo' in cobj.Content:
clist.append(cobj)
if len(clist) == 0:
form1.clearTable()
mApp(translate("A2plus", "There were no constraints selected in the Tree.\nSelect one or more constraints and try again."))
return
form1.loadtable(clist)
# select a part in the Gui and the attached constraints are sent to the form.
def selectforpart(self):
pnamelist = []
doc = FreeCAD.activeDocument()
clist = []
sels = FreeCADGui.Selection.getSelectionEx()
if len(sels) == 0:
mApp(translate("A2plus", "No parts were selected in the window."))
return
if len(sels) == 1:
pnamelist.append(sels[0].Object.Label)
else:
for sel in sels:
pnamelist.append(sel.Object.Label)
for obj in FreeCAD.ActiveDocument.Objects: # Select constraints
if 'ConstraintInfo' in obj.Content and '_mirror' not in obj.Name:
subobj1 = doc.getObject(obj.Object1)
subobj2 = doc.getObject(obj.Object2)
part1name = subobj1.Label
part2name = subobj2.Label
if len(sels) == 1:
if part1name in pnamelist or part2name in pnamelist:
clist.append(obj)
else:
if part1name in pnamelist and part2name in pnamelist:
clist.append(obj)
if len(clist) == 0:
if len(sels) == 1:
msg = translate("A2plus", "There are no constraints for this part.")
else:
msg = translate("A2plus", "There are no constraints between these parts.")
mApp(msg)
return
form1.loadtable(clist)
def checkforfixandsolve(self, constraintlist):
''' Checks to see if both parts are fixed, then solves constraint. '''
if len(constraintlist) == 0:
return
doc = FreeCAD.activeDocument()
cobj = constraintlist[0]
cobj = constraintlist[g.checkingnum]
subobj1 = cobj.getPropertyByName('Object1')
subobj2 = cobj.getPropertyByName('Object2')
part1 = doc.getObject(subobj1) # Save Position and fixed
part2 = doc.getObject(subobj2)
self.p1fix = False
self.p2fix = False
''' Get if both parts are fixed '''
if hasattr(part1, "fixedPosition"):
self.p1fix = part1.fixedPosition
if hasattr(part2, "fixedPosition"):
self.p2fix = part2.fixedPosition
if self.p1fix and self.p2fix:
mApp(translate("A2plus", "Both parts are fixed."))
return
''' if neither is fixed '''
if self.p1fix is False and self.p2fix is False:
part1.fixedPosition = True
a2p_solversystem.solveConstraints(doc, matelist=constraintlist, showFailMessage=False)
if hasattr(part1, "fixedPosition"):
part1.fixedPosition = self.p1fix
return
conflicts = classconflictreport()
class classsidefunctions():
def __init__(self, name):
self.name = name
self.sel1 = ''
def swapselectedleg(self):
# starts observer to select a new feature when replacing manually.
if lastclc.column < 4 or lastclc.column > 5:
mApp(translate("A2plus", "Surfaces can only be replaced in columns\n'Part1 feat' or 'Part2 feat'"))
return
if len(FreeCADGui.Selection.getSelectionEx()) == 0 and lastclc.text != 'None':
mApp(translate("A2plus", "No feature has been selected"))
return
selObv.SelObserverON()
def turnoffobserv(self):
# Turns observer off and selects both features
selObv.SelObserverOFF()
self.swap1leg()
def swap1leg(self):
""" This is used to swap one surface for another manually """
feat2name = ""
if len(FreeCADGui.Selection.getSelectionEx()) == 0:
return
sel = FreeCADGui.Selection.getSelectionEx()[0]
if lastclc.text == 'None':
feat2name = sel.SubElementNames[0]
else:
feat2name = sel.SubElementNames[0]
cname = lastclc.cname
FreeCADGui.Selection.clearSelection()
d = {'cname': cname,
'SubElement': lastclc.SubElement,
'dir': lastclc.dir,
'newfeat': feat2name
}
self.swapfeature(d)
cobj = FreeCAD.ActiveDocument.getObject(cname)
partobj1 = FreeCAD.ActiveDocument.getObject(cobj.Object1)
partobj2 = FreeCAD.ActiveDocument.getObject(cobj.Object2)
if sel.Object.Name != partobj1.Name and sel.Object.Name != partobj2.Name:
mApp(translate("A2plus", "The constraint can only be moved to another surface of the same part"))
return
FreeCADGui.Selection.addSelection(partobj1, cobj.SubElement1)
FreeCADGui.Selection.addSelection(partobj2, cobj.SubElement2)
""" Adds new feature name to table """
form1.tm.item(lastclc.row, lastclc.column).setText(feat2name)
def swapfeature(self, newfeaturedict):
# changes a legs mating feature
newfeat = newfeaturedict.get('newfeat')
cname = newfeaturedict.get('cname')
cobj = FreeCAD.ActiveDocument.getObject(cname)
mobj = FreeCAD.ActiveDocument.getObject(cname+'_mirror')
SubElement = newfeaturedict.get('SubElement')
if SubElement == 'SubElement1':
cobj.SubElement1 = newfeat
mobj.SubElement1 = newfeat
if SubElement == 'SubElement2':
cobj.SubElement2 = newfeat
mobj.SubElement2 = newfeat
direction = newfeaturedict.get('dir')
if hasattr(cobj, 'directionConstraint'):
cobj.directionConstraint = direction
if hasattr(mobj, 'directionConstraint'):
mobj.directionConstraint = direction
return
sidefuncs = classsidefunctions('sidefuncs')
class SelObserver:
def __init__(self):
pass
def SelObserverON(self):
FreeCADGui.Selection.addObserver(selObv)
def SelObserverOFF(self):
# print('SelObserverOFF')
try:
FreeCADGui.Selection.removeObserver(selObv)
except:
print(translate("A2plus", "removeObserver failed in C checker"))
def setPreselection(self, doc, obj, sub): # Preselection object
pass
def addSelection(self, doc, obj, sub, pnt): # Selection object
sidefuncs.turnoffobserv()
def removeSelection(self, doc, obj, sub): # Delete the selected object
pass
def setSelection(self, doc):
# this is sent from menu
# funcs.constraintselected('table') # funcs does not exist ??!!
pass
selObv = SelObserver()
class classsearch():
''' This is for searching in tree for constraint name '''
def __init__(self):
self.founditems = []
def startsearch(self, searchterm, colnum):
mw = FreeCADGui.getMainWindow()
tab = mw.findChild(QtGui.QTabWidget, u'combiTab')
tree = tab.widget(0).findChildren(QtGui.QTreeWidget)[0]
top = tree.topLevelItem(0)
for idx in range(top.childCount()):
self.searchTreeItem(tree, top.child(idx), searchterm, colnum)
def searchTreeItem(self, tree, item, searchterm, colnum):
for idx in range(item.childCount()):
itm = item.child(idx)
if searchterm in itm.text(colnum):
itm.setBackground(0, QtGui.QColor(255, 255, 0, 100))
self.expandParent(tree, itm)
self.searchTreeItem(tree, item.child(idx), searchterm, colnum)
def expandParent(self, tree, item):
parent = item.parent()
if parent:
tree.expandItem(parent)
self.expandParent(tree, parent)
def resetAll(self, item):
for idx in range(item.childCount()):
itm = item.child(idx)
self.founditems.append(itm)
itm.setBackground(0, QtGui.QBrush())
self.resetAll(itm)
def reset1(self):
mw = FreeCADGui.getMainWindow()
tab = mw.findChild(QtGui.QTabWidget, u'combiTab')
tree = tab.widget(0).findChildren(QtGui.QTreeWidget)[0]
top = tree.topLevelItem(0)
for idx in range(top.childCount()):
self.resetAll(top.child(idx))
search = classsearch()
def rondlist(inputList, inch=False):
x = inputList[0]
y = inputList[1]
z = inputList[2]
x = rondnum(x)
y = rondnum(y)
z = rondnum(z)
if inch:
x = x/25.4
y = y/25.4
z = z/25.4
return([x, y, z])
def rondnum(num, mmorin='mm'):
"""" round a number to digits in global
left in mm for accuracy. """
rn = round(num, g.roundto)
if mmorin == 'in':
rn = rn / 25.4
return(rn)
class classlastclickeditem:
def __init__(self, Name):
self.row = -1
self.column = -1
self.header = ''
self.cname = ''
self.cobj = None
self.dir = 'N'
self.text = ''
self.SubElement = ''
def clear(self):
self.row = -1
self.column = -1
self.header = ''
self.cname = ''
self.cobj = None
self.dir = 'N'
def cellpicked(self, row, column):
item = form1.tm.item(row, column)
self.item = item
self.row = row
self.column = column
self.text = item.text()
self.header = form1.tm.horizontalHeaderItem(column).text()
citem = form1.tm.item(self.row, 3)
cname = citem.text()
self.cname = cname
self.cobj = FreeCAD.ActiveDocument.getObject(self.cname)
if hasattr(self.cobj, 'directionConstraint'):
self.dir = self.cobj.directionConstraint
if self.column == 4:
self.SubElement = 'SubElement1'
if self.column == 5:
self.SubElement = 'SubElement2'
return(self.SubElement)
lastclc = classlastclickeditem("lastclc")
class rnp_Constraint_Viewer:
def Activated(self):
form1.showme()
def Deactivated(self):
"""This function is executed when the workbench is deactivated"""
return
def GetResources(self):
mypath = os.path.dirname(__file__)
return {
'Pixmap': mypath + "/icons/CD_ConstraintViewer.svg",
'MenuText': translate("A2plus", "View and edit selected constraints"),
'ToolTip': translate("A2plus", "Constraint Viewer. You can view the features the constraint is attached to,\n"
"run a single constraint or change the the feature the constraint is attached to.\n"
"See the help for more information."
)
}
FreeCADGui.addCommand('rnp_Constraint_Viewer', rnp_Constraint_Viewer())
================================================
FILE: CD_FeatureLabels.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2020 Dan Miel *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
# This is to be used with A2plus Assembly WorkBench
# from PySide.QtGui import *
from PySide import QtGui, QtCore
import FreeCAD
import FreeCADGui
translate = FreeCAD.Qt.translate
class formMain(QtGui.QMainWindow):
def __init__(self, name):
self.name = name
super(formMain, self).__init__()
self.setWindowTitle(translate("A2plus", "Create Labels"))
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setGeometry(300, 200, 260, 200)
self.setStyleSheet("font: 11pt arial MS")
self.btnLabels = [[translate("A2plus", "Add Face Labels"), translate("A2plus", "Add labels to all of the faces on a selected part")],
[translate("A2plus", "Add Edge Labels"), translate("A2plus", "Add labels to all of the edges on a selected part")],
[translate("A2plus", "Add Vertex Labels"), translate("A2plus", "Add labels to all of the vertices on a selected part")],
[translate("A2plus", "Delete Labels"), translate("A2plus", "Delete all labels")],
[translate("A2plus", "Close"), translate("A2plus", "Close this window")]
]
self.btns = []
BtnNum = 0
for row in range(0, len(self.btnLabels)):
btny = 20 + (28*row)
self.btn = QtGui.QPushButton(str(self.btnLabels[row][0]), self)
self.btn.move(5, btny)
self.btn.setFixedWidth(250)
self.btn.setFixedHeight(25)
self.btn.setToolTip(self.btnLabels[row][1])
self.btn.released.connect(self.button_pushed) # pressed
self.btns.append(self.btn)
BtnNum = BtnNum + 1
def button_pushed(self):
index = self.btns.index(self.sender())
btext = self.btns[index].text()
if btext == translate("A2plus", "Add Face Labels"):
labels.addlabels(translate("A2plus", "Face"))
if btext == translate("A2plus", "Add Edge Labels"):
labels.addlabels(translate("A2plus", "Edge"))
if btext == translate("A2plus", "Add Vertex Labels"):
labels.addlabels(translate("A2plus", "Vertex"))
if btext == translate("A2plus", "Delete Labels"):
labels.deletelabels()
if btext == translate("A2plus", "Close"):
self.Closeme()
# Not present?
if btext == 'Attach to':
labels.attachto()
if btext == 'Selected Labels':
labels.selectedlabels()
def hideMe(self):
QtGui.Selection.clearSelection()
self.close()
def showme(self):
self.show()
def Closeme(self):
self.close()
def closeEvent(self, event):
form1.Closeme()
self.close()
form1 = formMain('form1')
class classLabels():
def __init__(self):
self.labelGroup = None
def checkselection(self):
"""Checks to see if labels already exist."""
doc = FreeCAD.activeDocument()
self.labelGroup = doc.getObject("partLabels")
if self.labelGroup is None:
self.labelGroup = doc.addObject("App::DocumentObjectGroup", "partLabels")
if len(FreeCADGui.Selection.getSelection()) == 0:
mApp(translate("A2plus", "One part must be selected.") + "\n" + translate("A2plus", "Please select One part and try again"))
return(False)
return(True)
def addlabels(self, feat):
sel = self.checkselection()
if not sel:
return
sel = FreeCADGui.Selection.getSelection() # Select an object
if feat == translate("A2plus", "Face"):
features = sel[0].Shape.Faces
if feat == translate("A2plus", "Edge"):
features = sel[0].Shape.Edges
if feat == translate("A2plus", "Vertex"):
features = sel[0].Shape.Vertexes
for num in range(0, len(features)):
ent = features[num]
if feat == translate("A2plus", "Vertex"):
loc = ent.Point
else:
loc = ent.CenterOfMass
partLabel = self.makelabel(ent, feat+str(num+1), loc)
self.labelGroup.addObject(partLabel)
def makelabel(self, ent, name, loc):
partLabel = FreeCAD.ActiveDocument.addObject("App::AnnotationLabel", "partLabel")
partLabel.LabelText = name
partLabel.BasePosition.x = loc[0]
partLabel.BasePosition.y = loc[1]
partLabel.BasePosition.z = loc[2]
partLabel.ViewObject.BackgroundColor = (1.0, 1.0, 0.0)
partLabel.ViewObject.TextColor = (0.0, 0.0, 0.0)
return(partLabel)
def deletelabels(self):
for obj in FreeCAD.ActiveDocument.Objects:
if "partLabel" in obj.Label:
FreeCAD.ActiveDocument.removeObject(obj.Name)
def attachto(self, sel=None, featname=''):
sel = self.checkselection()
if not sel:
return
if featname == '':
featname = form1.txtboxaddlabel.text()
if sel is None:
sel = FreeCADGui.Selection.getSelection()[0]
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(sel, featname)
s = FreeCADGui.Selection.getSelectionEx()[0]
ent = s.SubObjects[0]
self.makelabel(ent, name, loc)
def getEntLoc(self, ent, featname):
if 'V' in featname:
loc = ent.Point
else:
loc = ent.CenterOfMass
partLabel = self.makelabel(ent, featname, loc)
self.labelGroup.addObject(partLabel)
self.makelabel(ent, featname, loc)
def labelForTable(self, ent, featname):
"""Create a label to find a part."""
sel = self.checkselection()
self.getEntLoc(ent, featname)
def selectedlabels(self):
sel = self.checkselection()
if not sel:
return
sels = FreeCADGui.Selection.getSelectionEx() # Select an object
for sel in sels:
featname = sel.SubElementNames[0]
ent = sel.SubObjects[0]
self.getentloc(ent, featname)
labels = classLabels()
class mApp(QtGui.QWidget):
''' This message box was added to make this file a standalone file'''
# for error messages
def __init__(self, msg):
super().__init__()
self.initUI(msg)
def initUI(self, msg):
self.setGeometry(100, 100, 400, 300)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
QtGui.QMessageBox.question(self, translate("A2plus", "Info"), msg, QtGui.QMessageBox.Ok | QtGui.QMessageBox.Ok)
self.show()
================================================
FILE: CD_OneButton.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2020 Dan Miel *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""
This is to be used in conjunction with A2plus Assembly Workbench.
Enables two features to be selected without using the control key.
"""
import os
from PySide import QtGui, QtCore
import FreeCAD
import FreeCADGui
translate = FreeCAD.Qt.translate
class globaluseclass:
def __init__(self, name):
self.sONOFF = 'off'
self.feat1 = ''
self.buttonenabled = False
self.obj1 = ''
self.partselected = False
g = globaluseclass("g")
class onebutton:
def readselect(self, doc, obj, sub):
if g.partselected:
g.partselected = False
return
sels = len(FreeCADGui.Selection.getSelectionEx())
if sub == "":
pass
elif sels == 1:
if g.obj1 == '' and g.feat1 == '' or obj == g.obj1:
g.obj1 = obj
g.feat1 = sub
obj = ''
sub = ''
elif g.obj1 != '' and g.feat1 != '':
if obj != '' and sub != '':
try:
obj1 = FreeCAD.ActiveDocument.getObject(g.obj1)
obj2 = FreeCAD.ActiveDocument.getObject(obj)
FreeCADGui.Selection.addSelection(obj1, g.feat1)
FreeCADGui.Selection.addSelection(obj2, sub)
g.partselected = True
except:
pass
g.feat1 = ''
g.obj1 = ''
obj = ''
sub = ''
class SelObserver:
def __init__(self):
pass
def SelObserverON(self):
if g.sONOFF != 'on':
FreeCADGui.Selection.addObserver(selObv)
g.sONOFF = 'on'
# print('SelObserverON')
def SelObserverOFF(self):
try:
FreeCADGui.Selection.removeObserver(selObv)
g.sONOFF = 'off'
# print('SelObserverOFF')
except:
FreeCAD.Console.PrintMessage(translate("A2plus", "SelObserverOFF by except") + "\n")
def addSelection(self, doc, obj, sub, pnt): # Selection object
onebutton.readselect(onebutton, doc, obj, sub)
def removeSelection(self, doc, obj, sub): # Delete the selected object
pass
def setSelection(self, doc):
pass
selObv = SelObserver()
# This class looks for mouse clicks in space to unselect parts.
class ViewObserver:
def __init__(self):
self.view = None
self.o = None
self.c = None
def vostart(self):
self.view = FreeCADGui.activeDocument().activeView()
self.o = ViewObserver()
self.c = self.view.addEventCallback("SoMouseButtonEvent", self.o.logPosition)
def vooff(self):
try:
self.view.removeEventCallback("SoMouseButtonEvent", self.c)
except Exception as e:
print(str(e))
def logPosition(self, myinfo):
down = (myinfo["State"] == "DOWN")
up = (myinfo["State"] == "UP")
pos = myinfo["Position"]
if up:
pass
if (down):
if myinfo['Button'] == 'BUTTON1':
pos = FreeCADGui.ActiveDocument.ActiveView.getCursorPos()
partinfo = FreeCADGui.activeDocument().activeView().getObjectInfo(pos)
if partinfo is None:
g.feat1 = ''
g.obj1 = ''
FreeCADGui.Selection.clearSelection()
else:
pass
viewob = ViewObserver()
class rnp_OneButton:
def GetResources(self):
mypath = os.path.dirname(__file__)
return {
'Pixmap': mypath + "/icons/CD_OneButton.svg",
'MenuText': translate("A2plus", "Use one mouse button to select features"),
'ToolTip': translate("A2plus", "Use left mouse button to select two features.\nDo not use the control key."),
'Checkable': self.IsChecked()
}
def Activated(self, placeholder=None):
if FreeCAD.activeDocument() is None:
mApp(translate("A2plus", "No file is opened.\nYou must open an assembly file first."))
return
FreeCADGui.Selection.clearSelection()
if g.buttonenabled is False:
selObv.SelObserverON() # Checks for part and entity click
viewob.vostart() # Checks for click in background
g.buttonenabled = True
FreeCAD.Console.PrintMessage(translate("A2plus", "OneButton is ON") + "\n")
else:
selObv.SelObserverOFF()
viewob.vooff()
g.buttonenabled = False
FreeCAD.Console.PrintMessage(translate("A2plus", "OneButton is OFF") + "\n")
def Deactivated(self):
"""This function is executed when the workbench is deactivated."""
selObv.SelObserverOFF()
viewob.vooff()
def IsChecked(self):
return(g.buttonenabled)
def IsActive(self):
return(True)
FreeCADGui.addCommand('rnp_OneButton', rnp_OneButton())
class mApp(QtGui.QWidget):
"""This message box was added to make this file a standalone file"""
# for error messages
def __init__(self, msg, msgtype='ok'):
super().__init__()
self.title = translate("A2plus", "Warning")
self.left = 100
self.top = 100
self.width = 400
self.height = 300
self.initUI(msg)
def initUI(self, msg):
# self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
QtGui.QMessageBox.question(self, translate("A2plus", "Warning"), msg, QtGui.QMessageBox.Ok | QtGui.QMessageBox.Ok)
self.show()
================================================
FILE: GuiA2p/Resources/compile_resources_pack.py
================================================
#! /usr/bin/env python
import os, glob
qrc_filename = 'resources.qrc'
assert not os.path.exists(qrc_filename)
qrc = '''<RCC version="1.0">
<qresource prefix="/a2p_">'''
for fn in glob.glob('./icons/*.svg') + glob.glob('./ui/*.ui'):
qrc = qrc + '\n\t\t<file>%s</file>' % fn
qrc = qrc + '''\n\t</qresource>
</RCC>'''
print(qrc)
f = open(qrc_filename,'w')
f.write(qrc)
f.close()
os.system('rcc -binary %s -o resources.rcc' % qrc_filename)
os.remove(qrc_filename)
================================================
FILE: GuiA2p/Resources/ui/a2p_prefs.ui
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::Dialog::DlgSettingsA2Plus</class>
<widget class="QWidget" name="Gui::Dialog::DlgSettingsA2Plus">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>680</width>
<height>825</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>A2plus settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>User interface settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_6">
<property name="toolTip">
<string>Adds a creation button for every constraint type to the toolbar</string>
</property>
<property name="text">
<string>Show constraints in toolbar</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>showConstraintsOnToolbar</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_10">
<property name="text">
<string>Use native file manager of your OS</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>useNativeFileManager</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
<property name="title">
<string>Behavior when updating imported parts</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_4">
<property name="toolTip">
<string>All parts of the assembly will be opened in FreeCAD to be
reconstructed using values from spreadsheets</string>
</property>
<property name="text">
<string>Recalculate imported parts before updating them (experimental)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>recalculateImportedParts</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_5">
<property name="toolTip">
<string>Opens all subassemblies recursively
to update them</string>
</property>
<property name="text">
<string>Enable recursive update of imported parts</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>enableRecursiveUpdate</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_2">
<property name="toolTip">
<string>While importing parts to the assembly, the topological names
are written into "mux Info" property. When the parts are
later updated the properties "Sub Elementx" of the constraints
will be updated according to the "mux Info" topology.</string>
</property>
<property name="text">
<string>Use experimental topological naming</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>useTopoNaming</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_7">
<property name="toolTip">
<string>Use color and transparency settings
from imported parts.
Note: For WB PartDesign it work for Body only.</string>
</property>
<property name="text">
<string>Inherit per face color and transparency from parts and subassemblies (experimental)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>usePerFaceTransparency</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_8">
<property name="toolTip">
<string>Invisible datum/construction shapes will be hidden.
Note: No constraints must be connected to
datum/construction shapes in higher or other
subassemblies. Otherwise you can break the assembly.</string>
</property>
<property name="statusTip">
<string>All imported parts will directly be put together as union.</string>
</property>
<property name="text">
<string>Do not import invisible shapes (for expert users)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>doNotImportInvisibleShapes</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_9">
<property name="text">
<string>Use solid union for importing parts and subassemblies (experimental)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>useSolidUnion</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QGroupBox" name="groupBox_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Storage of files</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="Gui::PrefRadioButton" name="radioButton_3">
<property name="text">
<string>Use relative paths for imported parts</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>useRelativePathes</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefRadioButton" name="radioButton_4">
<property name="text">
<string>Use absolute paths for imported parts</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>useAbsolutePathes</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefRadioButton" name="radioButton_5">
<property name="toolTip">
<string>Specify the project folder in the field below</string>
</property>
<property name="text">
<string>All files are in this project folder:</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>useProjectFolder</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefFileChooser" name="fileChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>16777215</height>
</size>
</property>
<property name="prefEntry" stdset="0">
<cstring>projectFolder</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Default solving method</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="Gui::PrefRadioButton" name="radioButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Solver begins with a fixed part and a part constrained to it.
All other parts are not calculated. If a solution could be
found, the next constrained part is added for the
calculation and so on.</string>
</property>
<property name="text">
<string>Use solving of partial systems (recommended for static assemblies)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>usePartialSolver</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefRadioButton" name="radioButton_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Solver tries to move all parts at once
in direction to a fixed part</string>
</property>
<property name="text">
<string>Use "magnetic" solver, solving all parts at once (for dynamical assemblies)</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>useMagneticSolver</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="checkBox_3">
<property name="toolTip">
<string>All parts will be fixed to the positions
where they were created</string>
</property>
<property name="text">
<string>Force fixed position to all imports</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>forceFixedPosition</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Default solver behavior</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="Gui::PrefCheckBox" name="checkBox">
<property name="text">
<string>Solve automatically if a constraint property is changed</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>autoSolve</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/A2plus</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<customwidgets>
<customwidget>
<class>Gui::FileChooser</class>
<extends>QWidget</extends>
<header>Gui/FileDialog.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefFileChooser</class>
<extends>Gui::FileChooser</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefRadioButton</class>
<extends>QRadioButton</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
================================================
FILE: Init.py
================================================
#***************************************************************************
#* *
#* Copyright (c) 2018 kbwbe *
#* *
#* Portions of code based on hamish's assembly 2 *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
================================================
FILE: InitGui.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2018 kbwbe *
# * *
# * Portions of code based on hamish's assembly 2 *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
__title__ = 'A2plus assembly Workbench - InitGui file'
__author__ = 'kbwbe'
# import sys
import FreeCAD
import FreeCADGui
import a2plib
# Need to determine if we are on a system using PySide2 or PySide6
try:
import PySide6 # try Qt6 first
import a2p_Resources3_Qt6
except ImportError:
import a2p_Resources3
translate = FreeCAD.Qt.translate
QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP
# add translations path
FreeCADGui.addLanguagePath(a2plib.getLanguagePath())
FreeCADGui.updateLocale()
class A2plusWorkbench (Workbench):
def __init__(self):
import a2plib
translate = FreeCAD.Qt.translate
self.__class__.Icon = a2plib.get_module_path() + "/icons/a2p_Workbench.svg"
self.__class__.MenuText = 'A2plus'
self.__class__.ToolTip = translate("A2plus", "An other assembly workbench for FreeCAD.")
def Initialize(self):
# import sys
import a2plib
# import a2p_Resources3
# translate = FreeCAD.Qt.translate
# add translations functions
translate = FreeCAD.Qt.translate
QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP
# print A2plus version
FreeCAD.Console.PrintMessage(
translate(
"A2plus", "Initializing A2plus Workbench v{}"
).format(a2plib.getA2pVersion()) + ".\n"
)
# add icons path
FreeCADGui.addIconPath(':/icons')
import a2p_importpart
import a2p_recursiveUpdatePlanner
import a2p_convertPart
import a2p_solversystem
import a2p_MuxAssembly
import a2p_partinformation
import a2p_ConstraintDialog
import a2p_ConstraintCommands
import a2p_BoM # BoM == Bill of Materials == partslist
import a2p_constraintServices
import a2p_searchConstraintConflicts
import CD_A2plusupdater # for Constraint Diagnostic function
import CD_CheckConstraints
import CD_OneButton
# Create list of commands for toolbar A2p_Part and menu
partCommands = [
'a2p_ImportPart',
'a2p_ImportShapeReferenceCommand',
'a2p_updateImportedParts',
'a2p_movePart',
'a2p_MovePartUnderConstraints',
'a2p_duplicatePart',
'a2p_ConvertPart',
'a2p_editImportedPart',
'a2p_SaveAndExit_Command',
]
if a2plib.getRecursiveUpdateEnabled():
partCommands.insert(3, 'a2p_recursiveUpdateImportedPartsCommand')
# Create list of commands for toolbar A2p_constraint
constraintCommands = [
'a2p_ConstraintDialogCommand',
'a2p_EditConstraintCommand',
'a2p_reAdjustConstraintDirectionsCommand',
'a2p_DeleteConnectionsCommand',
]
if a2plib.SHOW_CONSTRAINTS_ON_TOOLBAR:
constraintCommands1 = [
'a2p_PointIdentityConstraintCommand',
'a2p_PointOnLineConstraintCommand',
'a2p_PointOnPlaneConstraintCommand',
'a2p_SphericalSurfaceConstraintCommand',
'a2p_CircularEdgeConnection',
'a2p_AxialConstraintCommand',
'a2p_AxisParallelConstraintCommand',
'a2p_AxisPlaneParallelCommand',
'a2p_AxisPlaneNormalCommand',
'a2p_AxisPlaneAngleCommand',
'a2p_PlanesParallelConstraintCommand',
'a2p_PlaneCoincidentConstraintCommand',
'a2p_AngledPlanesConstraintCommand',
'a2p_CenterOfMassConstraintCommand',
]
constraintCommands.extend(constraintCommands1)
# Create list of commands for toolbar A2p_Solver
solverCommands = [
'a2p_SolverCommand',
'a2p_ToggleAutoSolveCommand',
'a2p_FlipConstraintDirectionCommand',
'a2p_Show_Hierarchy_Command',
'a2p_SearchConstraintConflictsCommand'
]
if a2plib.GRAPHICALDEBUG:
solverCommands.append(
'a2p_cleanUpDebug3dCommand'
)
# Create list of commands for toolbar A2p_View
viewCommands = [
'a2p_isolateCommand',
'a2p_ViewConnectionsCommand',
'a2p_Restore_Transparency',
'a2p_ToggleTransparencyCommand',
'a2p_Show_PartLabels_Command',
'a2p_Show_DOF_info_Command',
]
# Create list of commands for toolbar A2p_Misc
miscCommands = [
'a2p_SimpleAssemblyShapeCommand',
'a2p_repairTreeViewCommand',
'a2p_CreatePartInformationSheet_Command',
'a2p_CreatePartlist',
'a2p_CreateCutListOptimizerPartlist'
]
# Create list of commands for toolbar A2p_Diagnostics
DiagnosticCommands = [
'rnp_Constraint_Viewer',
'rnp_Update_A2pParts',
'rnp_Constraint_Checker',
'rnp_OneButton',
]
menuEntries = [
'a2p_absPath_to_relPath_Command',
'a2p_MigrateProxiesCommand'
]
# Create toolbars
self.appendToolbar(
'A2p_Part',
partCommands
)
self.appendToolbar(
'A2p_Constraint',
constraintCommands
)
self.appendToolbar(
'A2p_Solver',
solverCommands
)
self.appendToolbar(
'A2p_View',
viewCommands
)
self.appendToolbar(
'A2p_Misc',
miscCommands
)
self.appendToolbar(
'A2p_Diagnostics',
DiagnosticCommands
)
# Create menus
self.appendMenu(
'A2plus',
partCommands
)
self.appendMenu(
['A2plus', QT_TRANSLATE_NOOP("Workbench", "Constraint")],
constraintCommands
)
self.appendMenu(
['A2plus', QT_TRANSLATE_NOOP("Workbench", "Solver")],
solverCommands
)
self.appendMenu(
['A2plus', QT_TRANSLATE_NOOP("Workbench", "View")],
viewCommands
)
miscCommands.extend(menuEntries)
self.appendMenu(
['A2plus', QT_TRANSLATE_NOOP("Workbench", "Misc")],
miscCommands
)
self.appendMenu(
['A2plus', QT_TRANSLATE_NOOP("Workbench", "Diagnostic")],
DiagnosticCommands
)
FreeCADGui.addPreferencePage(
a2plib.get_module_path() +
'/GuiA2p/Resources/ui/a2p_prefs.ui', 'A2plus'
)
def Activated(self):
import a2p_observers
FreeCAD.addDocumentObserver(a2p_observers.redoUndoObserver)
def Deactivated(self):
import a2p_observers
FreeCAD.removeDocumentObserver(a2p_observers.redoUndoObserver)
def ContextMenu(self, recipient):
import FreeCAD
import FreeCADGui
selection = [s for s in FreeCADGui.Selection.getSelection() if s.Document == FreeCAD.ActiveDocument]
if len(selection) == 1:
obj = selection[0]
if 'sourceFile' in obj.Content:
self.appendContextMenu(
"A2plus",
[
'a2p_movePart',
'a2p_duplicatePart',
'a2p_editImportedPart',
'a2p_ConvertPart',
'a2p_DeleteConnectionsCommand',
'a2p_ToggleTransparencyCommand'
]
)
Gui.addWorkbench(A2plusWorkbench())
================================================
FILE: LICENSE
================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
(This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.)
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
{signature of Ty Coon}, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
================================================
FILE: README.md
================================================
<a href="https://github.com/kbwbe/A2plus"><img src="https://github.com/kbwbe/A2plus/blob/master/icons/a2p_Workbench.svg" height="100px" width="100px"></a>
# A2plus
[Documentation](https://wiki.freecadweb.org/A2plus_Workbench) •
[Forum](https://forum.freecadweb.org/viewtopic.php?f=20&t=29207) •
[Git repository](https://github.com/kbwbe/A2plus)
[](https://lgtm.com/projects/g/kbwbe/A2plus/alerts/) [](https://lgtm.com/projects/g/kbwbe/A2plus/context:python)
## Description
Another assembly workbench for FreeCAD, following and extending [Hamish's Assembly 2 workbench](https://github.com/hamish2014/FreeCAD_assembly2) hence Assembly2**plus**.
The main goal of A2plus is to create a very simple, easy to use, and not over-featured workbench for FreeCAD assemblies. Using the KISS principle: KEEP IT SIMPLE, STUPID
A2plus workbench tries to implement a new constraint solving algorithm to avoid some problems that occurred with the original solver. It is still under development and experimental at this moment but is showing some potential.
This project started as a fork of Hamish's Assembly 2 workbench (development on A2 went dormant and was unmaintained for a long period). A2plus can be used with FreeCAD from v0.16 including support for importing parts from external files.
[](http://www.youtube.com/watch?v=QMxcQ5tssWk "A2plus: Assembly of a gripping unit.")
## Assembly2 and A2plus
### Similarities
* The workflow and the UI, so users of Assembly 2 can use it in an intuitive way
* Same as Assembly 2 it mainly aims at importing external files to the assembly.
### Differences
* A new designed solving algorithm, able to solve some more complicated relations.
* Different and in future more constraints, internally with different names.
* No animation for degrees of freedom, as difficult for new solver type.
* No collision check of parts at moment. Planned for future versions
* Some new small features as visibility helpers (isolate and show only selected parts, transparency of whole assembly)
## Is A2plus compatible with Assembly2 ?
No. A2plus would have to handle everything in same way as Assembly 2, including bugs, exact orientations, etc.
You have to assemble existing projects again.
## Releases
There are prereleases available, please browse the [releases](https://github.com/kbwbe/A2plus/releases) section of this repository.
## Known Issues
A weak point is, and is similar to Assembly 2, updating / reimporting parts from external files.
Constraints will break. You should delete constraints of parts before reimporting them.
After that please constrain these parts again.
This behaviour is due to FreeCAD's lack of [Topological Naming](https://wiki.freecadweb.org/Topological_naming_problem) and is difficult to correct at the moment.
This phenomena is seen in many proprietary CAD solutions. Some work is being done to address this in future releases of FreeCAD which will improve this behaviour.
## Installation
### Automatic Installation
[](https://github.com/FreeCAD/FreeCAD-addons)
The recommended way to install A2plus is via FreeCAD's [Addon Manager](https://wiki.freecadweb.org/Addon_Manager) under the Tools → 'Addon Manager' dropdown menu.
### Manual Installation
<details>
<summary>The following directions explain how to install A2plus manually on different platforms</summary>
#### Linux
FreeCAD(version of your choice) has to be installed beforehand.
Use the CLI to install A2plus:
```bash
$ sudo apt-get install git python-numpy python-pyside
$ mkdir ~/.FreeCAD/Mod
$ cd ~/.FreeCAD/Mod
$ git clone https://github.com/kbwbe/A2plus.git
```
Once installed, use `git` to easily update to the latest version:
```bash
$ cd ~/.FreeCAD/Mod/A2plus
$ git pull
$ rm *.pyc
```
#### Windows
Pre-requisite: FreeCAD >= v0.16+
Download the git repository as a ZIP on to you local drive.
Refer to the [corresponding tutorial](https://wiki.freecadweb.org/How_to_install_additional_workbenches) on the FreeCAD wiki.
Unzip the downloaded repository within your Mod/ folder. A A2plus-folder should appear
within you Mod/ folder.
If you a familiar with `git` you can `git clone https://github.com/kbwbe/A2plus.git` directly in to your Mod/ folder.
#### MacOS
(borrowed from Hamish2014)
* download the git repository as ZIP
* assuming FreeCAD is installed in "/Applications/FreeCAD/v 0.17",
go to "/Applications/FreeCAD/v 0.17" in the Browser, and select FreeCAD.app
* right-click and select "Show Package Contents", a new window will appear with a folder named "Contents"
* single-click on the folder "Contents" and select the folder "Mod"
* in the folder "Mod" create a new folder named "A2plus"
* unzip downloaded repository in the folder "Contents/Mod/A2plus"
</details>
Features of the A2plus workbench
--------------------------------
(work in progress)
Current Features like shown in the workbench's toolbar:
* Add a part from external file (Shift+A) -
Begin and continue here with importing existing part or subassembly .fcstd files to the assembly
* Update parts imported into the assembly -
Use this to refresh changed parts already assembled
* Move part -
Just move selected part
* Duplicate part -
Adds one or more previously imported part(s) into assembly (hold Shift for multiple times)
* Convert part to A2plus form -
Converts an imported part to internal representation without external dependency
* Edit -
Opens the selected assembly part or subassembly in a new tab, to be changed, don't forget
to save and refresh the assembly
* Constraint Tools -
Open a dialog to define constraints. Find all constraints in the opening dialog!
This is the access to the A2plus constraining possibilities.
Depending on the context, like selected faces, edges, vertices, one or more of the following
list of constraints may get selectable:
(After selecting the constraint, a 'Constraint Properties' dialog will appear to appropriately ask
you for details, like offsets, angles and directions.)
Below, first selection is meant for the first part of the constraint (parent) and the second
for the second part (child). Choices lists, what you can expect to edit in "Constraint Properties"
and with the "Edit selected constraint" button later on.
- Add a point-to-point identity {pointIdentity constraint} -
(1. one point vertex, 2. second point vertex)
- Add a point-on-line match {pointOnLine constraint} -
(1. one point vertex, 2. a line vertex/ edge)
- Add a point-on-plane match {pointOnPlane constraint} -
(1. point vertex or center of a circle, 2. a plane)
Choices: offset
- Add a sphere-to-sphere constraint {sphereCenterIdent constraint} -
(1. first spherical surface or vertex, 2. second spherical surface or vertex)
- Add a circular-to-circular-edge match {circularEdge constraint} -
(1. parent's circular edge, 2. child's circular edge)
Choices: direction (aligned, opposed) +Flip, offset
- Add an axis-to-axis identity {axisCoincident constraint} -
(1. first cylinder face/linear edge, 2. second cylinder face/linear edge)
Choices: direction (aligned, opposed) + Flip, lockRotation
- Add an axis-to-axis parallelism {axisParallel constraint}
(1. first cylinder face/linear edge, 2. second cylinder face/linear edge)
Selected parts will get rotated, but the axis not coincident.
- Add an axis-to-plane parallelism {axisPlaneParallel constraint}
(1. first cylinder axis or linear edge, 2. second part's plane face)
- Add a plane-to-plane parallelism {planesParallel constraint} -
(1. parent's plane, 2. child's plane)
Selected planes would be parallel but not coincident.
Choices: direction (aligned, opposed) +Flip
- Add a plane-to-plane coincident match {planeCoincident constraint} -
(1. parent's plane, 2. child's plane)
Selected planes would be parallel and you have more choices:
Choices: direction (aligned, opposed) +Flip, offset
- Add an angle-between-planes {angledPlanes constraint} -
Selected planes make the latter object to be rotated by your edited 'angle' value.
Keep the angle between approx. 0.1° and 179.9° and use planesParallel for 0° and 180°.
* Edit selected constraint -
Select a constraint in the treeview and hit this button to edit it's properties
* Delete constraints -
Remove all constraints of exactly one selected part in one step
* Solve A2plus constraints -
Manually invoke the A2pus solver (especially when AutoSolve is OFF)
* Toggle Autosolve -
By pressing this button you can enable or disable automatic solving after a constraint
has been edited. If Autosolve is disabled you have to start it manually by hitting the
Solve button. Disabled, this can save computation time.
* Flip direction of last constraint -
does exactly what it means for suitable constraints
* Print detailed DOF information to console -
shows the degrees-of-freedom for the current constraints' solving state,
useful for analysing eventually missing constraints
* Generate HTML file with detailed constraining structure -
useful to visualize the current constraint dependencies
* Show connected elements -
highlights the parts connected by a constraint selected in treeview
* Toggle transparency of assembly -
The whole assembly will get transparent
* Show only selected items, or all if none selected -
Another visibility helper for assembly analysis
* Create or refresh simple shape of complete assembly -
the newly created compound can be found in treeview
* Repair the treeview, if damaged somehow -
After pressing this button constraints will be grouped under corresponding parts again
* Create a spreadsheet with logistic/ordering information -
Adds a spreadsheet to the treeview, editable by double-click in a new tab for part's info
* Create a spreadsheet with a partlist of this file -
Adds a spreadsheet to the treeview, editable by double-click in a new tab for assembly's BOM info
Usage hints for the A2plus workbench
------------------------------------
(work in progress)
Have a look on the Feature list above, and...
Please, follow the Tooltips in the workbench's toolbar and in the "Constraint Tools" toolbox. They
describe what to do in which order.
First steps to create an A2plus assembly:
* Create a new empty document.
* Save the active document with a name. If not, you'd be asked for by A2plus.
* Add a part or shapes from external file. The first imported part or shape gets set as fixed position (by default). You can change it later.
* Add a second part or shapes from external file.
* Select some faces or edges or vertices, you want to constrain, and push the "Constraint Tools" button,
the Tools menu pops-up,
alternatively you can push the button first and select the constraint's context afterwards
* Related to the context you'd be asked in the "Constraints Properties" (sub-menu), to edit the
appropriate parameters, to delete the constraint, to solve and or accept it.
* You can edit once-set "Constraints Properties" at any time later via the "Edit selected constraint" button.
Editing a subassembly:
* As you can also load a subassembly as a .fcstd file, you can also open it via the A2plus edit command,
to edit it. Please just make sure for higher assembly stages, to reload the changes file(s).
================================================
FILE: __init__.py
================================================
================================================
FILE: a2p_BoM.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2018 kbwbe *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import os
import string
from PySide import QtGui, QtCore
import FreeCAD
import FreeCADGui
import Part
import a2plib
import Spreadsheet
# from a2p_fcdocumentreader import FCdocumentReader
from a2p_simpleXMLreader import FCdocumentReader
from a2p_partlistglobals import (
PARTLIST_COLUMN_NAMES,
CLO_PARTLIST_COLUMN_NAMES,
BOM_SHEET_NAME,
BOM_SHEET_LABEL,
CLO_BOM_SHEET_NAME,
CLO_BOM_SHEET_LABEL,
PARTINFORMATION_SHEET_NAME,
BOM_MAX_COLS,
BOM_MAX_LENGTH
)
translate = FreeCAD.Qt.translate
def createCutListOptimizerPartList(
importPath,
parentAssemblyDir,
partListEntries,
recursive=False
):
"""
Extract quantities and descriptions of assembled parts from
document.xml
Is able to analyse subassemblies by recursion
It works with a dict. Structure of an entry is:
filename: [Quantity,[information,information,....] ]
CutListOptimizer (https://cutlistoptimizer.com/) expects the following CSV format:
Length,Width,Qty,Material,Label,Enabled
200,300,3,DEFAULT_MATERIAL,200×300,true
NOTE: The column Material could be skipped, if it is the same for all parts.
"""
fileNameInProject = a2plib.findSourceFileInProject(
importPath,
parentAssemblyDir
)
workingDir, basicFileName = os.path.split(fileNameInProject)
docReader1 = FCdocumentReader()
docReader1.openDocument(fileNameInProject)
for ob in docReader1.getA2pObjects():
# skip converted parts...
if a2plib.to_str(ob.getA2pSource()) == a2plib.to_str('converted'):
continue
if ob.isSubassembly() and recursive:
partListEntries = createPartList(
ob.getA2pSource(),
workingDir,
partListEntries,
recursive
)
# Process information of this a2p object
if not ob.isSubassembly() or not recursive:
# Try to get spreadsheetdata _PARTINFO_ from linked source
linkedSource1 = ob.getA2pSource()
# this returns unicode on py2 systems!
linkedSource = a2plib.findSourceFileInProject(
linkedSource1,
workingDir
)
if linkedSource is None:
print(translate("A2p_BoM", "BOM ERROR: Could not open sourcefile '{}'").format(
linkedSource1))
continue
# Is it already processed minimum one time ?
entry = partListEntries.get(linkedSource, None)
if entry is not None:
# count sourcefile usage
partListEntries.get(linkedSource)[0] += 1
continue # only needed to count imports of this file, information exists yet
# There is no entry in dict, need to read out information from importFile...
docReader2 = FCdocumentReader()
docReader2.openDocument(linkedSource)
# Initialize a default parts information...
partInformation = []
for i in range(0, len(CLO_PARTLIST_COLUMN_NAMES)):
partInformation.append("*")
# if there is a proper spreadsheet, then read it...
for ob in docReader2.getSpreadsheetObjects():
sheetName = PARTINFORMATION_SHEET_NAME
sheetName = a2plib.to_bytes(PARTINFORMATION_SHEET_NAME)
if ob.name == sheetName:
cells = ob.getCells()
for addr in cells.keys():
if addr[:1] == b'B': # column B contains the data, A only the titles
idx = int(addr[1:])-1
if idx < len(CLO_PARTLIST_COLUMN_NAMES): # don't read further!
partInformation[idx] = cells[addr]
# last entry of partinformations is reserved for filename
# without complete path...
partInformation[-1] = os.path.split(linkedSource)[1]
# #########################################################
# add dimensions from the overall bounding box of the file
# in the last 3 fields before the filename
dc = FreeCAD.openDocument(linkedSource)
op = Part.makeCompound([i.Shape for i in dc.findObjects(
"Part::Feature") if i.ViewObject.Visibility and not i.TypeId == "PartDesign::Plane"])
bb = op.BoundBox
partInformation[-2] = str(bb.ZLength)
partInformation[-3] = str(bb.YLength)
partInformation[-4] = str(bb.XLength)
FreeCAD.closeDocument(dc.Name)
# #########################################################
# put information to dict and count usage of sourcefiles..
entry = partListEntries.get(linkedSource, None)
if entry is None:
partListEntries[linkedSource] = [
1,
partInformation
]
else:
# count sourcefile usage
partListEntries.get(linkedSource)[0] += 1
return partListEntries
def createPartList(
importPath,
parentAssemblyDir,
partListEntries,
recursive=False
):
"""
Extract quantities and descriptions of assembled parts from
document.xml
Is able to analyse subassemblies by recursion
It works with a dict. Structure of an entry is:
filename: [Quantity,[information,information,....] ]
"""
fileNameInProject = a2plib.findSourceFileInProject(
importPath,
parentAssemblyDir
)
workingDir, basicFileName = os.path.split(fileNameInProject)
docReader1 = FCdocumentReader()
docReader1.openDocument(fileNameInProject)
for ob in docReader1.getA2pObjects():
# skip converted parts...
if a2plib.to_str(ob.getA2pSource()) == a2plib.to_str('converted'):
continue
if ob.isSubassembly() and recursive:
partListEntries = createPartList(
ob.getA2pSource(),
workingDir,
partListEntries,
recursive
)
# Process information of this a2p object
if not ob.isSubassembly() or not recursive:
# Try to get spreadsheetdata _PARTINFO_ from linked source
linkedSource1 = ob.getA2pSource()
# this returns unicode on py2 systems!
linkedSource = a2plib.findSourceFileInProject(
linkedSource1,
workingDir
)
if linkedSource is None:
print(translate("A2p_BoM", "BOM ERROR: Could not open sourcefile '{}'").format(
linkedSource1))
continue
# Is it already processed minimum one time ?
entry = partListEntries.get(linkedSource, None)
if entry is not None:
# count sourcefile usage
partListEntries.get(linkedSource)[0] += 1
continue # only needed to count imports of this file, information exists yet
# There is no entry in dict, need to read out information from importFile...
docReader2 = FCdocumentReader()
docReader2.openDocument(linkedSource)
# Initialize a default parts information...
partInformation = []
for i in range(0, len(PARTLIST_COLUMN_NAMES)):
partInformation.append("*")
# if there is a proper spreadsheet, then read it...
for ob in docReader2.getSpreadsheetObjects():
sheetName = PARTINFORMATION_SHEET_NAME
sheetName = a2plib.to_bytes(PARTINFORMATION_SHEET_NAME)
if ob.name == sheetName:
cells = ob.getCells()
for addr in cells.keys():
if addr[:1] == b'B': # column B contains the data, A only the titles
idx = int(addr[1:])-1
if idx < len(PARTLIST_COLUMN_NAMES): # don't read further!
partInformation[idx] = cells[addr]
# last entry of partinformations is reserved for filename
# without complete path...
partInformation[-1] = os.path.split(linkedSource)[1]
# put information to dict and count usage of sourcefiles..
entry = partListEntries.get(linkedSource, None)
if entry is None:
partListEntries[linkedSource] = [
1,
partInformation
]
else:
# count sourcefile usage
partListEntries.get(linkedSource)[0] += 1
return partListEntries
class a2p_CreatePartlist():
def clearPartList(self):
alphabet_list = list(string.ascii_uppercase)
doc = FreeCAD.activeDocument()
ss = doc.getObject(BOM_SHEET_NAME)
for i in range(0, BOM_MAX_COLS):
for k in range(0, BOM_MAX_LENGTH):
cellAdress = alphabet_list[i] + str(k + 1)
ss.set(cellAdress, '')
def Activated(self):
doc = FreeCAD.activeDocument()
if doc is None:
QtGui.QMessageBox.information(
QtGui.QApplication.activeWindow(),
translate("A2plus", "No active document found!"),
translate("A2plus", "You have to open a FCStd file first.")
)
return
completeFilePath = doc.FileName
p, f = os.path.split(completeFilePath)
flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No
msg = translate(
"A2plus", "Please save before generating a parts list! Save now?")
response = QtGui.QMessageBox.information(QtGui.QApplication.activeWindow(
), translate("A2plus", "Save document?"), msg, flags)
if response == QtGui.QMessageBox.No:
QtGui.QMessageBox.information(
QtGui.QApplication.activeWindow(),
translate("A2plus", "Parts list generation aborted!"),
translate("A2plus", "You have to save the assembly file first.")
)
return
else:
doc.save()
flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No
msg = translate(
"A2plus", "Do you want to iterate recursively over all included subassemblies?")
response = QtGui.QMessageBox.information(
QtGui.QApplication.activeWindow(), translate("A2p_BoM", "PARTSLIST"), msg, flags)
if response == QtGui.QMessageBox.Yes:
subAssyRecursion = True
else:
subAssyRecursion = False
partListEntries = createPartList(
doc.FileName,
p,
{},
recursive=subAssyRecursion
)
ss = None
try:
ss = doc.getObject(BOM_SHEET_NAME)
except:
pass
if ss is None:
ss = doc.addObject('Spreadsheet::Sheet', BOM_SHEET_NAME)
ss.Label = BOM_SHEET_LABEL
else:
self.clearPartList()
# Write Column headers to spreadsheet
ss.set('A1', translate("A2p_BoM", "POS"))
ss.set('B1', translate("A2p_BoM", "QTY"))
self.CreateColumnHeadersInSpreadsheet(ss, PARTLIST_COLUMN_NAMES, 'C')
# fill entries for partsList...
idx3 = ord('A')
for idx, k in enumerate(partListEntries.keys()):
ss.set('A' + str(idx + 2), str(idx + 1))
ss.set('B' + str(idx + 2), str(partListEntries[k][0]))
values = partListEntries[k][1]
for j, tx in enumerate(values): # all strings inside values are unicode!
ss.set(chr(idx3 + 2 + j) + str(idx + 2),
tx.replace(''', ''))
# recompute to finish...
doc.recompute()
FreeCAD.Console.PrintMessage(
translate("A2p_BoM", "#PARTSLIST# spreadsheet has been created") + "\n")
def GetResources(self):
return {
'Pixmap': ':/icons/a2p_PartsList.svg',
'MenuText': translate("A2plus", "Create a spreadsheet with a parts list of this file"),
'ToolTip': translate(
"A2plus", "Create a spreadsheet with a " + "\n" +
"parts list of this file." + "\n\n" +
"This function will read out " + "\n" +
"the #PARTINFO# spreadsheet of " + "\n" +
"all involved parts of the " + "\n" +
"assembly and create a new " + "\n" +
"spreadsheet containing the " + "\n" +
"parts list." + "\n\n" +
"This button will open a dialog " + "\n" +
"with the Question:" + "\n" +
"- Iterate recursively over " + "\n" +
" all subassenblies?" + "\n\n" +
"Answer Yes:" + "\n" +
"All parts of all subassemblies are " + "\n" +
"collected to the partlist " + "\n\n" +
"Answer No:" + "\n" +
"Only the parts within the " + "\n" +
"recent assembly are collected.")
}
def CreateColumnHeadersInSpreadsheet(self, ss, columnHeaders, startColumn):
"""
Creates the column headers in the given spreadsheet (`ss`) starting in the column `startColumn`.
The column headers are specified in the array `columnHeaders`.
"""
# Write Column headers to spreadsheet
idx1 = ord(startColumn)
idx2 = idx1 + len(columnHeaders)
i = 0
for c in range(idx1, idx2):
ss.set(chr(c) + "1", columnHeaders[i])
i += 1
# Set the background color of the column headers
ss.setBackground('A1:' + chr(idx2 - 1) + '1',
(0.000000, 1.000000, 0.000000, 1.000000))
# Set the columnwith to proper values
ss.setColumnWidth('A', 40)
i = 0
for c in range(idx1, idx2):
ss.setColumnWidth(chr(c), 150)
i += 1
class a2p_CreateCutListOptimizerPartlist(a2p_CreatePartlist):
def clearPartList(self):
alphabet_list = list(string.ascii_uppercase)
doc = FreeCAD.activeDocument()
ss = doc.getObject(CLO_BOM_SHEET_NAME)
for i in range(0, CLO_BOM_MAX_COLS):
for k in range(0, CLO_BOM_MAX_LENGTH):
cellAdress = alphabet_list[i] + str(k + 1)
ss.set(cellAdress, '')
def Activated(self):
doc = FreeCAD.activeDocument()
if doc is None:
QtGui.QMessageBox.information(
QtGui.QApplication.activeWindow(),
translate("A2plus", "No active document found!"),
translate("A2plus", "You have to open a FCStd file first.")
)
return
completeFilePath = doc.FileName
p, f = os.path.split(completeFilePath)
flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No
msg = translate(
"A2plus", "Please save before generating a parts list! Save now?")
response = QtGui.QMessageBox.information(QtGui.QApplication.activeWindow(
), translate("A2plus", "Save document?"), msg, flags)
if response == QtGui.QMessageBox.No:
QtGui.QMessageBox.information(
QtGui.QApplication.activeWindow(),
translate("A2plus", "Parts list generation aborted!"),
translate("A2plus", "You have to save the assembly file first.")
)
return
else:
doc.save()
flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No
msg = translate(
"A2plus", "Do you want to iterate recursively over all included subassemblies?")
response = QtGui.QMessageBox.information(
QtGui.QApplication.activeWindow(), translate("A2p_BoM", "PARTSLIST"), msg, flags)
if response == QtGui.QMessageBox.Yes:
subAssyRecursion = True
else:
subAssyRecursion = False
partListEntries = createCutListOptimizerPartList(
doc.FileName,
p,
{},
recursive=subAssyRecursion
)
ss = None
try:
ss = doc.getObject(CLO_BOM_SHEET_NAME)
except:
pass
if ss is None:
ss = doc.addObject('Spreadsheet::Sheet', CLO_BOM_SHEET_NAME)
ss.Label = CLO_BOM_SHEET_LABEL
else:
self.clearPartList()
self.CreateColumnHeadersInSpreadsheet(
ss, CLO_PARTLIST_COLUMN_NAMES, 'A')
self.FillPartsListEntries(ss, partListEntries)
# recompute to finish...
doc.recompute()
FreeCAD.Console.PrintMessage(translate(
"A2p_BoM", "#PARTSLIST_CutListOptimizer# spreadsheet has been created") + "\n")
def GetResources(self):
return {
'Pixmap': ':/icons/a2p_PartsList_CutListOptimizer.svg',
'MenuText': translate("A2plus", "Create a spreadsheet with a parts list for https://cutlistoptimizer.com/ of this file"),
'ToolTip': translate(
"A2plus", "Create a spreadsheet with a " + "\n" +
"parts list https://cutlistoptimizer.com/ of this file." + "\n\n" +
"This function will read out " + "\n" +
"the #PARTINFO# spreadsheet of " + "\n" +
"all involved parts of the " + "\n" +
"assembly and create a new " + "\n" +
"spreadsheet containing the " + "\n" +
"parts list." + "\n\n" +
"This button will open a dialog " + "\n" +
"with the Question:" + "\n" +
"- Iterate recursively over " + "\n" +
" all subassenblies?" + "\n\n" +
"Answer Yes:" + "\n" +
"All parts of all subassemblies are " + "\n" +
"collected to the partlist " + "\n\n" +
"Answer No:" + "\n" +
"Only the parts within the " + "\n" +
"recent assembly are collected.")
}
def FillPartsListEntries(self, ss, partListEntries):
# [2, ['*', '*', '75.0', '45.0', '1050.0', 'LongBeam.FCStd']]
QUANTITY_IDX = 0
LENGTH_IDX = 2
WIDTH_IDX = 3
HEIGHT_IDX = 4
FILENAME_IDX = 5
for idx, k in enumerate(partListEntries.keys()):
quantity = partListEntries[k][QUANTITY_IDX]
values = partListEntries[k][1]
ss.set('A' + str(idx + 2), str(values[LENGTH_IDX]))
ss.set('B' + str(idx + 2), str(values[WIDTH_IDX]))
ss.set('C' + str(idx + 2), str(quantity))
ss.set('D' + str(idx + 2), str(values[HEIGHT_IDX]))
ss.set('E' + str(idx + 2), values[FILENAME_IDX])
ss.set('F' + str(idx + 2), "true")
FreeCADGui.addCommand('a2p_CreatePartlist', a2p_CreatePartlist())
FreeCADGui.addCommand('a2p_CreateCutListOptimizerPartlist',
a2p_CreateCutListOptimizerPartlist())
================================================
FILE: a2p_ConstraintCommands.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2019 kbwbe *
# * *
# * Portions of code based on hamish's assembly 2 *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import os
import sys
import math
import copy
from PySide import QtGui, QtCore
import FreeCAD
# from FreeCAD import Base
import FreeCADGui
import Part
from a2plib import *
import a2p_constraints
import a2p_ConstraintDialog
from a2p_viewProviderProxies import *
from a2p_solversystem import solveConstraints
translate = FreeCAD.Qt.translate
class a2p_PointIdentityConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.PointIdentityConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.PointIdentityConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_PointIdentity.svg',
'MenuText': translate("A2plus_Constraints", "Add PointIdentity constraint"),
'ToolTip': a2p_constraints.PointIdentityConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_PointIdentityConstraintCommand', a2p_PointIdentityConstraintCommand())
class a2p_PointOnLineConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.PointOnLineConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.PointOnLineConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_PointOnLineConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add PointOnLine constraint"),
'ToolTip': a2p_constraints.PointOnLineConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_PointOnLineConstraintCommand', a2p_PointOnLineConstraintCommand())
class a2p_PointOnPlaneConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.PointOnPlaneConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.PointOnPlaneConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_PointOnPlaneConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add PointOnPlane constraint"),
'ToolTip': a2p_constraints.PointOnPlaneConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_PointOnPlaneConstraintCommand', a2p_PointOnPlaneConstraintCommand())
class a2p_SphericalSurfaceConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.SphericalConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.SphericalConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_SphericalSurfaceConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add SphereCenterIdent constraint"),
'ToolTip': a2p_constraints.SphericalConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_SphericalSurfaceConstraintCommand', a2p_SphericalSurfaceConstraintCommand())
class a2p_CircularEdgeConnectionCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.CircularEdgeConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.CircularEdgeConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_CircularEdgeConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add CircularEdge constraint"),
'ToolTip': a2p_constraints.CircularEdgeConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_CircularEdgeConnection', a2p_CircularEdgeConnectionCommand())
class a2p_AxialConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.AxialConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.AxialConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_AxialConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add AxisCoincident constraint"),
'ToolTip': a2p_constraints.AxialConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_AxialConstraintCommand', a2p_AxialConstraintCommand())
class a2p_AxisParallelConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.AxisParallelConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.AxisParallelConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': ':/icons/a2p_AxisParallelConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add AxisParallel constraint"),
'ToolTip': a2p_constraints.AxisParallelConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_AxisParallelConstraintCommand', a2p_AxisParallelConstraintCommand())
class a2p_AxisPlaneParallelCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.AxisPlaneParallelConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.AxisPlaneParallelConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': ':/icons/a2p_AxisPlaneParallelConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add AxisPlaneParallel constraint"),
'ToolTip': a2p_constraints.AxisPlaneParallelConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_AxisPlaneParallelCommand', a2p_AxisPlaneParallelCommand())
class a2p_AxisPlaneAngleCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.AxisPlaneAngleConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.AxisPlaneAngleConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': ':/icons/a2p_AxisPlaneAngleConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add AxisPlaneAngle constraint"),
'ToolTip': a2p_constraints.AxisPlaneAngleConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_AxisPlaneAngleCommand', a2p_AxisPlaneAngleCommand())
class a2p_AxisPlaneNormalCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.AxisPlaneNormalConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.AxisPlaneNormalConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': ':/icons/a2p_AxisPlaneNormalConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add AxisPlaneNormal constraint"),
'ToolTip': a2p_constraints.AxisPlaneNormalConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_AxisPlaneNormalCommand', a2p_AxisPlaneNormalCommand())
class a2p_PlanesParallelConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.PlanesParallelConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.PlanesParallelConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_PlanesParallelConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add PlanesParallel constraint"),
'ToolTip': a2p_constraints.PlanesParallelConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_PlanesParallelConstraintCommand', a2p_PlanesParallelConstraintCommand())
class a2p_PlaneCoincidentConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.PlaneConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.PlaneConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_PlaneCoincidentConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add PlaneCoincident constraint"),
'ToolTip': a2p_constraints.PlaneConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_PlaneCoincidentConstraintCommand', a2p_PlaneCoincidentConstraintCommand())
class a2p_AngledPlanesConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.AngledPlanesConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.AngledPlanesConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_AngleConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add AngledPlanes constraint"),
'ToolTip': a2p_constraints.AngledPlanesConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_AngledPlanesConstraintCommand', a2p_AngledPlanesConstraintCommand())
class a2p_CenterOfMassConstraintCommand:
def Activated(self):
selection = FreeCADGui.Selection.getSelectionEx()
c = a2p_constraints.CenterOfMassConstraint(selection)
cvp = a2p_ConstraintDialog.a2p_ConstraintValuePanel(
c.constraintObject,
'createConstraint'
)
FreeCADGui.Selection.clearSelection()
def IsActive(self):
return a2p_constraints.CenterOfMassConstraint.isValidSelection(
FreeCADGui.Selection.getSelectionEx()
)
def GetResources(self):
return {
'Pixmap': path_a2p + '/icons/a2p_CenterOfMassConstraint.svg',
'MenuText': translate("A2plus_Constraints", "Add CenterOfMass constraint"),
'ToolTip': a2p_constraints.CenterOfMassConstraint.getToolTip()
}
FreeCADGui.addCommand('a2p_CenterOfMassConstraintCommand', a2p_CenterOfMassConstraintCommand())
================================================
FILE: a2p_ConstraintDialog.py
================================================
# ***************************************************************************
# * *
# * Copyright (c) 2018 kbwbe *
# * *
# * Portions of code based on hamish's assembly 2 *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import os
import sys
import math
import copy
from PySide import QtGui, QtCore
import FreeCAD
import FreeCADGui
# import Part
import a2plib
import a2p_constraints
# from a2p_viewProviderProxies import *
# from FreeCAD import Base
from a2p_translateUtils import *
# from a2plib import *
from a2p_solversystem import solveConstraints
from FreeCAD import Units
CONSTRAINT_DIALOG_STORED_POSITION = QtCore.QPoint(-1, -1)
class a2p_ConstraintValueWidget(QtGui.QWidget):
# class a2p_ConstraintValueWidget(QtGui.QDialog):
Deleted = QtCore.Signal()
Accepted = QtCore.Signal()
def __init__(self, parent, constraintObject, mode):
super(a2p_ConstraintValueWidget, self).__init__(parent=parent)
self.mode = mode # either "editConstraint" or "createConstraint"
# The documentObject of a constraint!
self.constraintObject = constraintObject
self.savedOffset = None
self.savedDirectionConstraint = None
self.savedAngle = None
self.savedLockRotation = None
if hasattr(self.constraintObject, 'offset'):
self.savedOffset = self.constraintObject.offset
if hasattr(self.constraintObject, 'directionConstraint'):
self.savedDirectionConstraint = self.constraintObject.directionConstraint
if hasattr(self.constraintObject, 'angle'):
self.savedAngle = self.constraintObject.angle
if hasattr(self.constraintObject, 'lockRotation'):
self.savedLockRotation = self.constraintObject.lockRotation
self.winModified = False
self.lineNo = 0
self.neededHight = 0
self.isTopLevelWin = True # Window management
self.position = None # Window position
self.recentUnit = "mm"
self.initUI()
def initUI(self):
# self.setMinimumHeight(self.minHeight)
self.setWindowTitle(translate("A2plus", "Constraint properties"))
# self.resize(300, 600)
self.mainLayout = QtGui.QGridLayout() # a VBoxLayout for the whole form
lbl1 = QtGui.QLabel(self)
lbl1.setText(self.constraintObject.Label)
lbl1.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken)
self.mainLayout.addWidget(lbl1, self.lineNo, 0, 1, 4)
self.lineNo += 1
if hasattr(self.constraintObject, "directionConstraint"):
lbl3 = QtGui.QLabel(self)
lbl3.setText(translate("A2plus", "Direction"))
lbl3.setFixedHeight(32)
self.mainLayout.addWidget(lbl3, self.lineNo, 0)
# create items list for QComboBox
self.directionCombo = QtGui.QComboBox(self)
self.directionCombo.insertItem(0, translate("A2plus", "aligned"))
self.directionCombo.insertItem(1, translate("A2plus", "opposed"))
d = self.constraintObject.directionConstraint # not every constraint has a direction
# for compat with old A2plus assemblies
if d == "none":
self.directionCombo.insertItem(2, translate("A2plus", "none"))
# activate item of list
if d == "aligned":
self.directionCombo.setCurrentIndex(0)
elif d == "opposed":
self.directionCombo.setCurrentIndex(1)
elif d == "none": # will only occur with old A2plus assemblies
self.directionCombo.setCurrentIndex(2)
self.directionCombo.setFixedHeight(32)
self.directionCombo.currentIndexChanged[int].connect(self.flipDirection2)
self.mainLayout.addWidget(self.directionCombo, self.lineNo, 1)
self.flipDirectionButton = QtGui.QPushButton(self)
self.flipDirectionButton.setIcon(QtGui.QIcon(':/icons/a2p_FlipConstraint.svg'))
self.flipDirectionButton.setToolTip(translate("A2plus", "Flip direction between 'aligned' and 'opposed'"))
self.flipDirectionButton.setText(translate("A2plus", "Flip direction"))
self.flipDirectionButton.setFixedHeight(32)
QtCore.QObject.connect(self.flipDirectionButton, QtCore.SIGNAL("clicked()"), self.flipDirection)
self.mainLayout.addWidget(self.flipDirectionButton, self.lineNo, 2)
self.lineNo += 1
if hasattr(self.constraintObject, "offset"):
offs = self.constraintObject.offset
lbl4 = QtGui.QLabel(self)
lbl4.setText(translate("A2plus", "Offset"))
lbl4.setFixedHeight(32)
self.mainLayout.addWidget(lbl4, self.lineNo, 0)
self.offsetEdit = QtGui.QDoubleSpinBox(self)
# get the length unit as string
self.offsetEdit.setSuffix(" " + str(FreeCAD.Units.Quantity(1, FreeCAD.Units.Length))[3:])
# the maximum is by default 99.99 and we can allow more
self.offsetEdit.setMaximum(1e7) # allow up to 1 km
# set minimum to negative of maximum
self.offsetEdit.setMinimum(-1*self.offsetEdit.maximum())
# use the number of decimals defined by thew user in FC
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units")
self.offsetEdit.setDecimals(params.GetInt('Decimals'))
userPreferred = Units.Quantity(offs).getUserPreferred()[0] # offs is string with value and unit
# the following line does not work with all possible units.
# sometimes a separating blank is missing
# user_qty, user_unit = userPreferred.split(' ')
# so do own parsing
user_qty = ''
user_unit = ''
for c in userPreferred:
if c == ' ':
continue
elif c == '-':
if len(user_qty) == 0:
user_qty += c
elif c.isdigit():
user_qty += c
elif c in ('.', ','):
user_qty += c
else:
user_unit += c
user_qty = float(user_qty.replace(",", "."))
# self.offsetEdit.setValue(offs.Value)
# self.offsetEdit.setSuffix(" " + str(FreeCAD.Units.Quantity(1, FreeCAD.Units.Length))[3:])
self.offsetEdit.setSuffix(" " + user_unit)
self.offsetEdit.setValue(user_qty)
self.recentUnit = user_unit
self.offsetEdit.setFixedHeight(32)
QtCore.QObject.connect(self.offsetEdit, QtCore.SIGNAL("valueChanged(double)"), self.handleOffsetChanged)
self.mainLayout.addWidget(self.offsetEdit, self.lineNo, 1)
self.offsetSetZeroButton = QtGui.QPushButton(self)
self.offsetSetZeroButton.setToolTip(translate("A2plus", "Set 0 to 'Offset' field"))
self.offsetSetZeroButton.setText(translate("A2plus", "Set Zero"))
self.offsetSetZeroButton.setFixedHeight(32)
QtCore.QObject.connect(self.offsetSetZeroButton, QtCore.SIGNAL("clicked()"), self.setOffsetZero)
self.mainLayout.addWidget(self.offsetSetZeroButton, self.lineNo, 2)
self.flipOffsetSignButton = QtGui.QPushButton(self)
self.flipOffsetSignButton.setToolTip(
translate("A2plus", "Flip sign between '+' and '-' in 'Offset' field")
)
self.flipOffsetSignButton.setText(translate("A2plus", "Flip sign"))
self.flipOffsetSignButton.setFixedHeight(32)
QtCore.QObject.connect(self.flipOffsetSignButton, QtCore.SIGNAL("clicked()"), self.flipOffsetSign)
self.mainLayout.addWidget(self.flipOffsetSignButton, self.lineNo, 3)
gitextract_7vrix2pg/
├── .gitignore
├── A2plus.qrc
├── CD_A2plusupdater.py
├── CD_CheckConstraints.py
├── CD_ConstraintViewer.py
├── CD_FeatureLabels.py
├── CD_OneButton.py
├── GuiA2p/
│ └── Resources/
│ ├── compile_resources_pack.py
│ ├── resources-a2p.rcc
│ ├── resources.rcc
│ └── ui/
│ └── a2p_prefs.ui
├── Init.py
├── InitGui.py
├── LICENSE
├── README.md
├── __init__.py
├── a2p_BoM.py
├── a2p_ConstraintCommands.py
├── a2p_ConstraintDialog.py
├── a2p_MuxAssembly.py
├── a2p_Resources3.py
├── a2p_Resources3_Qt6.py
├── a2p_constraintServices.py
├── a2p_constraints.py
├── a2p_convertPart.py
├── a2p_dependencies.py
├── a2p_fcdocumentreader.py
├── a2p_importedPart_class.py
├── a2p_importpart.py
├── a2p_lcs_support.py
├── a2p_libDOF.py
├── a2p_observers.py
├── a2p_partinformation.py
├── a2p_partlistglobals.py
├── a2p_recursiveUpdatePlanner.py
├── a2p_rigid.py
├── a2p_searchConstraintConflicts.py
├── a2p_simpleXMLreader.py
├── a2p_solversystem.py
├── a2p_topomapper.py
├── a2p_translateUtils.py
├── a2p_viewProviderProxies.py
├── a2plib.py
├── compileA2pResources.py
├── crowdin.yml
├── package.xml
└── translations/
├── A2plus.ts
├── A2plus_de.qm
├── A2plus_de.ts
├── A2plus_es-AR.qm
├── A2plus_es-AR.ts
├── A2plus_es-ES.qm
├── A2plus_es-ES.ts
├── A2plus_fr.qm
├── A2plus_fr.ts
├── A2plus_it.qm
├── A2plus_it.ts
├── A2plus_pt-br.qm
├── A2plus_pt-br.ts
├── A2plus_pt-pt.qm
├── A2plus_pt-pt.ts
├── A2plus_ru.qm
├── A2plus_ru.ts
├── A2plus_zh-CN.qm
├── A2plus_zh-CN.ts
├── README.md
└── update_ts.py
SYMBOL INDEX (977 symbols across 33 files)
FILE: CD_A2plusupdater.py
class globaluseclass (line 44) | class globaluseclass:
method __init__ (line 46) | def __init__(self):
method resetvars (line 60) | def resetvars(self):
class sideFuncs1 (line 76) | class sideFuncs1():
method __init__ (line 77) | def __init__(self):
method opendoccheck (line 80) | def opendoccheck(self):
class classFuncs (line 97) | class classFuncs():
method __init__ (line 98) | def __init__(self):
method selectfiles (line 101) | def selectfiles(self):
method firstrun (line 133) | def firstrun(self, partobj):
method secondrun (line 144) | def secondrun(self, newpart=False):
method runpostchange (line 162) | def runpostchange(self):
method getfeatstomove (line 185) | def getfeatstomove(self):
method getfacebynum (line 241) | def getfacebynum(self, facenum, shape):
method getedgebynum (line 275) | def getedgebynum(self, num, shape):
method getvertexbynum (line 323) | def getvertexbynum(self, num, shape):
method findfeats_attempt1 (line 333) | def findfeats_attempt1(self):
method swapfeature (line 361) | def swapfeature(self, newfeat, d):
method findfeats_attempt2 (line 386) | def findfeats_attempt2(self):
method findnewface_attempt1 (line 410) | def findnewface_attempt1(self, d):
method findnewface_attempt2 (line 424) | def findnewface_attempt2(self, dict_):
method findCylinderattempt1 (line 473) | def findCylinderattempt1(self, dict_):
method findCylinderattempt2 (line 483) | def findCylinderattempt2(self, dict_):
method findnewedge_attempt1 (line 495) | def findnewedge_attempt1(self, dict_):
method findnewedge_attempt2 (line 512) | def findnewedge_attempt2(self, dict_):
method findnewvertex_attempt1 (line 531) | def findnewvertex_attempt1(self, dict_):
function getfacelists (line 543) | def getfacelists():
function selectforpart (line 553) | def selectforpart(partlabel, selectType='std'):
function rondnum (line 574) | def rondnum(num, rndto=g.roundto, mmorin='mm'):
function rondlist (line 583) | def rondlist(inpList, inch=False):
class mApp (line 597) | class mApp(QtGui.QWidget):
method __init__ (line 599) | def __init__(self, msg, msgtype='ok'):
method initUI (line 603) | def initUI(self, msg, msgtype='ok'):
class formReport (line 615) | class formReport(QtGui.QDialog):
method __init__ (line 617) | def __init__(self, name):
method showme (line 635) | def showme(self, msg):
method Closeme (line 639) | def Closeme(self):
method closeEvent (line 642) | def closeEvent(self, event):
class rnp_Update_A2pParts (line 649) | class rnp_Update_A2pParts:
method Activated (line 650) | def Activated(self):
method Deactivated (line 654) | def Deactivated(self):
method GetResources (line 659) | def GetResources(self):
FILE: CD_CheckConstraints.py
class globaluseclass (line 38) | class globaluseclass:
method __init__ (line 39) | def __init__(self):
class mApp (line 52) | class mApp(QtGui.QWidget):
method __init__ (line 55) | def __init__(self, msg):
method initUI (line 60) | def initUI(self, msg, msgtype='ok'):
class formMain (line 78) | class formMain(QtGui.QMainWindow):
method __init__ (line 80) | def __init__(self, name):
method openViewer (line 114) | def openViewer(self):
method resizeEvent (line 125) | def resizeEvent(self, event):
method showme (line 131) | def showme(self, msg):
method Closeme (line 135) | def Closeme(self):
method closeEvent (line 138) | def closeEvent(self, event):
class classCheckConstraints (line 146) | class classCheckConstraints():
method __init__ (line 147) | def __init__(self):
method startcheck (line 153) | def startcheck(self):
method checkformovement (line 191) | def checkformovement(self, constraintlist, putPartBack=True):
method addError (line 276) | def addError(self, cobj, errortype):
method getallconstraints (line 279) | def getallconstraints(self):
class formReport (line 292) | class formReport(QtGui.QDialog):
method __init__ (line 294) | def __init__(self, name):
method showme (line 302) | def showme(self, msg):
method Closeme (line 306) | def Closeme(self):
method closeEvent (line 309) | def closeEvent(self, event):
function rondlist (line 316) | def rondlist(list, inch=False):
function rondnum (line 330) | def rondnum(num, mmorin='mm'):
class rnp_Constraint_Checker (line 339) | class rnp_Constraint_Checker:
method Activated (line 341) | def Activated(self):
method onDeleteConstraint (line 344) | def onDeleteConstraint(self):
method Deactivated (line 347) | def Deactivated():
method GetResources (line 351) | def GetResources(self):
FILE: CD_ConstraintViewer.py
class globaluseclass (line 47) | class globaluseclass:
method __init__ (line 48) | def __init__(self):
class mApp (line 57) | class mApp(QtGui.QWidget):
method __init__ (line 60) | def __init__(self, msg, msgtype='ok'):
method initUI (line 64) | def initUI(self, msg, msgtype='ok'):
class ShowPartProperties (line 91) | class ShowPartProperties(QtGui.QWidget):
method __init__ (line 93) | def __init__(self):
method drt (line 98) | def drt(self):
method createButtonColumn (line 173) | def createButtonColumn(self, xloc, btnLabels):
method button_pushed (line 184) | def button_pushed(self):
method clearTable (line 239) | def clearTable(self):
method process_menus (line 242) | def process_menus(self, q):
method process_misc_menus (line 263) | def process_misc_menus(self, q):
method stdSolve (line 268) | def stdSolve(self):
method fun4 (line 272) | def fun4(self, Ncol):
method fun (line 275) | def fun(self, i):
method loadtable (line 279) | def loadtable(self, TempList):
method hoveronoff (line 364) | def hoveronoff(self, val):
method cell_was_clicked (line 367) | def cell_was_clicked(self, row, column):
method showme (line 422) | def showme(self):
method Closeme (line 430) | def Closeme(self):
method closeEvent (line 435) | def closeEvent(self, event):
method resizeEvent (line 440) | def resizeEvent(self, event):
class classconflictreport (line 450) | class classconflictreport():
method __init__ (line 451) | def __init__(self):
method selectforTree (line 454) | def selectforTree(self):
method selectforpart (line 475) | def selectforpart(self):
method checkforfixandsolve (line 509) | def checkforfixandsolve(self, constraintlist):
class classsidefunctions (line 542) | class classsidefunctions():
method __init__ (line 543) | def __init__(self, name):
method swapselectedleg (line 547) | def swapselectedleg(self):
method turnoffobserv (line 557) | def turnoffobserv(self):
method swap1leg (line 562) | def swap1leg(self):
method swapfeature (line 591) | def swapfeature(self, newfeaturedict):
class SelObserver (line 616) | class SelObserver:
method __init__ (line 617) | def __init__(self):
method SelObserverON (line 620) | def SelObserverON(self):
method SelObserverOFF (line 623) | def SelObserverOFF(self):
method setPreselection (line 630) | def setPreselection(self, doc, obj, sub): # Preselection object
method addSelection (line 633) | def addSelection(self, doc, obj, sub, pnt): # Selection object
method removeSelection (line 636) | def removeSelection(self, doc, obj, sub): # Delete the selected object
method setSelection (line 639) | def setSelection(self, doc):
class classsearch (line 648) | class classsearch():
method __init__ (line 650) | def __init__(self):
method startsearch (line 653) | def startsearch(self, searchterm, colnum):
method searchTreeItem (line 661) | def searchTreeItem(self, tree, item, searchterm, colnum):
method expandParent (line 669) | def expandParent(self, tree, item):
method resetAll (line 675) | def resetAll(self, item):
method reset1 (line 682) | def reset1(self):
function rondlist (line 694) | def rondlist(inputList, inch=False):
function rondnum (line 708) | def rondnum(num, mmorin='mm'):
class classlastclickeditem (line 717) | class classlastclickeditem:
method __init__ (line 718) | def __init__(self, Name):
method clear (line 728) | def clear(self):
method cellpicked (line 736) | def cellpicked(self, row, column):
class rnp_Constraint_Viewer (line 759) | class rnp_Constraint_Viewer:
method Activated (line 761) | def Activated(self):
method Deactivated (line 764) | def Deactivated(self):
method GetResources (line 768) | def GetResources(self):
FILE: CD_FeatureLabels.py
class formMain (line 32) | class formMain(QtGui.QMainWindow):
method __init__ (line 33) | def __init__(self, name):
method button_pushed (line 60) | def button_pushed(self):
method hideMe (line 81) | def hideMe(self):
method showme (line 85) | def showme(self):
method Closeme (line 88) | def Closeme(self):
method closeEvent (line 91) | def closeEvent(self, event):
class classLabels (line 99) | class classLabels():
method __init__ (line 100) | def __init__(self):
method checkselection (line 103) | def checkselection(self):
method addlabels (line 114) | def addlabels(self, feat):
method makelabel (line 135) | def makelabel(self, ent, name, loc):
method deletelabels (line 145) | def deletelabels(self):
method attachto (line 150) | def attachto(self, sel=None, featname=''):
method getEntLoc (line 164) | def getEntLoc(self, ent, featname):
method labelForTable (line 173) | def labelForTable(self, ent, featname):
method selectedlabels (line 178) | def selectedlabels(self):
class mApp (line 192) | class mApp(QtGui.QWidget):
method __init__ (line 195) | def __init__(self, msg):
method initUI (line 199) | def initUI(self, msg):
FILE: CD_OneButton.py
class globaluseclass (line 37) | class globaluseclass:
method __init__ (line 38) | def __init__(self, name):
class onebutton (line 49) | class onebutton:
method readselect (line 50) | def readselect(self, doc, obj, sub):
class SelObserver (line 80) | class SelObserver:
method __init__ (line 81) | def __init__(self):
method SelObserverON (line 84) | def SelObserverON(self):
method SelObserverOFF (line 90) | def SelObserverOFF(self):
method addSelection (line 98) | def addSelection(self, doc, obj, sub, pnt): # Selection object
method removeSelection (line 101) | def removeSelection(self, doc, obj, sub): # Delete the selected object
method setSelection (line 104) | def setSelection(self, doc):
class ViewObserver (line 112) | class ViewObserver:
method __init__ (line 113) | def __init__(self):
method vostart (line 118) | def vostart(self):
method vooff (line 123) | def vooff(self):
method logPosition (line 129) | def logPosition(self, myinfo):
class rnp_OneButton (line 150) | class rnp_OneButton:
method GetResources (line 151) | def GetResources(self):
method Activated (line 160) | def Activated(self, placeholder=None):
method Deactivated (line 176) | def Deactivated(self):
method IsChecked (line 181) | def IsChecked(self):
method IsActive (line 184) | def IsActive(self):
class mApp (line 191) | class mApp(QtGui.QWidget):
method __init__ (line 194) | def __init__(self, msg, msgtype='ok'):
method initUI (line 203) | def initUI(self, msg):
FILE: InitGui.py
class A2plusWorkbench (line 47) | class A2plusWorkbench (Workbench):
method __init__ (line 49) | def __init__(self):
method Initialize (line 56) | def Initialize(self):
method Activated (line 238) | def Activated(self):
method Deactivated (line 242) | def Deactivated(self):
method ContextMenu (line 246) | def ContextMenu(self, recipient):
FILE: a2p_BoM.py
function createCutListOptimizerPartList (line 53) | def createCutListOptimizerPartList(
function createPartList (line 168) | def createPartList(
class a2p_CreatePartlist (line 264) | class a2p_CreatePartlist():
method clearPartList (line 266) | def clearPartList(self):
method Activated (line 275) | def Activated(self):
method GetResources (line 349) | def GetResources(self):
method CreateColumnHeadersInSpreadsheet (line 374) | def CreateColumnHeadersInSpreadsheet(self, ss, columnHeaders, startCol...
class a2p_CreateCutListOptimizerPartlist (line 397) | class a2p_CreateCutListOptimizerPartlist(a2p_CreatePartlist):
method clearPartList (line 399) | def clearPartList(self):
method Activated (line 408) | def Activated(self):
method GetResources (line 473) | def GetResources(self):
method FillPartsListEntries (line 498) | def FillPartsListEntries(self, ss, partListEntries):
FILE: a2p_ConstraintCommands.py
class a2p_PointIdentityConstraintCommand (line 46) | class a2p_PointIdentityConstraintCommand:
method Activated (line 47) | def Activated(self):
method IsActive (line 56) | def IsActive(self):
method GetResources (line 61) | def GetResources(self):
class a2p_PointOnLineConstraintCommand (line 72) | class a2p_PointOnLineConstraintCommand:
method Activated (line 73) | def Activated(self):
method IsActive (line 82) | def IsActive(self):
method GetResources (line 87) | def GetResources(self):
class a2p_PointOnPlaneConstraintCommand (line 98) | class a2p_PointOnPlaneConstraintCommand:
method Activated (line 99) | def Activated(self):
method IsActive (line 108) | def IsActive(self):
method GetResources (line 113) | def GetResources(self):
class a2p_SphericalSurfaceConstraintCommand (line 124) | class a2p_SphericalSurfaceConstraintCommand:
method Activated (line 125) | def Activated(self):
method IsActive (line 134) | def IsActive(self):
method GetResources (line 139) | def GetResources(self):
class a2p_CircularEdgeConnectionCommand (line 150) | class a2p_CircularEdgeConnectionCommand:
method Activated (line 151) | def Activated(self):
method IsActive (line 160) | def IsActive(self):
method GetResources (line 165) | def GetResources(self):
class a2p_AxialConstraintCommand (line 176) | class a2p_AxialConstraintCommand:
method Activated (line 177) | def Activated(self):
method IsActive (line 186) | def IsActive(self):
method GetResources (line 191) | def GetResources(self):
class a2p_AxisParallelConstraintCommand (line 202) | class a2p_AxisParallelConstraintCommand:
method Activated (line 203) | def Activated(self):
method IsActive (line 212) | def IsActive(self):
method GetResources (line 217) | def GetResources(self):
class a2p_AxisPlaneParallelCommand (line 228) | class a2p_AxisPlaneParallelCommand:
method Activated (line 229) | def Activated(self):
method IsActive (line 238) | def IsActive(self):
method GetResources (line 243) | def GetResources(self):
class a2p_AxisPlaneAngleCommand (line 254) | class a2p_AxisPlaneAngleCommand:
method Activated (line 255) | def Activated(self):
method IsActive (line 264) | def IsActive(self):
method GetResources (line 269) | def GetResources(self):
class a2p_AxisPlaneNormalCommand (line 280) | class a2p_AxisPlaneNormalCommand:
method Activated (line 281) | def Activated(self):
method IsActive (line 290) | def IsActive(self):
method GetResources (line 295) | def GetResources(self):
class a2p_PlanesParallelConstraintCommand (line 306) | class a2p_PlanesParallelConstraintCommand:
method Activated (line 307) | def Activated(self):
method IsActive (line 316) | def IsActive(self):
method GetResources (line 321) | def GetResources(self):
class a2p_PlaneCoincidentConstraintCommand (line 332) | class a2p_PlaneCoincidentConstraintCommand:
method Activated (line 333) | def Activated(self):
method IsActive (line 342) | def IsActive(self):
method GetResources (line 347) | def GetResources(self):
class a2p_AngledPlanesConstraintCommand (line 358) | class a2p_AngledPlanesConstraintCommand:
method Activated (line 359) | def Activated(self):
method IsActive (line 368) | def IsActive(self):
method GetResources (line 373) | def GetResources(self):
class a2p_CenterOfMassConstraintCommand (line 384) | class a2p_CenterOfMassConstraintCommand:
method Activated (line 385) | def Activated(self):
method IsActive (line 394) | def IsActive(self):
method GetResources (line 399) | def GetResources(self):
FILE: a2p_ConstraintDialog.py
class a2p_ConstraintValueWidget (line 50) | class a2p_ConstraintValueWidget(QtGui.QWidget):
method __init__ (line 56) | def __init__(self, parent, constraintObject, mode):
method initUI (line 83) | def initUI(self):
method setConstraintEditorData (line 315) | def setConstraintEditorData(self):
method solve (line 337) | def solve(self):
method flipLockRotation (line 356) | def flipLockRotation(self):
method handleOffsetChanged (line 363) | def handleOffsetChanged(self):
method setOffsetZero (line 369) | def setOffsetZero(self):
method flipOffsetSign (line 375) | def flipOffsetSign(self):
method flipDirection2 (line 388) | def flipDirection2(self, idx):
method flipDirection (line 393) | def flipDirection(self):
method handleAngleChanged (line 402) | def handleAngleChanged(self):
method roundAngle (line 408) | def roundAngle(self):
method perpendicularAngle (line 418) | def perpendicularAngle(self):
method restoreConstraintValues (line 444) | def restoreConstraintValues(self):
method delete (line 454) | def delete(self):
method keyPressEvent (line 481) | def keyPressEvent(self, e):
method accept (line 487) | def accept(self):
method cancelOperation (line 502) | def cancelOperation(self):
class a2p_ConstraintCollection (line 549) | class a2p_ConstraintCollection(QtGui.QWidget):
method __init__ (line 550) | def __init__(self, parent):
method initUI (line 557) | def initUI(self):
method showConstraintCollectionHelp (line 742) | def showConstraintCollectionHelp(self):
method parseSelections (line 754) | def parseSelections(self):
method onTimer (line 798) | def onTimer(self):
method manageConstraint (line 802) | def manageConstraint(self):
method onAcceptConstraint (line 813) | def onAcceptConstraint(self):
method onDeleteConstraint (line 820) | def onDeleteConstraint(self):
method onPointIdentityButton (line 826) | def onPointIdentityButton(self):
method onPointOnLineButton (line 831) | def onPointOnLineButton(self):
method onPointOnPlaneButton (line 836) | def onPointOnPlaneButton(self):
method onCircularEdgeButton (line 841) | def onCircularEdgeButton(self):
method onAxialButton (line 846) | def onAxialButton(self):
method onAxisParallelButton (line 851) | def onAxisParallelButton(self):
method onAxisPlaneParallelButton (line 856) | def onAxisPlaneParallelButton(self):
method onAxisPlaneAngleButton (line 861) | def onAxisPlaneAngleButton(self):
method onAxisPlaneNormalButton (line 866) | def onAxisPlaneNormalButton(self):
method onPlanesParallelButton (line 871) | def onPlanesParallelButton(self):
method onPlaneCoincidentButton (line 876) | def onPlaneCoincidentButton(self):
method onAngledPlanesButton (line 881) | def onAngledPlanesButton(self):
method onCenterOfMassButton (line 886) | def onCenterOfMassButton(self):
method onSpericalConstraintButton (line 891) | def onSpericalConstraintButton(self):
function getMoveDistToStoredPosition (line 897) | def getMoveDistToStoredPosition(widg):
class a2p_ConstraintValuePanel (line 926) | class a2p_ConstraintValuePanel(QtGui.QDockWidget):
method __init__ (line 931) | def __init__(self, constraintObject, mode):
method storeWindowPosition (line 967) | def storeWindowPosition(self):
method onAcceptConstraint (line 978) | def onAcceptConstraint(self):
method onDeleteConstraint (line 985) | def onDeleteConstraint(self):
method closeEvent (line 992) | def closeEvent(self, event):
class a2p_ConstraintPanel (line 997) | class a2p_ConstraintPanel(QtGui.QDockWidget):
method __init__ (line 998) | def __init__(self):
method onTimer (line 1019) | def onTimer(self):
method closeEvent (line 1038) | def closeEvent(self, event):
class a2p_ConstraintDialogCommand (line 1044) | class a2p_ConstraintDialogCommand:
method Activated (line 1046) | def Activated(self):
method IsActive (line 1051) | def IsActive(self):
method GetResources (line 1058) | def GetResources(self):
class a2p_EditConstraintCommand (line 1070) | class a2p_EditConstraintCommand:
method Activated (line 1072) | def Activated(self):
method IsActive (line 1090) | def IsActive(self):
method onAcceptConstraint (line 1095) | def onAcceptConstraint(self):
method onDeleteConstraint (line 1100) | def onDeleteConstraint(self):
method GetResources (line 1105) | def GetResources(self):
FILE: a2p_MuxAssembly.py
function createTopoInfo (line 43) | def createTopoInfo(obj): # used during converting an object to a2p object
function makePlacedShape (line 61) | def makePlacedShape(obj):
function muxAssemblyWithTopoNames (line 73) | def muxAssemblyWithTopoNames(doc, desiredShapeLabel=None):
class SimpleAssemblyShape (line 194) | class SimpleAssemblyShape:
method __init__ (line 195) | def __init__(self, obj):
method onChanged (line 200) | def onChanged(self, fp, prop):
method execute (line 203) | def execute(self, fp):
class ViewProviderSimpleAssemblyShape (line 207) | class ViewProviderSimpleAssemblyShape:
method __init__ (line 208) | def __init__(self, obj):
method onDelete (line 211) | def onDelete(self, viewObject, subelements):
method __getstate__ (line 214) | def __getstate__(self):
method __setstate__ (line 217) | def __setstate__(self, state):
method dumps (line 220) | def dumps(self):
method loads (line 223) | def loads(self, state):
method getIcon (line 226) | def getIcon(self):
method attach (line 229) | def attach(self, obj):
method getDisplayModes (line 235) | def getDisplayModes(self, obj):
method getDefaultDisplayMode (line 243) | def getDefaultDisplayMode(self):
method setDisplayMode (line 247) | def setDisplayMode(self, mode):
function createOrUpdateSimpleAssemblyShape (line 268) | def createOrUpdateSimpleAssemblyShape(doc):
class a2p_SimpleAssemblyShapeCommand (line 322) | class a2p_SimpleAssemblyShapeCommand():
method GetResources (line 324) | def GetResources(self):
method Activated (line 331) | def Activated(self):
method IsActive (line 342) | def IsActive(self):
FILE: a2p_Resources3.py
function qInitResources (line 24412) | def qInitResources():
function qCleanupResources (line 24415) | def qCleanupResources():
FILE: a2p_Resources3_Qt6.py
function qInitResources (line 24412) | def qInitResources():
function qCleanupResources (line 24415) | def qCleanupResources():
FILE: a2p_constraintServices.py
function reAdjustConstraintDirections (line 35) | def reAdjustConstraintDirections(doc):
class a2p_reAdjustConstraintDirectionsCommand (line 105) | class a2p_reAdjustConstraintDirectionsCommand:
method Activated (line 106) | def Activated(self):
method IsActive (line 120) | def IsActive(self):
method GetResources (line 125) | def GetResources(self):
FILE: a2p_constraints.py
class BasicConstraint (line 48) | class BasicConstraint():
method __init__ (line 52) | def __init__(self, selection):
method create (line 74) | def create(self, selection):
method setupProxies (line 109) | def setupProxies(self):
method groupUnderParentTreeObject (line 120) | def groupUnderParentTreeObject(self):
method setInitialValues (line 128) | def setInitialValues(self):
method calcInitialValues (line 145) | def calcInitialValues(self):
method recalculateMatingDirection (line 153) | def recalculateMatingDirection(c):
method getToolTip (line 161) | def getToolTip(self):
method isValidSelection (line 165) | def isValidSelection(selection):
class PointIdentityConstraint (line 169) | class PointIdentityConstraint(BasicConstraint):
method __init__ (line 170) | def __init__(self, selection):
method calcInitialValues (line 177) | def calcInitialValues(self):
method recalculateMatingDirection (line 181) | def recalculateMatingDirection(c):
method getToolTip (line 185) | def getToolTip():
method isValidSelection (line 200) | def isValidSelection(selection):
class PointOnLineConstraint (line 219) | class PointOnLineConstraint(BasicConstraint):
method __init__ (line 220) | def __init__(self, selection):
method calcInitialValues (line 227) | def calcInitialValues(self):
method recalculateMatingDirection (line 231) | def recalculateMatingDirection(c):
method getToolTip (line 235) | def getToolTip():
method isValidSelection (line 250) | def isValidSelection(selection):
class PointOnPlaneConstraint (line 263) | class PointOnPlaneConstraint(BasicConstraint):
method __init__ (line 264) | def __init__(self, selection):
method calcInitialValues (line 271) | def calcInitialValues(self):
method recalculateMatingDirection (line 275) | def recalculateMatingDirection(c):
method getToolTip (line 290) | def getToolTip():
method isValidSelection (line 303) | def isValidSelection(selection):
class SphericalConstraint (line 319) | class SphericalConstraint(BasicConstraint):
method __init__ (line 320) | def __init__(self, selection):
method calcInitialValues (line 327) | def calcInitialValues(self):
method recalculateMatingDirection (line 331) | def recalculateMatingDirection(c):
method getToolTip (line 335) | def getToolTip():
method isValidSelection (line 350) | def isValidSelection(selection):
class CircularEdgeConstraint (line 369) | class CircularEdgeConstraint(BasicConstraint):
method __init__ (line 370) | def __init__(self, selection):
method calcInitialValues (line 377) | def calcInitialValues(self):
method recalculateMatingDirection (line 395) | def recalculateMatingDirection(c):
method getToolTip (line 416) | def getToolTip():
method isValidSelection (line 431) | def isValidSelection(selection):
class AxialConstraint (line 441) | class AxialConstraint(BasicConstraint):
method __init__ (line 442) | def __init__(self, selection):
method calcInitialValues (line 449) | def calcInitialValues(self):
method recalculateMatingDirection (line 461) | def recalculateMatingDirection(c):
method getToolTip (line 473) | def getToolTip():
method isValidSelection (line 487) | def isValidSelection(selection):
class AxisParallelConstraint (line 503) | class AxisParallelConstraint(BasicConstraint):
method __init__ (line 504) | def __init__(self, selection):
method calcInitialValues (line 511) | def calcInitialValues(self):
method recalculateMatingDirection (line 522) | def recalculateMatingDirection(c):
method getToolTip (line 534) | def getToolTip():
method isValidSelection (line 551) | def isValidSelection(selection):
class AxisPlaneParallelConstraint (line 564) | class AxisPlaneParallelConstraint(BasicConstraint):
method __init__ (line 565) | def __init__(self, selection):
method calcInitialValues (line 572) | def calcInitialValues(self):
method recalculateMatingDirection (line 576) | def recalculateMatingDirection(c):
method getToolTip (line 580) | def getToolTip():
method isValidSelection (line 595) | def isValidSelection(selection):
class AxisPlaneAngleConstraint (line 608) | class AxisPlaneAngleConstraint(BasicConstraint):
method __init__ (line 609) | def __init__(self, selection):
method calcInitialValues (line 616) | def calcInitialValues(self):
method recalculateMatingDirection (line 634) | def recalculateMatingDirection(c):
method getToolTip (line 648) | def getToolTip():
method isValidSelection (line 665) | def isValidSelection(selection):
class AxisPlaneNormalConstraint (line 678) | class AxisPlaneNormalConstraint(BasicConstraint):
method __init__ (line 679) | def __init__(self, selection):
method calcInitialValues (line 686) | def calcInitialValues(self):
method recalculateMatingDirection (line 699) | def recalculateMatingDirection(c):
method getToolTip (line 713) | def getToolTip():
method isValidSelection (line 729) | def isValidSelection(selection):
class PlanesParallelConstraint (line 742) | class PlanesParallelConstraint(BasicConstraint):
method __init__ (line 743) | def __init__(self, selection):
method calcInitialValues (line 750) | def calcInitialValues(self):
method recalculateMatingDirection (line 765) | def recalculateMatingDirection(c):
method getToolTip (line 781) | def getToolTip():
method isValidSelection (line 796) | def isValidSelection(selection):
class PlaneConstraint (line 808) | class PlaneConstraint(BasicConstraint):
method __init__ (line 809) | def __init__(self, selection):
method calcInitialValues (line 816) | def calcInitialValues(self):
method recalculateMatingDirection (line 832) | def recalculateMatingDirection(c):
method getToolTip (line 851) | def getToolTip():
method isValidSelection (line 865) | def isValidSelection(selection):
class AngledPlanesConstraint (line 877) | class AngledPlanesConstraint(BasicConstraint):
method __init__ (line 878) | def __init__(self, selection):
method calcInitialValues (line 885) | def calcInitialValues(self):
method recalculateMatingDirection (line 893) | def recalculateMatingDirection(c):
method getToolTip (line 897) | def getToolTip():
method isValidSelection (line 917) | def isValidSelection(selection):
class CenterOfMassConstraint (line 927) | class CenterOfMassConstraint(BasicConstraint):
method __init__ (line 928) | def __init__(self, selection):
method calcInitialValues (line 935) | def calcInitialValues(self):
method recalculateMatingDirection (line 957) | def recalculateMatingDirection(c):
method getToolTip (line 978) | def getToolTip():
method isValidSelection (line 992) | def isValidSelection(selection):
FILE: a2p_convertPart.py
function updateConvertedPart (line 41) | def updateConvertedPart(doc, obj):
function convertToImportedPart (line 76) | def convertToImportedPart(doc, obj):
class a2p_ConvertPartCommand (line 158) | class a2p_ConvertPartCommand():
method GetResources (line 160) | def GetResources(self):
method Activated (line 167) | def Activated(self):
method IsActive (line 184) | def IsActive(self):
FILE: a2p_dependencies.py
class Dependency (line 49) | class Dependency():
method __init__ (line 50) | def __init__(self, constraint, refType, axisRotation):
method clear (line 87) | def clear(self):
method __str__ (line 105) | def __str__(self):
method Create (line 113) | def Create(doc, constraint, solver, rigid1, rigid2):
method applyPlacement (line 391) | def applyPlacement(self, placement):
method enable (line 397) | def enable(self, workList):
method disable (line 403) | def disable(self):
method getMovement (line 407) | def getMovement(self):
method calcDOF (line 410) | def calcDOF(self, _dofRot, _dofPos, _pointconstraints = []):
method getRotation (line 414) | def getRotation(self, solver):
class DependencyPointIdentity (line 471) | class DependencyPointIdentity(Dependency):
method __init__ (line 472) | def __init__(self, constraint, refType):
method getMovement (line 477) | def getMovement(self):
method calcDOF (line 483) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyPointOnLine (line 497) | class DependencyPointOnLine(Dependency):
method __init__ (line 498) | def __init__(self, constraint, refType):
method getMovement (line 503) | def getMovement(self):
method calcDOF (line 527) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyPointOnPlane (line 541) | class DependencyPointOnPlane(Dependency):
method __init__ (line 542) | def __init__(self, constraint, refType):
method getMovement (line 547) | def getMovement(self):
method calcDOF (line 572) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyCircularEdge (line 586) | class DependencyCircularEdge(Dependency):
method __init__ (line 587) | def __init__(self, constraint, refType):
method getMovement (line 592) | def getMovement(self):
method calcDOF (line 598) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyParallelPlanes (line 619) | class DependencyParallelPlanes(Dependency):
method __init__ (line 620) | def __init__(self, constraint, refType):
method getMovement (line 625) | def getMovement(self):
method calcDOF (line 630) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyAngledPlanes (line 637) | class DependencyAngledPlanes(Dependency):
method __init__ (line 638) | def __init__(self, constraint, refType):
method getMovement (line 643) | def getMovement(self):
method getRotation (line 648) | def getRotation(self, solver):
method calcDOF (line 671) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyPlane (line 678) | class DependencyPlane(Dependency):
method __init__ (line 679) | def __init__(self, constraint, refType):
method getMovement (line 684) | def getMovement(self):
method calcDOF (line 695) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyAxial (line 707) | class DependencyAxial(Dependency):
method __init__ (line 708) | def __init__(self, constraint, refType):
method getMovement (line 725) | def getMovement(self):
method adjustRefPoints (line 735) | def adjustRefPoints(self,obj,sub,refPoint,axis):
method calcDOF (line 749) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyAxisParallel (line 761) | class DependencyAxisParallel(Dependency):
method __init__ (line 762) | def __init__(self, constraint, refType):
method getMovement (line 767) | def getMovement(self):
method calcDOF (line 771) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyAxisPlaneParallel (line 780) | class DependencyAxisPlaneParallel(Dependency):
method __init__ (line 781) | def __init__(self, constraint, refType):
method getMovement (line 786) | def getMovement(self):
method getRotation (line 790) | def getRotation(self, solver):
method calcDOF (line 813) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyAxisPlaneAngle (line 820) | class DependencyAxisPlaneAngle(Dependency):
method __init__ (line 821) | def __init__(self, constraint, refType):
method getMovement (line 826) | def getMovement(self):
method getRotation (line 830) | def getRotation(self, solver):
method calcDOF (line 853) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyAxisPlaneNormal (line 860) | class DependencyAxisPlaneNormal(Dependency):
method __init__ (line 861) | def __init__(self, constraint, refType):
method getMovement (line 866) | def getMovement(self):
method calcDOF (line 870) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
class DependencyCenterOfMass (line 877) | class DependencyCenterOfMass(Dependency):
method __init__ (line 878) | def __init__(self, constraint, refType):
method getMovement (line 883) | def getMovement(self):
method calcDOF (line 889) | def calcDOF(self, _dofPos, _dofRot, _pointconstraints=[]):
FILE: a2p_fcdocumentreader.py
class A2p_xmldoc_Property (line 33) | class A2p_xmldoc_Property(object):
method __init__ (line 37) | def __init__(self,treeElement, name,_type):
method __str__ (line 42) | def __str__(self):
class A2p_xmldoc_PropertyString (line 47) | class A2p_xmldoc_PropertyString(A2p_xmldoc_Property):
method getStringValue (line 49) | def getStringValue(self):
class A2p_xmldoc_PropertyBool (line 55) | class A2p_xmldoc_PropertyBool(A2p_xmldoc_Property):
method getBool (line 57) | def getBool(self):
class A2p_xmldoc_PropertyFloat (line 66) | class A2p_xmldoc_PropertyFloat(A2p_xmldoc_Property):
method getFloat (line 68) | def getFloat(self):
class A2p_xmldoc_PropertyFile (line 74) | class A2p_xmldoc_PropertyFile(A2p_xmldoc_Property):
method getStringValue (line 76) | def getStringValue(self):
class A2p_xmldoc_PropertySheet (line 82) | class A2p_xmldoc_PropertySheet(A2p_xmldoc_Property):
method getCellValues (line 84) | def getCellValues(self):
class A2p_xmldoc_Object (line 97) | class A2p_xmldoc_Object(object):
method __init__ (line 101) | def __init__(self,name,_type, tree):
method __str__ (line 110) | def __str__(self):
method loadPropertyDict (line 117) | def loadPropertyDict(self,tree):
class A2p_xmldoc_SpreadSheet (line 161) | class A2p_xmldoc_SpreadSheet(A2p_xmldoc_Object):
method getCells (line 163) | def getCells(self):
class A2p_xmldoc_FeaturePython (line 167) | class A2p_xmldoc_FeaturePython(A2p_xmldoc_Object):
method isA2pObject (line 169) | def isA2pObject(self):
method getA2pSource (line 173) | def getA2pSource(self):
method isSubassembly (line 178) | def isSubassembly(self):
method getTimeLastImport (line 187) | def getTimeLastImport(self):
class FCdocumentReader (line 198) | class FCdocumentReader(object):
method __init__ (line 204) | def __init__(self):
method clear (line 209) | def clear(self):
method openDocument (line 215) | def openDocument(self,fileName):
method loadObjects (line 232) | def loadObjects(self):
method getA2pObjects (line 252) | def getA2pObjects(self):
method getSpreadsheetObjects (line 264) | def getSpreadsheetObjects(self):
method getObjectByName (line 271) | def getObjectByName(self,name):
FILE: a2p_importedPart_class.py
class Proxy_importPart (line 36) | class Proxy_importPart:
method __init__ (line 39) | def __init__(self,obj):
method setProperties (line 44) | def setProperties(self,obj):
method onDocumentRestored (line 79) | def onDocumentRestored(self,obj):
method __getstate__ (line 82) | def __getstate__(self):
method __setstate__ (line 85) | def __setstate__(self, state):
method dumps (line 88) | def dumps(self):
method loads (line 91) | def loads(self, state):
method execute (line 94) | def execute(self, obj):
class ImportedPartViewProviderProxy (line 105) | class ImportedPartViewProviderProxy:
method __init__ (line 108) | def __init__(self,vobj):
method claimChildren (line 111) | def claimChildren(self):
method onDelete (line 128) | def onDelete(self, viewObject, subelements): # subelements is a tuple...
method getIcon (line 151) | def getIcon(self):
method __getstate__ (line 166) | def __getstate__(self):
method __setstate__ (line 169) | def __setstate__(self, state):
method dumps (line 172) | def dumps(self):
method loads (line 175) | def loads(self, state):
method attach (line 178) | def attach(self, vobj):
method setupContextMenu (line 182) | def setupContextMenu(self, ViewObject, popup_menu):
class Proxy_muxAssemblyObj (line 186) | class Proxy_muxAssemblyObj(Proxy_importPart):
class Proxy_convertPart (line 192) | class Proxy_convertPart(Proxy_importPart):
FILE: a2p_importpart.py
class DataContainer (line 49) | class DataContainer():
method __init__ (line 50) | def __init__(self):
class ObjectCache (line 54) | class ObjectCache:
method __init__ (line 59) | def __init__(self):
method cleanUp (line 62) | def cleanUp(self,doc):
method add (line 70) | def add(self,fileName,obj): # pi_obj = PartInformation-Object
method get (line 73) | def get(self,fileName):
method isCached (line 80) | def isCached(self,fileName):
method len (line 86) | def len(self):
class a2p_multiShapeExtractDialog (line 93) | class a2p_multiShapeExtractDialog(QtGui.QDialog):
method __init__ (line 100) | def __init__(self, parent, labelList = [], iconList = [], data = None):
method initUI (line 107) | def initUI(self):
method accept (line 140) | def accept(self):
method reject (line 156) | def reject(self):
class a2p_shapeExtractDialog (line 162) | class a2p_shapeExtractDialog(QtGui.QDialog):
method __init__ (line 170) | def __init__(self,parent,labelList = [], data = None):
method initUI (line 177) | def initUI(self):
method accept (line 198) | def accept(self):
method reject (line 203) | def reject(self):
function importPartFromFile (line 207) | def importPartFromFile(
class a2p_ImportShapeReferenceCommand (line 432) | class a2p_ImportShapeReferenceCommand():
method GetResources (line 434) | def GetResources(self):
method Activated (line 441) | def Activated(self):
method GuiViewFit (line 612) | def GuiViewFit(self):
class a2p_Restore_Transparency_Command (line 628) | class a2p_Restore_Transparency_Command():
method GetResources (line 630) | def GetResources(self):
method Activated (line 637) | def Activated(self):
method IsActive (line 652) | def IsActive(self):
class a2p_ImportPartCommand (line 668) | class a2p_ImportPartCommand():
method GetResources (line 670) | def GetResources(self):
method Activated (line 677) | def Activated(self):
method GuiViewFit (line 752) | def GuiViewFit(self):
function updateImportedParts (line 761) | def updateImportedParts(doc, partial=False):
class a2p_UpdateImportedPartsCommand (line 896) | class a2p_UpdateImportedPartsCommand:
method Activated (line 898) | def Activated(self):
method GetResources (line 902) | def GetResources(self):
method IsActive (line 909) | def IsActive(self):
function duplicateImportedPart (line 917) | def duplicateImportedPart( part ):
class a2p_DuplicatePartCommand (line 971) | class a2p_DuplicatePartCommand:
method __init__ (line 973) | def __init__(self):
method Activated (line 976) | def Activated(self):
method onTimer (line 988) | def onTimer(self):
method IsActive (line 995) | def IsActive(self):
method GetResources (line 1007) | def GetResources(self):
class a2p_EditPartCommand (line 1041) | class a2p_EditPartCommand:
method Activated (line 1042) | def Activated(self):
method IsActive (line 1125) | def IsActive(self):
method GetResources (line 1136) | def GetResources(self):
class PartMover (line 1148) | class PartMover:
method __init__ (line 1149) | def __init__(self, view, obj, deleteOnEscape):
method moveMouse (line 1160) | def moveMouse(self, info):
method removeCallbacks (line 1164) | def removeCallbacks(self):
method clickMouse (line 1170) | def clickMouse(self, info):
method KeyboardEvent (line 1180) | def KeyboardEvent(self, info):
class a2p_MovePartCommand (line 1206) | class a2p_MovePartCommand:
method __init__ (line 1208) | def __init__(self):
method Activated (line 1211) | def Activated(self):
method IsActive (line 1216) | def IsActive(self):
method GetResources (line 1228) | def GetResources(self):
class ConstrainedPartsMover (line 1239) | class ConstrainedPartsMover:
method __init__ (line 1240) | def __init__(self, view):
method setPreselection (line 1249) | def setPreselection(self,doc,obj,sub):
method addSelection (line 1254) | def addSelection(self,doc,obj,sub,pnt):
method removeSelection (line 1257) | def removeSelection(self,doc,obj,sub):
method clearSelection (line 1260) | def clearSelection(self,doc):
method onMouseMove (line 1263) | def onMouseMove(self, info):
method removeCallbacks (line 1280) | def removeCallbacks(self):
method onMouseClicked (line 1286) | def onMouseClicked(self, info):
method KeyboardEvent (line 1308) | def KeyboardEvent(self, info):
class a2p_MovePartUnderConstraints (line 1327) | class a2p_MovePartUnderConstraints:
method __init__ (line 1329) | def __init__(self):
method Activated (line 1332) | def Activated(self):
method IsActive (line 1338) | def IsActive(self):
method GetResources (line 1350) | def GetResources(self):
class DeleteConnectionsCommand (line 1380) | class DeleteConnectionsCommand:
method Activated (line 1381) | def Activated(self):
method IsActive (line 1420) | def IsActive(self):
method GetResources (line 1431) | def GetResources(self):
class ViewConnectionsCommand (line 1456) | class ViewConnectionsCommand:
method Activated (line 1457) | def Activated(self):
method IsActive (line 1478) | def IsActive(self):
method GetResources (line 1482) | def GetResources(self):
class ViewConnectionsObserver (line 1493) | class ViewConnectionsObserver:
method __init__ (line 1494) | def __init__(self,initialTransparencyState):
method clearSelection (line 1499) | def clearSelection(self, doc):
method setSelection (line 1510) | def setSelection(self, doc):
class a2p_isolateCommand (line 1540) | class a2p_isolateCommand:
method hasFaces (line 1542) | def hasFaces(self,ob):
method Activated (line 1547) | def Activated(self):
method GetResources (line 1580) | def GetResources(self):
class a2p_ToggleTransparencyCommand (line 1591) | class a2p_ToggleTransparencyCommand:
method Activated (line 1592) | def Activated(self, checked):
method IsChecked (line 1604) | def IsChecked(self):
method IsActive (line 1607) | def IsActive(self):
method GetResources (line 1610) | def GetResources(self):
class a2p_ToggleAutoSolveCommand (line 1636) | class a2p_ToggleAutoSolveCommand:
method Activated (line 1638) | def Activated(self, checked):
method IsChecked (line 1641) | def IsChecked(self):
method GetResources (line 1644) | def GetResources(self):
class a2p_TogglePartialProcessingCommand (line 1655) | class a2p_TogglePartialProcessingCommand:
method Activated (line 1657) | def Activated(self, checked):
method IsChecked (line 1660) | def IsChecked(self):
method GetResources (line 1663) | def GetResources(self):
class a2p_repairTreeViewCommand (line 1677) | class a2p_repairTreeViewCommand:
method Activated (line 1679) | def Activated(self):
method GetResources (line 1688) | def GetResources(self):
class a2p_FlipConstraintDirectionCommand (line 1712) | class a2p_FlipConstraintDirectionCommand:
method Activated (line 1714) | def Activated(self):
method GetResources (line 1723) | def GetResources(self):
function a2p_FlipConstraintDirection (line 1733) | def a2p_FlipConstraintDirection():
class a2p_Show_Hierarchy_Command (line 1757) | class a2p_Show_Hierarchy_Command:
method Activated (line 1759) | def Activated(self):
method GetResources (line 1772) | def GetResources(self):
class a2p_Show_PartLabels_Command (line 1782) | class a2p_Show_PartLabels_Command:
method Activated (line 1784) | def Activated(self, index):
method IsChecked (line 1828) | def IsChecked(self):
method IsActive (line 1834) | def IsActive(self):
method GetResources (line 1838) | def GetResources(self):
class a2p_Show_DOF_info_Command (line 1849) | class a2p_Show_DOF_info_Command:
method Activated (line 1851) | def Activated(self, index):
method IsActive (line 1864) | def IsActive(self):
method IsChecked (line 1868) | def IsChecked(self):
method GetResources (line 1874) | def GetResources(self):
class a2p_absPath_to_relPath_Command (line 1885) | class a2p_absPath_to_relPath_Command:
method Activated (line 1886) | def Activated(self):
method GetResources (line 1910) | def GetResources(self):
class a2p_SaveAndExit_Command (line 1920) | class a2p_SaveAndExit_Command:
method Activated (line 1921) | def Activated(self):
method IsActive (line 1937) | def IsActive(self):
method GetResources (line 1940) | def GetResources(self):
class a2p_MigrateProxiesCommand (line 1969) | class a2p_MigrateProxiesCommand():
method Activated (line 1971) | def Activated(self):
method GetResources (line 2004) | def GetResources(self):
function importUpdateConstraintSubobjects (line 2015) | def importUpdateConstraintSubobjects( doc, oldObject, newObject ):
class a2p_cleanUpDebug3dCommand (line 2132) | class a2p_cleanUpDebug3dCommand():
method Activated (line 2134) | def Activated(self):
method GetResources (line 2143) | def GetResources(self):
FILE: a2p_lcs_support.py
function LCS_Group_deleteContent (line 29) | def LCS_Group_deleteContent(_self,doc):
class LCS_Group (line 42) | class LCS_Group(object):
method __init__ (line 43) | def __init__(self, obInstance):
method execute (line 50) | def execute(self, obj):
method onChanged (line 53) | def onChanged(self, obj, prop):
class VP_LCS_Group (line 58) | class VP_LCS_Group(object):
method __init__ (line 59) | def __init__(self,vobj):
method attach (line 63) | def attach(self, vobj):
method onDelete (line 71) | def onDelete(self, viewObject, subelements): # subelements is a tuple ...
method getIcon (line 85) | def getIcon(self):
method __getstate__ (line 88) | def __getstate__(self):
method __setstate__ (line 91) | def __setstate__(self, state):
method dumps (line 94) | def dumps(self):
method loads (line 97) | def loads(self, state):
function getListOfLCS (line 102) | def getListOfLCS(targetDoc,sourceDoc):
FILE: a2p_libDOF.py
function create_Axis (line 70) | def create_Axis(_base, _direction):
function create_Axis2Points (line 78) | def create_Axis2Points(_start, _end):
function zeroIfLessThanTol (line 85) | def zeroIfLessThanTol(vector):
function cleanAxis (line 95) | def cleanAxis(axisa):
function copynorm_AxisToOrigin (line 105) | def copynorm_AxisToOrigin(axisa, dbg=False):
function normal_2Axis (line 111) | def normal_2Axis(axisa,axisb,dbg=False):
function make_planeNormal (line 124) | def make_planeNormal(axisa,dbg=False):
function check_ifParallel (line 138) | def check_ifParallel(axisa,axisb,dbg=False):
function check_ifPerpendicular (line 153) | def check_ifPerpendicular(axisa,axisb,dbg=False):
function check_ifCollinear (line 167) | def check_ifCollinear(axisa,axisb,dbg=False):
function check_ifCoincident (line 194) | def check_ifCoincident(Vertex1, Vertex2, dbg=False):
function check_ifPointOnAxis (line 209) | def check_ifPointOnAxis(vertexa, axisa, dbg=False):
function AxisAlignment (line 231) | def AxisAlignment(axisa , dofrot, pointconstraints = None, dbg=True):
function LockRotation (line 271) | def LockRotation(enabled, dofrot, pointconstraints = None, dbg=True):
function AngleAlignment (line 282) | def AngleAlignment(axisa , dofrot, pointconstraints = None, dbg=True):
function AxisDistance (line 328) | def AxisDistance(axisa, dofpos, pointconstraints = None, dbg=False):
function PlaneOffset (line 363) | def PlaneOffset(axisa, dofpos, pointconstraints = [], dbg=False):
function PointIdentity (line 395) | def PointIdentity(axisa, dofpos, dofrot, pointconstraints, dbg=False):
FILE: a2p_observers.py
class RedoUndoObserver (line 31) | class RedoUndoObserver(object):
method slotRedoDocument (line 32) | def slotRedoDocument(self,doc):
method slotUndoDocument (line 34) | def slotUndoDocument(self,doc):
FILE: a2p_partinformation.py
class a2p_CreatePartInformationSheet_Command (line 54) | class a2p_CreatePartInformationSheet_Command:
method Activated (line 56) | def Activated(self):
method GetResources (line 86) | def GetResources(self):
FILE: a2p_recursiveUpdatePlanner.py
function createUpdateFileList (line 35) | def createUpdateFileList(
class a2p_recursiveUpdateImportedPartsCommand (line 106) | class a2p_recursiveUpdateImportedPartsCommand:
method Activated (line 108) | def Activated(self):
method GetResources (line 195) | def GetResources(self):
method IsActive (line 214) | def IsActive(self):
FILE: a2p_rigid.py
class Rigid (line 69) | class Rigid():
method __init__ (line 72) | def __init__(self,
method prepareRestart (line 115) | def prepareRestart(self):
method countDependencies (line 120) | def countDependencies(self):
method enableDependencies (line 123) | def enableDependencies(self, workList):
method assignParentship (line 129) | def assignParentship(self, distance):
method printHierarchy (line 157) | def printHierarchy(self, level):
method getCandidates (line 163) | def getCandidates(self, solverStage = None):
method addChildrenByDistance (line 170) | def addChildrenByDistance(self, addList, distance):
method areAllParentTempFixed (line 183) | def areAllParentTempFixed(self):
method applyPlacementStep (line 189) | def applyPlacementStep(self, pl):
method clear (line 196) | def clear(self):
method applySolution (line 202) | def applySolution(self, doc, solver):
method getRigidCenter (line 222) | def getRigidCenter(self):
method calcSpinCenterDepsEnabled (line 226) | def calcSpinCenterDepsEnabled(self):
method calcSpinCenter (line 238) | def calcSpinCenter(self):
method calcSpinBasicDataDepsEnabled (line 249) | def calcSpinBasicDataDepsEnabled(self):
method calcRefPointsBoundBoxSizeDepsEnabled (line 276) | def calcRefPointsBoundBoxSizeDepsEnabled(self):
method calcRefPointsBoundBoxSize (line 293) | def calcRefPointsBoundBoxSize(self):
method calcMoveData (line 309) | def calcMoveData(self, doc, solver):
method updateCachedState (line 398) | def updateCachedState(self, newPlacement):
method move (line 404) | def move(self,doc):
method currentDOF (line 442) | def currentDOF(self):
method isFullyConstrainedByRigid (line 462) | def isFullyConstrainedByRigid(self,rig):
method isFullyConstrainedByFixedRigids (line 471) | def isFullyConstrainedByFixedRigids(self):
method linkedTempFixedDOF (line 486) | def linkedTempFixedDOF(self):
method reorderDependencies (line 500) | def reorderDependencies(self):
method beautyDOFPrint (line 516) | def beautyDOFPrint(self):
FILE: a2p_searchConstraintConflicts.py
class a2p_SearchConstraintConflictsCommand (line 45) | class a2p_SearchConstraintConflictsCommand:
method Activated (line 49) | def Activated(self):
method IsActive (line 106) | def IsActive(self):
method GetResources (line 110) | def GetResources(self):
FILE: a2p_simpleXMLreader.py
class simpleXMLObject (line 43) | class simpleXMLObject(object):
method __init__ (line 44) | def __init__(self):
method clear (line 49) | def clear(self):
method initialize (line 54) | def initialize(self,xmlDefs):
method scanForProperties (line 71) | def scanForProperties(self):
method parseCellLine (line 159) | def parseCellLine(self,line):
method isA2pObject (line 172) | def isA2pObject(self):
method isA2pSketch (line 176) | def isA2pSketch(self):
method isSpreadSheet (line 185) | def isSpreadSheet(self):
method getA2pSource (line 189) | def getA2pSource(self):
method isSubassembly (line 194) | def isSubassembly(self):
method getTimeLastImport (line 203) | def getTimeLastImport(self):
method getCells (line 212) | def getCells(self):
class FCdocumentReader (line 216) | class FCdocumentReader(object):
method __init__ (line 225) | def __init__(self):
method clear (line 230) | def clear(self):
method openDocument (line 235) | def openDocument(self,_fileName):
method getA2pObjects (line 290) | def getA2pObjects(self):
method getSpreadsheetObjects (line 303) | def getSpreadsheetObjects(self):
method getObjectByName (line 311) | def getObjectByName(self,name):
FILE: a2p_solversystem.py
class SolverSystem (line 62) | class SolverSystem():
method __init__ (line 69) | def __init__(self):
method clear (line 91) | def clear(self):
method getSolverControlData (line 100) | def getSolverControlData(self):
method getRigid (line 119) | def getRigid(self,objectName):
method removeFaultyConstraints (line 125) | def removeFaultyConstraints(self, doc):
method loadSystem (line 147) | def loadSystem(self,doc, matelist=None):
method DOF_info_to_console (line 250) | def DOF_info_to_console(self):
method retrieveDOFInfo (line 317) | def retrieveDOFInfo(self):
method assignParentship (line 361) | def assignParentship(self, doc):
method visualizeHierarchy (line 382) | def visualizeHierarchy(self):
method calcMoveData (line 431) | def calcMoveData(self,doc):
method prepareRestart (line 435) | def prepareRestart(self):
method detectUnmovedParts (line 440) | def detectUnmovedParts(self):
method solveAccuracySteps (line 450) | def solveAccuracySteps(self,doc, matelist=None):
method solveSystem (line 498) | def solveSystem(self,doc,matelist=None, showFailMessage=True):
method checkForUnmovedParts (line 533) | def checkForUnmovedParts(self):
method printList (line 561) | def printList(self, name, l):
method calculateChain (line 567) | def calculateChain(self, doc):
method calculateWorkList (line 614) | def calculateWorkList(self, doc, workList):
method solutionToParts (line 710) | def solutionToParts(self,doc):
function solveConstraints (line 715) | def solveConstraints( doc, cache=None, useTransaction = True, matelist=N...
function autoSolveConstraints (line 732) | def autoSolveConstraints( doc, callingFuncName, cache=None, useTransacti...
class a2p_SolverCommand (line 745) | class a2p_SolverCommand:
method Activated (line 746) | def Activated(self):
method GetResources (line 749) | def GetResources(self):
FILE: a2p_topomapper.py
class TopoMapper (line 100) | class TopoMapper(object):
method __init__ (line 101) | def __init__(self,doc):
method calcFloatKey (line 116) | def calcFloatKey(self,val):
method calcVertexKey (line 119) | def calcVertexKey(self,inOb):
method calcAxisKey (line 143) | def calcAxisKey(self,axis):
method calcEdgeKeys (line 159) | def calcEdgeKeys(self, edge, pl):
method calcFaceKeys (line 203) | def calcFaceKeys(self, face, pl):
method populateShapeDict (line 267) | def populateShapeDict(self,objName):
method processTopoData (line 348) | def processTopoData(self,objName,level=0):
method makePlacedShape (line 364) | def makePlacedShape(self,obj):
method addedByPathWB (line 377) | def addedByPathWB(self,obName):
method isTopLevelInList (line 396) | def isTopLevelInList(self,lst):
method getTopLevelObjects (line 410) | def getTopLevelObjects(self, allowSketches=False):
method detectPartDesignDocument (line 517) | def detectPartDesignDocument(self):
method getLinkedObjectRecursive (line 524) | def getLinkedObjectRecursive(self,ob):
method createTopoNames (line 542) | def createTopoNames(self, desiredShapeLabel = None):
FILE: a2p_translateUtils.py
function QT_TRANSLATE_NOOP (line 33) | def QT_TRANSLATE_NOOP(context, text):
function translate (line 36) | def translate(context, text):
function tr_ (line 40) | def tr_(text):
FILE: a2p_viewProviderProxies.py
class PopUpMenuItem (line 34) | class PopUpMenuItem:
method __init__ (line 35) | def __init__( self, proxy, menu, label, Freecad_cmd ):
method execute (line 42) | def execute( self ):
class ConstraintViewProviderProxy (line 51) | class ConstraintViewProviderProxy:
method __init__ (line 52) | def __init__(
method getIcon (line 75) | def getIcon(self):
method doubleClicked (line 82) | def doubleClicked(self,vobj):
method attach (line 87) | def attach(self, vobj): #attach to what document?
method getDisplayModes (line 90) | def getDisplayModes(self,obj):
method getDefaultDisplayMode (line 93) | def getDefaultDisplayMode(self):
method onChanged (line 96) | def onChanged(self,viewObject,prop):
method onDelete (line 114) | def onDelete(self, viewObject, subelements): # subelements is a tuple ...
class ConstraintMirrorViewProviderProxy (line 136) | class ConstraintMirrorViewProviderProxy:
method __init__ (line 137) | def __init__( self, constraintObj, iconPath ):
method doubleClicked (line 143) | def doubleClicked(self,vobj):
method getIcon (line 147) | def getIcon(self):
method attach (line 155) | def attach(self, vobj):
method getDisplayModes (line 158) | def getDisplayModes(self,obj):
method getDefaultDisplayMode (line 161) | def getDefaultDisplayMode(self):
method onChanged (line 164) | def onChanged(self,viewObject,prop):
method onDelete (line 182) | def onDelete(self, viewObject, subelements): # subelements is a tuple ...
function create_constraint_mirror (line 204) | def create_constraint_mirror( constraintObj, iconPath, origLabel= '', mi...
class ConstraintObjectProxy (line 252) | class ConstraintObjectProxy:
method __init__ (line 253) | def __init__(self,obj=None):
method setProperties (line 259) | def setProperties(self,obj):
method onDocumentRestored (line 274) | def onDocumentRestored(self,obj):
method execute (line 277) | def execute(self, obj):
method onChanged (line 280) | def onChanged(self, obj, prop):
method reduceDirectionChoices (line 295) | def reduceDirectionChoices( self, obj, value):
method callSolveConstraints (line 302) | def callSolveConstraints(self):
class ConstraintMirrorObjectProxy (line 312) | class ConstraintMirrorObjectProxy:
method __init__ (line 313) | def __init__(self, obj, constraintObj ):
method setProperties (line 322) | def setProperties(self,obj):
method onDocumentRestored (line 337) | def onDocumentRestored(self,obj):
method execute (line 340) | def execute(self, obj):
method onChanged (line 343) | def onChanged(self, obj, prop):
FILE: a2plib.py
function get_module_path (line 139) | def get_module_path():
function getLanguagePath (line 167) | def getLanguagePath():
function getA2pVersion (line 185) | def getA2pVersion():
function drawDebugVectorAt (line 205) | def drawDebugVectorAt(position,direction,rgbColor):
function isGlobalVisible (line 243) | def isGlobalVisible(ob):
function to_bytes (line 279) | def to_bytes(tx):
function to_str (line 287) | def to_str(tx):
function setSimulationState (line 295) | def setSimulationState(boolVal):
function doNotImportInvisibleShapes (line 299) | def doNotImportInvisibleShapes():
function getPerFaceTransparency (line 303) | def getPerFaceTransparency():
function getNativeFileManagerUsage (line 307) | def getNativeFileManagerUsage():
function getRecalculateImportedParts (line 311) | def getRecalculateImportedParts():
function getRecursiveUpdateEnabled (line 315) | def getRecursiveUpdateEnabled():
function getForceFixedPosition (line 319) | def getForceFixedPosition():
function getUseSolidUnion (line 323) | def getUseSolidUnion():
function getConstraintEditorRef (line 327) | def getConstraintEditorRef():
function setConstraintEditorRef (line 331) | def setConstraintEditorRef(ref):
function setConstraintViewMode (line 335) | def setConstraintViewMode(active):
function getConstraintViewMode (line 339) | def getConstraintViewMode():
function getConstraintDialogRef (line 343) | def getConstraintDialogRef():
function setConstraintDialogRef (line 347) | def setConstraintDialogRef(ref):
function getUseTopoNaming (line 351) | def getUseTopoNaming():
function getRelativePathesEnabled (line 355) | def getRelativePathesEnabled():
function setAutoSolve (line 359) | def setAutoSolve(enabled):
function getAutoSolveState (line 363) | def getAutoSolveState():
function setPartialProcessing (line 366) | def setPartialProcessing(enabled):
function isPartialProcessing (line 370) | def isPartialProcessing():
function filterShapeObs (line 373) | def filterShapeObs(_list, allowSketches=False):
function setTransparency (line 403) | def setTransparency():
function restoreTransparency (line 430) | def restoreTransparency():
function isTransparencyEnabled (line 448) | def isTransparencyEnabled():
function getSelectedConstraint (line 452) | def getSelectedConstraint():
function appVersionStr (line 463) | def appVersionStr():
function numpyVecToFC (line 468) | def numpyVecToFC(nv):
function fit_rotation_axis_to_surface1 (line 472) | def fit_rotation_axis_to_surface1( surface, n_u=3, n_v=3 ):
function fit_plane_to_surface1 (line 516) | def fit_plane_to_surface1( surface, n_u=3, n_v=3 ):
function isLine (line 525) | def isLine(param):
function getObjectFaceFromName (line 531) | def getObjectFaceFromName( obj, faceName ):
function getProjectFolder (line 536) | def getProjectFolder():
function pathToOS (line 548) | def pathToOS(path):
function findFile (line 558) | def findFile(_name, _path):
function findSourceFileInProject (line 570) | def findSourceFileInProject(_pathImportPart, _assemblyPath):
function checkFileIsInProjectFolder (line 621) | def checkFileIsInProjectFolder(path):
function Msg (line 635) | def Msg(tx):
function DebugMsg (line 639) | def DebugMsg(level, tx):
function drawSphere (line 644) | def drawSphere(center, color):
function drawVector (line 652) | def drawVector(fromPoint,toPoint, color):
function findUnusedObjectName (line 678) | def findUnusedObjectName(base, counterStart=1, fmt='%03i', document=None):
function findUnusedObjectLabel (line 699) | def findUnusedObjectLabel(base, counterStart=1, fmt='%03i', document=Non...
class ConstraintSelectionObserver (line 725) | class ConstraintSelectionObserver:
method __init__ (line 727) | def __init__(self, selectionGate, parseSelectionFunction,
method addSelection (line 740) | def addSelection( self, docName, objName, sub, pnt ):
method stopSelectionObservation (line 749) | def stopSelectionObservation(self):
class SelectionRecord (line 756) | class SelectionRecord:
method __init__ (line 757) | def __init__(self, docName, objName, sub):
class SelectionTaskDialog (line 764) | class SelectionTaskDialog:
method __init__ (line 766) | def __init__(self, title, iconPath, textLines ):
method reject (line 772) | def reject(self):
method getStandardButtons (line 775) | def getStandardButtons(self): #http://forum.freecadweb.org/viewtopic.p...
class SelectionTaskDialogForm (line 778) | class SelectionTaskDialogForm(QtGui.QWidget):
method __init__ (line 780) | def __init__(self, textLines ):
method initUI (line 785) | def initUI(self):
class SelectionExObject (line 792) | class SelectionExObject:
method __init__ (line 794) | def __init__(self, doc, Object, subElementName):
function getObjectEdgeFromName (line 800) | def getObjectEdgeFromName( obj, name ):
function CircularEdgeSelected (line 805) | def CircularEdgeSelected( selection ):
function ClosedEdgeSelected (line 832) | def ClosedEdgeSelected( selection ):
function AxisOfPlaneSelected (line 843) | def AxisOfPlaneSelected( selection ): #adding Planes/Faces selection for...
function printSelection (line 857) | def printSelection(selection):
function updateObjectProperties (line 868) | def updateObjectProperties( c ):
function planeSelected (line 871) | def planeSelected( selection ):
function vertexSelected (line 884) | def vertexSelected( selection ):
function cylindricalFaceSelected (line 889) | def cylindricalFaceSelected( selection ):
function LinearEdgeSelected (line 905) | def LinearEdgeSelected( selection ):
function sphericalSurfaceSelected (line 924) | def sphericalSurfaceSelected( selection ):
function getObjectVertexFromName (line 932) | def getObjectVertexFromName( obj, name ):
function removeConstraint (line 937) | def removeConstraint( constraint ):
function getPos (line 944) | def getPos(obj, subElementName):
function getPlaneNormal (line 1000) | def getPlaneNormal(surface):
function getAxis (line 1008) | def getAxis(obj, subElementName):
function unTouchA2pObjects (line 1050) | def unTouchA2pObjects():
function isA2pSketch (line 1058) | def isA2pSketch(obj):
function isA2pPart (line 1065) | def isA2pPart(obj):
function isEditableA2pPart (line 1078) | def isEditableA2pPart(obj):
function isA2pConstraint (line 1084) | def isA2pConstraint(obj):
function isA2pObject (line 1091) | def isA2pObject(obj):
function isFastenerObject (line 1097) | def isFastenerObject(obj):
function makeDiffuseElement (line 1107) | def makeDiffuseElement(color,trans):
function copyObjectColors (line 1111) | def copyObjectColors(ob1,ob2):
function isConstrainedPart (line 1141) | def isConstrainedPart(doc,obj):
function objectExists (line 1151) | def objectExists(name):
function deleteConstraintsOfDeletedObjects (line 1160) | def deleteConstraintsOfDeletedObjects():
function a2p_repairTreeView (line 1188) | def a2p_repairTreeView():
FILE: translations/update_ts.py
function create_ts (line 37) | def create_ts():
function merge_ts (line 71) | def merge_ts():
function update_ts (line 82) | def update_ts():
Condensed preview — 67 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,292K chars).
[
{
"path": ".gitignore",
"chars": 402,
"preview": "# file types to ignore\n\n*.pyc\n*.obj\n*.lib\n*.pch\n*.vcproj\n*.exp\n*.dep\n*.bak\n*.manifest\nqrc_*.cpp\nBuildLog.htm\ncmake_insta"
},
{
"path": "A2plus.qrc",
"chars": 2564,
"preview": "<RCC>\n <qresource prefix=\"/\">\n <file>icons/a2p_SetRelativePathes.svg</file>\n <file>icons/a2p_CenterOfMa"
},
{
"path": "CD_A2plusupdater.py",
"chars": 25178,
"preview": "# ***************************************************************************\r\n# * "
},
{
"path": "CD_CheckConstraints.py",
"chars": 13562,
"preview": "# ***************************************************************************\r\n# * "
},
{
"path": "CD_ConstraintViewer.py",
"chars": 30216,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "CD_FeatureLabels.py",
"chars": 8389,
"preview": "# ***************************************************************************\r\n# * "
},
{
"path": "CD_OneButton.py",
"chars": 7401,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "GuiA2p/Resources/compile_resources_pack.py",
"chars": 470,
"preview": "#! /usr/bin/env python\nimport os, glob\n\nqrc_filename = 'resources.qrc'\nassert not os.path.exists(qrc_filename)\n\nqrc = ''"
},
{
"path": "GuiA2p/Resources/ui/a2p_prefs.ui",
"chars": 14710,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>Gui::Dialog::DlgSettingsA2Plus</class>\n <widget class="
},
{
"path": "Init.py",
"chars": 1772,
"preview": "#***************************************************************************\n#* "
},
{
"path": "InitGui.py",
"chars": 9603,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "LICENSE",
"chars": 26460,
"preview": " GNU LESSER GENERAL PUBLIC LICENSE\n Version 2.1, February 1999\n\n Copyright (C) 19"
},
{
"path": "README.md",
"chars": 11746,
"preview": "<a href=\"https://github.com/kbwbe/A2plus\"><img src=\"https://github.com/kbwbe/A2plus/blob/master/icons/a2p_Workbench.svg\""
},
{
"path": "__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "a2p_BoM.py",
"chars": 20957,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "a2p_ConstraintCommands.py",
"chars": 15032,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "a2p_ConstraintDialog.py",
"chars": 49702,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "a2p_MuxAssembly.py",
"chars": 12660,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "a2p_Resources3.py",
"chars": 909110,
"preview": "# Resource object code (Python 3)\n# Created by: object code\n# Created by: The Resource Compiler for Qt version 5.15.3\n# "
},
{
"path": "a2p_Resources3_Qt6.py",
"chars": 909118,
"preview": "# Resource object code (Python 3)\n# Created by: object code\n# Created by: The Resource Compiler for Qt version 5.15.14\n#"
},
{
"path": "a2p_constraintServices.py",
"chars": 6588,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "a2p_constraints.py",
"chars": 39633,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "a2p_convertPart.py",
"chars": 7838,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_dependencies.py",
"chars": 39718,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_fcdocumentreader.py",
"chars": 10802,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_importedPart_class.py",
"chars": 8112,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_importpart.py",
"chars": 84074,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_lcs_support.py",
"chars": 4866,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_libDOF.py",
"chars": 23277,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_observers.py",
"chars": 1964,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_partinformation.py",
"chars": 4131,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_partlistglobals.py",
"chars": 2674,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_recursiveUpdatePlanner.py",
"chars": 9354,
"preview": "# ***************************************************************************\n# * "
},
{
"path": "a2p_rigid.py",
"chars": 21182,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_searchConstraintConflicts.py",
"chars": 4980,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_simpleXMLreader.py",
"chars": 12414,
"preview": "# -*- coding: utf-8 -*-\n#***************************************************************************\n#* "
},
{
"path": "a2p_solversystem.py",
"chars": 31659,
"preview": "# -*- coding: utf-8 -*-\n#***************************************************************************\n#* "
},
{
"path": "a2p_topomapper.py",
"chars": 29931,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2p_translateUtils.py",
"chars": 2014,
"preview": "# -*- coding: utf8 -*-\n\n#***************************************************************************\n#* "
},
{
"path": "a2p_viewProviderProxies.py",
"chars": 15436,
"preview": "#***************************************************************************\n#* "
},
{
"path": "a2plib.py",
"chars": 51421,
"preview": "# -*- coding: utf-8 -*-\n#***************************************************************************\n#* "
},
{
"path": "compileA2pResources.py",
"chars": 3342,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n#*************************************************************************"
},
{
"path": "crowdin.yml",
"chars": 103,
"preview": "files:\n - source: /translations/A2plus.ts\n translation: /translations/A2plus_%two_letters_code%.ts\n"
},
{
"path": "package.xml",
"chars": 1029,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n<package format=\"1\" xmlns=\"https://wiki.freecad.org/Package_Meta"
},
{
"path": "translations/A2plus.ts",
"chars": 86787,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS><TS version=\"1.1\" language=\"en_US\">\n<context>\n <name>A2p_BoM</nam"
},
{
"path": "translations/A2plus_de.ts",
"chars": 129319,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"de\">\n<context>\n <name>A2p_BoM</name>"
},
{
"path": "translations/A2plus_es-AR.ts",
"chars": 122862,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"es_AR\" sourcelanguage=\"en_US\">\n<context"
},
{
"path": "translations/A2plus_es-ES.ts",
"chars": 122862,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"es_ES\" sourcelanguage=\"en_US\">\n<context"
},
{
"path": "translations/A2plus_fr.ts",
"chars": 142949,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"fr\" sourcelanguage=\"en\">\n<context>\n "
},
{
"path": "translations/A2plus_it.ts",
"chars": 115526,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"it_IT\">\n<context>\n <name>A2p_BoM</na"
},
{
"path": "translations/A2plus_pt-br.ts",
"chars": 162601,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"pt_BR\">\n<context>\n <name>A2p_BoM</na"
},
{
"path": "translations/A2plus_pt-pt.ts",
"chars": 162564,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"pt_PT\">\n<context>\n <name>A2p_BoM</na"
},
{
"path": "translations/A2plus_ru.ts",
"chars": 116157,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"ru_RU\">\n<context>\n <name>A2p_BoM</na"
},
{
"path": "translations/A2plus_zh-CN.ts",
"chars": 111404,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"zh_CN\" sourcelanguage=\"en\">\n<context>\n "
},
{
"path": "translations/README.md",
"chars": 1898,
"preview": "# About translating A2plus Workbench\n\nA2plus Workbench supported 10 locales:\n\nEnglish: en,\nChinese Simplified"
},
{
"path": "translations/update_ts.py",
"chars": 5148,
"preview": "#! /usr/bin/env python3\n#\n# ***************************************************************************\n# * "
}
]
// ... and 11 more files (download for full content)
About this extraction
This page contains the full source code of the kbwbe/A2plus GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 67 files (3.6 MB), approximately 943.9k tokens, and a symbol index with 977 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.