Macro Dump Objects: Difference between revisions

From FreeCAD Documentation
No edit summary
(convert to PySide2)
 
(14 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<languages/>
<translate>
<translate>

<!--T:1-->
<!--T:1-->
{{Macro
{{Macro|Icon=Macro_Dump_Objects|Name=Macro Dump Objects|Description=This macro generates a listing of all objects in the current document - the list can be in a window or on the Report view.|Author=Piffpoof}}
|Name=Macro Dump Objects
|Description=This macro generates a listing of all objects in the current document - the list can be in a window or on the Report view.
|Author=Piffpoof
|Version=0.3
|Date=2023-01-14
|FCVersion= 0.20
|Download=[https://www.freecadweb.org/wiki/images/2/2e/Macro_Dump_Objects.png ToolBar Icon]
}}


<!--T:2-->
<!--T:2-->
When developing complex object models it is easy to loose track of exactly which objects are present as some may be hidden, obscured or transparent. Additionally with a large number of objects a naming system becomes necessary to keep track of the objects.
When developing complex object models it is easy to lose track of exactly which objects are present as some may be hidden, obscured or transparent. Additionally with a large number of objects a naming system becomes necessary to keep track of the objects.


<!--T:3-->
<!--T:3-->
[[File:DumpObjectsScreenSnapshot.jpg|500px]]
[[File:DumpObjectsScreenSnapshot.jpg|500px]]


==Description== <!--T:4-->
==Description== <!--T:18-->

<!--T:4-->
The Dump Object code takes the current document and enumerates all the objects. A report is then generated listing each object, then a summary giving the total number of instances of each Class, followed by the total number of Classes and finally the total number of objects. The output may be directed to the Report view or to a window. The window is non-modal and will stay open until closed by the user. Each window has the time of the object dump in it's title bar, so the contents of multiple windows can be compared, say before and after a piece of code running.
The Dump Object code takes the current document and enumerates all the objects. A report is then generated listing each object, then a summary giving the total number of instances of each Class, followed by the total number of Classes and finally the total number of objects. The output may be directed to the Report view or to a window. The window is non-modal and will stay open until closed by the user. Each window has the time of the object dump in it's title bar, so the contents of multiple windows can be compared, say before and after a piece of code running.


Line 15: Line 27:
The default operation lists all objects, optionally the placement of each object can be listed. Also for Sketches, each segment of the Geometry can be listed.
The default operation lists all objects, optionally the placement of each object can be listed. Also for Sketches, each segment of the Geometry can be listed.


==Installation== <!--T:6-->
==Installation== <!--T:19-->

<!--T:6-->
All the code for dumpObject.FCMacro is in one macro. So installation is comprised of copying the code to the appropriate Macro directory and invoking dumpObject from the Macro menu. Alternatively it may be run from the console.
All the code for dumpObject.FCMacro is in one macro. So installation is comprised of copying the code to the appropriate Macro directory and invoking dumpObject from the Macro menu. Alternatively it may be run from the console.
* see [[Macro_Install_HowTo]] for information on how to install this macro code
* see [[How to install macros]] for information on how to install this macro code
* see [[Customize_ToolsBar]] for information how to install as a button on a toolbar
* see [[Customize Toolbars]] for information how to install as a button on a toolbar


==Usage== <!--T:7-->
==Usage== <!--T:20-->

<!--T:7-->
Select the document you wish to dump objects for, then start the macro from one of:
Select the document you wish to dump objects for, then start the macro from one of:
* the Macro menu
* the Macro menu
Line 38: Line 54:
* unexpected segments in the Sketch Geometry (when the Show Sketcher Segments option is selected)
* unexpected segments in the Sketch Geometry (when the Show Sketcher Segments option is selected)


==User Interface== <!--T:10-->
==User Interface== <!--T:21-->

<!--T:10-->
The first window will take input which configures the Object Dump:
The first window will take input which configures the Object Dump:


<!--T:11-->
<!--T:11-->
[[File:DumpObjectsGui1.jpg]]
[[File:DumpObjectsGui1.jpg]]



<!--T:12-->
<!--T:12-->
The second window will be the report on the objects in the current document:
The second window will be the report on the objects in the current document:



<!--T:13-->
<!--T:13-->
[[File:DumpObjectsGui2.jpg]]
[[File:DumpObjectsGui2.jpg]]


==Options== <!--T:14-->
==Options== <!--T:22-->

<!--T:14-->
* output may be directed to one of:
* output may be directed to one of:
** the Report view
** the Report view
Line 59: Line 77:
* Placement specifics may be listed for objects
* Placement specifics may be listed for objects


==Remarks== <!--T:15-->
==Remarks== <!--T:23-->

<!--T:15-->
Although tested with many object types in FreeCAD, there probably are some objects that it does not expect, in that case it should list them generically.
Although tested with many object types in FreeCAD, there probably are some objects that it does not expect, in that case it should list them generically.

==Links== <!--T:16-->
none (so far)


==Script== <!--T:17-->
==Script== <!--T:17-->

<!--T:24-->
Toolbar Icon
</translate>
</translate>


[[Image:Macro_Dump_Objects.png]]
{{Code|code=

'''Macro_Dump_Objects.FCMacro'''

{{MacroCode|code=
#
#
# Dump Object
# Dump Object
# v 0.3 - 14/01/2023 conversion to PySide2
# v 0.2 - added report to CSV file
# v 0.2 - added report to CSV file
# v 0.1 - added report to window
# v 0.1 - added report to window
Line 78: Line 103:
# routine to dump object space for Geometric model in the currently active file
# routine to dump object space for Geometric model in the currently active file
#
#
__title__ = "Dump_Objects"
# import statements
__author__ = "Piffpoof"
from PySide import QtGui, QtCore
__url__ = "https://wiki.freecadweb.org/Macro_Dump_Objects"
from datetime import datetime # datestamp on output window
__version__ = "0.3"
from os.path import expanduser # output directory for CSV
__date__ = "2023/01/14" #YYYY/MM/DD
#


# import statements
from PySide2 import QtWidgets, QtGui, QtCore
from PySide2.QtWidgets import * #QDialog , QLineEdit, QPushButton, QApplication
from datetime import datetime # datestamp on output window
from os.path import expanduser # output directory for CSV
import math
# UI Class definitions
# UI Class definitions


class configureMacro(QtGui.QDialog):
class configureMacro(QtWidgets.QDialog):
""""""
""""""
def __init__(self):
def __init__(self):
super(configureMacro, self).__init__()
super(configureMacro, self).__init__()
self.initUI()
self.initUI()
def initUI(self):
def initUI(self):
self.result = None
self.result = None
# set up display only field for selected path type
# set up display only field for selected path type
self.cbss = QtGui.QCheckBox("Show Sketcher Segments?", self)
self.cbss = QtWidgets.QCheckBox("Show Sketcher Segments?", self)
self.cbss.move(20,20)
self.cbss.move(20,20)
self.cbp = QtGui.QCheckBox("Show Positions?", self)
self.cbp = QtWidgets.QCheckBox("Show Positions?", self)
self.cbp.move(220,20)
self.cbp.move(220,20)
self.pathTypeLbl = QtGui.QLabel("Select Report Destination:", self)
self.pathTypeLbl = QtWidgets.QLabel("Select Report Destination:", self)
self.pathTypeLbl.move(20, 70)
self.pathTypeLbl.move(20, 70)
# cancel button
# cancel button
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton = QtWidgets.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.clicked.connect(self.onCancel)
cancelButton.move(10, 100)
cancelButton.move(10, 100)
# button #1
# button #1
button1 = QtGui.QPushButton(choice1, self)
button1 = QtWidgets.QPushButton(choice1, self)
button1.clicked.connect(self.onBtn1)
button1.clicked.connect(self.onBtn1)
button1.move(120, 100)
button1.move(120, 100)
# button #2
# button #2
button2 = QtGui.QPushButton(choice2, self)
button2 = QtWidgets.QPushButton(choice2, self)
button2.clicked.connect(self.onBtn2)
button2.clicked.connect(self.onBtn2)
button2.move(235, 100)
button2.move(235, 100)
# button #3
# button #3
button3 = QtGui.QPushButton(choice3, self)
button3 = QtWidgets.QPushButton(choice3, self)
button3.clicked.connect(self.onBtn3)
button3.clicked.connect(self.onBtn3)
button3.move(327, 100)
button3.move(327, 100)
# define window xLoc,yLoc,xDim,yDim
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( 250, 250, 435, 150)
self.setGeometry( 250, 250, 435, 150)
self.setWindowTitle("Select a Report Destination")
self.setWindowTitle("Select a Report Destination")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.show()
self.show()
def onCancel(self):
def onCancel(self):
self.result = "cancelled"
self.result = "cancelled"
self.close()
self.close()
def onBtn1(self):
def onBtn1(self):
self.result = choice1
self.result = choice1
self.close()
self.close()
def onBtn2(self):
def onBtn2(self):
self.result = choice2
self.result = choice2
self.close()
self.close()
def onBtn3(self):
def onBtn3(self):
self.result = choice3
self.result = choice3
self.close()
self.close()


class DisplayText(QtGui.QWidget):
class DisplayText(QtWidgets.QWidget):
""""""
""""""
def __init__(self, textToDisplay):
def __init__(self, textToDisplay):
self.text = textToDisplay
self.text = textToDisplay
super(DisplayText, self).__init__()
super(DisplayText, self).__init__()
self.initUI(textToDisplay)
self.initUI(textToDisplay)
def initUI(self, textToDisplay):
def initUI(self, textToDisplay):
"""Constructor"""
"""Constructor"""
self.textToDisplay = textToDisplay
self.textToDisplay = textToDisplay
# some window dimensions
# some window dimensions
self.windowHome = screenWidth * 0.05
self.windowHome = screenWidth * 0.05
self.windowWidth = screenWidth * 0.9
self.windowWidth = screenWidth * 0.9
self.windowHeight = 400
self.windowHeight = 400
self.fieldMargin = 40
self.fieldMargin = 40
# some column titles
# some column titles
columnLabels = QtGui.QLabel(formatPrintLine("Type / WB","Shape","User Supplied Label","Name"))
columnLabels = QtWidgets.QLabel(formatPrintLine("Type / WB","Shape","User Supplied Label","Name"))
columnLabels.setFont('Courier')
columnLabels.setFont('Courier')
# set up text editing widget
# set up text editing widget
text_editor = QtGui.QTextEdit(self)
text_editor = QtWidgets.QTextEdit(self)
#self.setCentralWidget(self.text_editor)
#self.setCentralWidget(self.text_editor)
text_editor.setFont('Courier')
text_editor.setFont('Courier')
text_editor.setLineWrapMode(QtGui.QTextEdit.NoWrap)
text_editor.setLineWrapMode(QtWidgets.QTextEdit.NoWrap)
text_editor.move(self.fieldMargin,self.fieldMargin)
text_editor.move(self.fieldMargin,self.fieldMargin)
text_editor.move(0,self.fieldMargin)
text_editor.move(0,self.fieldMargin)
text_editor.resize(self.windowWidth-(2*self.fieldMargin),self.windowHeight-(2*self.fieldMargin))
text_editor.resize(self.windowWidth-(2*self.fieldMargin),self.windowHeight-(2*self.fieldMargin))
text_editor.resize(self.windowWidth,self.windowHeight-(2*self.fieldMargin))
text_editor.resize(self.windowWidth,self.windowHeight-(2*self.fieldMargin))
text_editor.append(self.textToDisplay)
text_editor.append(self.textToDisplay)
# set up the layout
# set up the layout
vBox = QtGui.QVBoxLayout()
vBox = QtWidgets.QVBoxLayout()
vBox.addWidget(columnLabels)
vBox.addWidget(columnLabels)
vBox.addWidget(text_editor)
vBox.addWidget(text_editor)
self.setLayout(vBox)
self.setLayout(vBox)
# define window xLoc,yLoc,xDim,yDim
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( self.windowHome, self.windowHome, self.windowWidth, self.windowHeight)
self.setGeometry( self.windowHome, self.windowHome, self.windowWidth, self.windowHeight)
self.setWindowTitle("Object Dump of '" + FreeCADGui.ActiveDocument.Document.Label + "' at " + str(datetime.now()))
self.setWindowTitle("Object Dump of '" + FreeCADGui.ActiveDocument.Document.Label + "' at " + str(datetime.now()))
self.show()
self.show()
#----------------------------------------------------------------------
#----------------------------------------------------------------------
def onOk(self):
def onOk(self):
self.close()
self.close()


# Class definitions
# Class definitions
Line 178: Line 211:


def countObjects():
def countObjects():
printList = list()
printList = list()
objectTypeTable = {}
objectTypeTable = {}
# build up dictionary of different classes and keep a count
# build up dictionary of different classes and keep a count
for obj in FreeCAD.ActiveDocument.Objects:
for obj in FreeCAD.ActiveDocument.Objects:
if objectTypeTable.has_key(obj.TypeId):
if objectTypeTable.__contains__(obj.TypeId):
objectTypeTable[obj.TypeId] = objectTypeTable[obj.TypeId]+1
objectTypeTable[obj.TypeId] = objectTypeTable[obj.TypeId]+1
else:
else:
objectTypeTable[obj.TypeId] = 1
objectTypeTable[obj.TypeId] = 1
wb = obj.TypeId[0:obj.TypeId.find("::")]
wb = obj.TypeId[0:obj.TypeId.find("::")]
shape = obj.TypeId[obj.TypeId.find("::")+2:]
shape = obj.TypeId[obj.TypeId.find("::")+2:]
#print wb + "---" + shape
#print wb + "---" + shape
placementString = ""
placementString = ""
if obj.TypeId == "Sketcher::SketchObject":
if obj.TypeId == "Sketcher::SketchObject":
printList.append(formatPrintLine("Sketch", "", str(obj.Label)))
printList.append(formatPrintLine("Sketch", "", str(obj.Label)))
if showSketcherSegmentsFlag:
if showSketcherSegmentsFlag:
for i in obj.Geometry:
for i in obj.Geometry:
printList.append(formatPrintLine("", " -segment", str(i)))
printList.append(formatPrintLine("", " -segment", str(i)))
elif wb == "Part":
elif wb == "Part":
if showPlacementFlag:
if showPlacementFlag:
placementString = str(obj.Placement)
placementString = str(obj.Placement)
if shape in ("Cylinder", "Cut", "Box", "Fuse", "Loft", "Feature", "FeaturePython", "Part2DObjectPython"):
if shape in ("Cylinder", "Cut", "Box", "Fuse", "Loft", "Feature", "FeaturePython", "Part2DObjectPython"):
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
else: # print shapes not in list above
else: # print shapes not in list above
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
elif wb == "PartDesign":
elif wb == "PartDesign":
if showPlacementFlag:
if showPlacementFlag:
placementString = str(obj.Placement)
placementString = str(obj.Placement)
if shape in ("Pad", "Feature", "Fillet", "Part2DObjectPython"):
if shape in ("Pad", "Feature", "Fillet", "Part2DObjectPython"):
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
else: # print shapes not in list above
else: # print shapes not in list above
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
elif obj.TypeId == "App::DocumentObjectGroup":
elif obj.TypeId == "App::DocumentObjectGroup":
printList.append(formatPrintLine("Group", "", str(obj.Label)))
printList.append(formatPrintLine("Group", "", str(obj.Label)))
elif obj.TypeId == "Image::ImagePlane":
elif obj.TypeId == "Image::ImagePlane":
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
else: # print workbench shapes not in lists above
else: # print workbench shapes not in lists above
printList.append(formatPrintLine(str(obj.TypeId), str(obj.Label), str(obj.Name)))
printList.append(formatPrintLine(str(obj.TypeId), str(obj.Label), str(obj.Name)))
if showPlacementFlag and len(placementString)!=0:
if showPlacementFlag and len(placementString)!=0:
printList.append(formatPrintLineMax("", " -placement", placementString))
printList.append(formatPrintLineMax("", " -placement", placementString))


printList.append("")
printList.append("")
printList.append(summarySeparator)
printList.append(summarySeparator)
from collections import OrderedDict
from collections import OrderedDict
sortedByTags = OrderedDict(sorted(objectTypeTable.items(), key=lambda x: x[1], reverse=True))
sortedByTags = OrderedDict(sorted(objectTypeTable.items(), key=lambda x: x[1], reverse=True))
for k, v in sortedByTags.items():
for k, v in sortedByTags.items():
printList.append(formatPrintLineSum(k,v))
printList.append(formatPrintLineSum(k,v))
printList.append("")
printList.append("")
objectClassCount = 0; objectTotalCount = 0
objectClassCount = 0; objectTotalCount = 0
for i in objectTypeTable:
for i in objectTypeTable:
objectTotalCount = objectTotalCount + objectTypeTable[i]
objectTotalCount = objectTotalCount + objectTypeTable[i]
objectCLassCount = len(objectTypeTable)
objectCLassCount = len(objectTypeTable)
printList.append(formatPrintLineSum("Object Class Total is ", str(objectCLassCount)))
printList.append(formatPrintLineSum("Object Class Total is ", str(objectCLassCount)))
printList.append(formatPrintLineSum("Object Total is ", str(objectTotalCount)))
printList.append(formatPrintLineSum("Object Total is ", str(objectTotalCount)))
printList.append(summarySeparator)
printList.append(summarySeparator)
return printList
return printList


def formatPrintLineSum(a,b):
def formatPrintLineSum(a,b):
return printLineFormatter(2, a, str(b), "", "")
return printLineFormatter(2, a, str(b), "", "")


def formatPrintLineMax(a,b,c):
def formatPrintLineMax(a,b,c):
return printLineFormatter(1, a, b, "", "")
return printLineFormatter(1, a, b, "", "")


def formatPrintLine(a,b,c, *args):
def formatPrintLine(a,b,c, *args):
d = ""
d = ""
if len(args)==1:
if len(args)==1:
d = args[0]
d = args[0]
return printLineFormatter(0, a, b, c, d)
return printLineFormatter(0, a, b, c, d)


def printLineFormatter(flag,a,b,c,d):
def printLineFormatter(flag,a,b,c,d):
# flag = 0 standard print, spread values over 4 columns
# flag = 0 standard print, spread values over 4 columns
# flag = 1 printing verbose things like Sketch details or Placements, combine columns 3 & 4
# flag = 1 printing verbose things like Sketch details or Placements, combine columns 3 & 4
# flag = 2 printing the summary lines, combine columns 1 & 2
# flag = 2 printing the summary lines, combine columns 1 & 2
suffix = ""
suffix = ""
if csvFlag:
if csvFlag:
pfs2 = printFormatString2csv
pfs2 = printFormatString2csv
pfs3 = printFormatString3csv
pfs3 = printFormatString3csv
pfs4 = printFormatString4csv
pfs4 = printFormatString4csv
else:
else:
pfs2 = printFormatString2
pfs2 = printFormatString2
pfs3 = printFormatString3
pfs3 = printFormatString3
pfs4 = printFormatString4
pfs4 = printFormatString4
if flag==0:
if flag==0:
aa = a[:f1]
aa = a[:f1]
bb = b[:f2]
bb = b[:f2]
cc = c[:f3]
cc = c[:f3]
dd = d[:f4]
dd = d[:f4]
return pfs4.format(aa,bb,cc,dd)
return pfs4.format(aa,bb,cc,dd)
elif flag==1:
elif flag==1:
aa = a[:f1]
aa = a[:f1]
bb = b[:f2]
bb = b[:f2]
cc = c[:f3+f4]
cc = c[:f3+f4]
dd = d[:f4]
dd = d[:f4]
return pfs3.format(aa,bb,cc)
return pfs3.format(aa,bb,cc)
else:
else:
print(a)
aa = a[:f1+f2]
bb = b[:f3+f4]
aa = a[:f1+f2]
bb = b[:f3+f4]
return pfs2.format(aa,bb)
return pfs2.format(aa,bb)


# Constant definitions
# Constant definitions
# set some field widths
# set some field widths
screenWidth = QtGui.QDesktopWidget().screenGeometry().width()
screenWidth = QtWidgets.QDesktopWidget().screenGeometry().width()
global f1, f2, f3, f4
global f1, f2, f3, f4
# f1 = 15; f2 = 25; f3 = 45; f4 = 25 # 110 columns in 1000 pixels
# f1 = 15; f2 = 25; f3 = 45; f4 = 25 # 110 columns in 1000 pixels
f1 = 15*screenWidth/1000; f2 = 25*screenWidth/1000; f3 = 45*screenWidth/1000; f4 = 25*screenWidth/1000
f1 = math.floor(15*screenWidth/1000); f2 = math.floor(25*screenWidth/1000); f3 = math.floor(45*screenWidth/1000); f4 = math.floor(25*screenWidth/1000)
# and some print format strings
# and some print format strings
global printFormatString2, printFormatString3, printFormatString4
global printFormatString2, printFormatString3, printFormatString4
Line 299: Line 333:
# code ***********************************************************************************
# code ***********************************************************************************
if FreeCAD.ActiveDocument != None:
if FreeCAD.ActiveDocument != None:
# ask if to window or to Report View...
# ask if to window or to Report View...
form = configureMacro()
form = configureMacro()
form.exec_()
form.exec_()
showSketcherSegmentsFlag = False
showSketcherSegmentsFlag = False
if form.cbss.isChecked():
if form.cbss.isChecked():
showSketcherSegmentsFlag = True
showSketcherSegmentsFlag = True
showPlacementFlag = False
showPlacementFlag = False
if form.cbp.isChecked():
if form.cbp.isChecked():
showPlacementFlag = True
showPlacementFlag = True
if form.result == choice2:
if form.result == choice2:
csvFlag = True
csvFlag = True
showSketcherSegmentsFlag = False
showSketcherSegmentsFlag = False
showPlacementFlag = False
showPlacementFlag = False
summarySeparator = summarySeparatorCsv
summarySeparator = summarySeparatorCsv
printList = countObjects()
printList = countObjects()
if form.result == choice1: # report to Report View
if form.result == choice1: # report to Report View
mainWindow = FreeCADGui.getMainWindow()
mainWindow = FreeCADGui.getMainWindow()
dockWidgets = mainWindow.findChildren(QtGui.QDockWidget)
dockWidgets = mainWindow.findChildren(QtWidgets.QDockWidget)
reportViewFlag = False
reportViewFlag = False
for dw in dockWidgets:
for dw in dockWidgets:
if dw.objectName() == "Report view":
if dw.objectName() == "Report view":
reportViewFlag = True
reportViewFlag = True
if reportViewFlag:
if reportViewFlag:
print printFormatString4.format( "", "", "(User Supplied)", "")
print( printFormatString4.format( "", "", "(User Supplied)", ""))
print printFormatString4.format( "Type", "Shape", "Label", "Name")
print( printFormatString4.format( "Type", "Shape", "Label", "Name"))
print ""
print( "")
for line in printList:
for line in printList:
print line + "\n"
print( line + "\n")
else:
else:
QtGui.QMessageBox.information(None,"","Please use 'Menu->View->Views->Report view' to open the 'Report view'")
QtWidgets.QMessageBox.information(None,"","Please use 'Menu->View->Views->Report view' to open the 'Report view'")
if form.result == choice2: # report to CSV file
if form.result == choice2: # report to CSV file
filePath = QtGui.QFileDialog.getSaveFileName(parent=None,caption="Save CSV file as",dir=expanduser("~"),filter="*.csv")
filePath = QtWidgets.QFileDialog.getSaveFileName(parent=None,caption="Save CSV file as",dir=expanduser("~"),filter="*.csv")
file = open(filePath[0],"w")
file = open(filePath[0],"w")
for line in printList:
for line in printList:
file.write(line + "\n")
file.write(line + "\n")
file.close()
file.close()
if form.result == choice3: # report to window
if form.result == choice3: # report to window
#----------------------------------------------------------------------
#----------------------------------------------------------------------
longPrintLine = ""
longPrintLine = ""
for line in printList:
for line in printList:
longPrintLine = longPrintLine + line + "\n"
longPrintLine = longPrintLine + line + "\n"
form = DisplayText(longPrintLine)
form = DisplayText(longPrintLine)
#
#
#OS: Mac OS X
#OS: Windows 10 Version 2009
#Word size: 64-bit
#Word size of FreeCAD: 64-bit
#Version: 0.14.3703 (Git)
#Version: 0.21.0.31513 (Git)
#Build type: Release
#Branch: releases/FreeCAD-0-14
#Branch: master
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
#Hash: b2ab8edba4bfd71681e639f8c3f1105066bed4c7
#Python version: 2.7.5
#Python 3.10.8, Qt 5.15.4, Coin 4.0.0, Vtk 9.1.0, OCC 7.6.3
#Qt version: 4.8.6
#Coin version: 3.1.3
#SoQt version: 1.5.0
#OCC version: 6.7.0
#
#
#thus ends the macro...
#thus ends the macro...

}}
}}
{{clear}}
{{clear}}
<languages/>

Latest revision as of 17:40, 14 January 2023

Other languages:

Macro Dump Objects

Description
This macro generates a listing of all objects in the current document - the list can be in a window or on the Report view.

Macro version: 0.3
Last modified: 2023-01-14
FreeCAD version: 0.20
Download: ToolBar Icon
Author: Piffpoof
Author
Piffpoof
Download
ToolBar Icon
Links
Macro Version
0.3
Date last modified
2023-01-14
FreeCAD Version(s)
0.20
Default shortcut
None
See also
None

When developing complex object models it is easy to lose track of exactly which objects are present as some may be hidden, obscured or transparent. Additionally with a large number of objects a naming system becomes necessary to keep track of the objects.

Description

The Dump Object code takes the current document and enumerates all the objects. A report is then generated listing each object, then a summary giving the total number of instances of each Class, followed by the total number of Classes and finally the total number of objects. The output may be directed to the Report view or to a window. The window is non-modal and will stay open until closed by the user. Each window has the time of the object dump in it's title bar, so the contents of multiple windows can be compared, say before and after a piece of code running.

The default operation lists all objects, optionally the placement of each object can be listed. Also for Sketches, each segment of the Geometry can be listed.

Installation

All the code for dumpObject.FCMacro is in one macro. So installation is comprised of copying the code to the appropriate Macro directory and invoking dumpObject from the Macro menu. Alternatively it may be run from the console.

Usage

Select the document you wish to dump objects for, then start the macro from one of:

  • the Macro menu
  • from the Python console
  • from a Toolbar

Depending on the parameters selected in the first window, the report will be displayed on the Report view or in a window. The information will show all objects in the current document. Some of the benefits to be expected are the detection of:

  • irregularities in object names (e.g. spelling errors or default names generated by FreeCAD)
  • duplicate objects
  • objects with duplicate names (where FreeCAD has had to make the second object name unique)
  • unexpected objects
  • unexpected object Placements (when the Show Positions option is selected)
  • unexpected segments in the Sketch Geometry (when the Show Sketcher Segments option is selected)

User Interface

The first window will take input which configures the Object Dump:

The second window will be the report on the objects in the current document:

Options

  • output may be directed to one of:
    • the Report view
    • a non-modal window
  • segments in the Geometry for each Sketch may be listed
  • Placement specifics may be listed for objects

Remarks

Although tested with many object types in FreeCAD, there probably are some objects that it does not expect, in that case it should list them generically.

Script

Toolbar Icon

Macro_Dump_Objects.FCMacro

#
#                       Dump Object
#                       v 0.3 - 14/01/2023 conversion to PySide2
#                       v 0.2 - added report to CSV file
#                       v 0.1 - added report to window
#                       v 0.0 - report to Report view
#
#***********************************************************************************
# routine to dump object space for Geometric model in the currently active file
#
__title__   = "Dump_Objects"
__author__  = "Piffpoof"
__url__     = "https://wiki.freecadweb.org/Macro_Dump_Objects"
__version__ = "0.3"
__date__    = "2023/01/14"    #YYYY/MM/DD
#

# import statements
from PySide2 import QtWidgets, QtGui, QtCore
from PySide2.QtWidgets import * #QDialog , QLineEdit, QPushButton, QApplication
from datetime import datetime       # datestamp on output window
from os.path import expanduser      # output directory for CSV
import math
# UI Class definitions

class configureMacro(QtWidgets.QDialog):
    """"""
    def __init__(self):
        super(configureMacro, self).__init__()
        self.initUI()
    def initUI(self):
        self.result         = None
        # set up display only field for selected path type
        self.cbss           = QtWidgets.QCheckBox("Show Sketcher Segments?", self)
        self.cbss.move(20,20)
        self.cbp            = QtWidgets.QCheckBox("Show Positions?", self)
        self.cbp.move(220,20)
        self.pathTypeLbl    = QtWidgets.QLabel("Select Report Destination:", self)
        self.pathTypeLbl.move(20, 70)
        # cancel button
        cancelButton = QtWidgets.QPushButton('Cancel', self)
        cancelButton.clicked.connect(self.onCancel)
        cancelButton.move(10, 100)
        # button #1
        button1 = QtWidgets.QPushButton(choice1, self)
        button1.clicked.connect(self.onBtn1)
        button1.move(120, 100)
        # button #2
        button2 = QtWidgets.QPushButton(choice2, self)
        button2.clicked.connect(self.onBtn2)
        button2.move(235, 100)
        # button #3
        button3 = QtWidgets.QPushButton(choice3, self)
        button3.clicked.connect(self.onBtn3)
        button3.move(327, 100)
        # define window     xLoc,yLoc,xDim,yDim
        self.setGeometry(   250, 250, 435, 150)
        self.setWindowTitle("Select a Report Destination")
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        self.show()
    def onCancel(self):
        self.result = "cancelled"
        self.close()
    def onBtn1(self):
        self.result = choice1
        self.close()
    def onBtn2(self):
        self.result = choice2
        self.close()
    def onBtn3(self):
        self.result = choice3
        self.close()

class DisplayText(QtWidgets.QWidget):
    """"""
    def __init__(self, textToDisplay):
        self.text = textToDisplay
        super(DisplayText, self).__init__()
        self.initUI(textToDisplay)
    def initUI(self, textToDisplay):
        """Constructor"""
        self.textToDisplay = textToDisplay
        # some window dimensions
        self.windowHome = screenWidth * 0.05
        self.windowWidth = screenWidth * 0.9
        self.windowHeight = 400
        self.fieldMargin = 40
        # some column titles
        columnLabels = QtWidgets.QLabel(formatPrintLine("Type / WB","Shape","User Supplied Label","Name"))
        columnLabels.setFont('Courier')
        # set up text editing widget
        text_editor = QtWidgets.QTextEdit(self)
        #self.setCentralWidget(self.text_editor)
        text_editor.setFont('Courier')
        text_editor.setLineWrapMode(QtWidgets.QTextEdit.NoWrap)
        text_editor.move(self.fieldMargin,self.fieldMargin)
        text_editor.move(0,self.fieldMargin)
        text_editor.resize(self.windowWidth-(2*self.fieldMargin),self.windowHeight-(2*self.fieldMargin))
        text_editor.resize(self.windowWidth,self.windowHeight-(2*self.fieldMargin))
        text_editor.append(self.textToDisplay)
        # set up the layout
        vBox = QtWidgets.QVBoxLayout()
        vBox.addWidget(columnLabels)
        vBox.addWidget(text_editor)
        self.setLayout(vBox)
        # define window     xLoc,yLoc,xDim,yDim
        self.setGeometry(   self.windowHome, self.windowHome, self.windowWidth, self.windowHeight)
        self.setWindowTitle("Object Dump of '" + FreeCADGui.ActiveDocument.Document.Label + "' at " + str(datetime.now()))
        self.show()
    #----------------------------------------------------------------------
    def onOk(self):
        self.close()

# Class definitions

# Function Definitions

def countObjects():
    printList = list()
    objectTypeTable = {}
    # build up dictionary of different classes and keep a count
    for obj in FreeCAD.ActiveDocument.Objects:
        if objectTypeTable.__contains__(obj.TypeId):
            objectTypeTable[obj.TypeId] = objectTypeTable[obj.TypeId]+1
        else:
            objectTypeTable[obj.TypeId] = 1
        wb = obj.TypeId[0:obj.TypeId.find("::")]
        shape = obj.TypeId[obj.TypeId.find("::")+2:]
        #print wb + "---" + shape
        placementString = ""
        if obj.TypeId == "Sketcher::SketchObject":
            printList.append(formatPrintLine("Sketch", "", str(obj.Label)))
            if showSketcherSegmentsFlag:
                for i in obj.Geometry:
                    printList.append(formatPrintLine("", " -segment", str(i)))
        elif wb == "Part":
            if showPlacementFlag:
                placementString = str(obj.Placement)
            if shape in ("Cylinder", "Cut", "Box", "Fuse", "Loft", "Feature", "FeaturePython", "Part2DObjectPython"):
                printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
            else: # print shapes not in list above
                printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
        elif wb == "PartDesign":
            if showPlacementFlag:
                placementString = str(obj.Placement)
            if shape in ("Pad", "Feature", "Fillet", "Part2DObjectPython"):
                printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))
            else: # print shapes not in list above
                printList.append(formatPrintLine(str(obj.TypeId), "", str(obj.Label), str(obj.Name)))
        elif obj.TypeId == "App::DocumentObjectGroup":
            printList.append(formatPrintLine("Group", "", str(obj.Label)))
        elif obj.TypeId == "Image::ImagePlane":
            printList.append(formatPrintLine(wb, shape, str(obj.Label), str(obj.Name)))     
        else: # print workbench shapes not in lists above
            printList.append(formatPrintLine(str(obj.TypeId), str(obj.Label), str(obj.Name)))
        if showPlacementFlag and len(placementString)!=0:
            printList.append(formatPrintLineMax("", " -placement", placementString))

    printList.append("")
    printList.append(summarySeparator)
    from collections import OrderedDict
    sortedByTags = OrderedDict(sorted(objectTypeTable.items(), key=lambda x: x[1], reverse=True))
    for k, v in sortedByTags.items():
        printList.append(formatPrintLineSum(k,v))
    printList.append("")
    objectClassCount = 0; objectTotalCount = 0
    for i in objectTypeTable:
        objectTotalCount = objectTotalCount + objectTypeTable[i]
    objectCLassCount = len(objectTypeTable)
    printList.append(formatPrintLineSum("Object Class Total is ", str(objectCLassCount)))
    printList.append(formatPrintLineSum("Object Total is ", str(objectTotalCount)))
    printList.append(summarySeparator)
    return printList

def formatPrintLineSum(a,b):
    return printLineFormatter(2, a, str(b), "", "")

def formatPrintLineMax(a,b,c):
    return printLineFormatter(1, a, b, "", "")

def formatPrintLine(a,b,c, *args):
    d = ""
    if len(args)==1:
        d = args[0]
    return printLineFormatter(0, a, b, c, d)

def printLineFormatter(flag,a,b,c,d):
    # flag = 0  standard print, spread values over 4 columns
    # flag = 1  printing verbose things like Sketch details or Placements, combine columns 3 & 4
    # flag = 2  printing the summary lines, combine columns 1 & 2
    suffix = ""
    if csvFlag:
        pfs2 = printFormatString2csv
        pfs3 = printFormatString3csv
        pfs4 = printFormatString4csv
    else:
        pfs2 = printFormatString2
        pfs3 = printFormatString3
        pfs4 = printFormatString4
    if flag==0:
        aa = a[:f1]
        bb = b[:f2]
        cc = c[:f3]
        dd = d[:f4]
        return pfs4.format(aa,bb,cc,dd)
    elif flag==1:
        aa = a[:f1]
        bb = b[:f2]
        cc = c[:f3+f4]
        dd = d[:f4]
        return pfs3.format(aa,bb,cc)
    else:
        print(a)
        aa = a[:f1+f2]
        bb = b[:f3+f4]
        return pfs2.format(aa,bb)

# Constant definitions
# set some field widths
screenWidth = QtWidgets.QDesktopWidget().screenGeometry().width()
global f1, f2, f3, f4
# f1 = 15; f2 = 25; f3 = 45; f4 = 25 # 110 columns in 1000 pixels
f1 = math.floor(15*screenWidth/1000); f2 = math.floor(25*screenWidth/1000); f3 = math.floor(45*screenWidth/1000); f4 = math.floor(25*screenWidth/1000)
# and some print format strings
global printFormatString2, printFormatString3, printFormatString4
global printFormatString2csv, printFormatString3csv, printFormatString4csv
printFormatString2 = "{0:<"+str(f1+f2)+"} {1:<"+str(f3)+"}"
printFormatString2csv = "{0}, {1}"
printFormatString3 = "{0:<"+str(f1)+"} {1:<"+str(f2)+"} {2:<"+str(f3+f4)+"}"
printFormatString3csv = "{0}, {1}, {2}"
printFormatString4 = "{0:<"+str(f1)+"} {1:<"+str(f2)+"} {2:<"+str(f3)+"} {3:<"+str(f4)+"}"
printFormatString4csv = "{0}, {1}, {2}, {3}"
# some button labels that are checked in the code
global choice1, choice2, choice3, csvFlag
choice1 = "Report View"; choice2 = "CSV File"; choice3 = "Window"
csvFlag = False
summarySeparator = "======================================================="
summarySeparatorCsv = "-------------------------------------------------------"
# code ***********************************************************************************
if FreeCAD.ActiveDocument != None:
    # ask if to window or to Report View...
    form = configureMacro()
    form.exec_()
    showSketcherSegmentsFlag = False
    if form.cbss.isChecked():
        showSketcherSegmentsFlag = True
    showPlacementFlag = False
    if form.cbp.isChecked():
        showPlacementFlag = True
    if form.result == choice2:
        csvFlag = True
        showSketcherSegmentsFlag = False
        showPlacementFlag = False
        summarySeparator = summarySeparatorCsv
    printList = countObjects()
    if form.result == choice1: # report to Report View
        mainWindow = FreeCADGui.getMainWindow()
        dockWidgets = mainWindow.findChildren(QtWidgets.QDockWidget)
        reportViewFlag = False
        for dw in dockWidgets:
            if dw.objectName() == "Report view":
                reportViewFlag = True
        if reportViewFlag:
            print( printFormatString4.format(   "", "", "(User Supplied)", ""))
            print( printFormatString4.format(   "Type", "Shape", "Label", "Name"))
            print( "")
            for line in printList:
                print( line + "\n")
        else:
            QtWidgets.QMessageBox.information(None,"","Please use 'Menu->View->Views->Report view' to open the 'Report view'")
    if form.result == choice2: # report to CSV file
        filePath = QtWidgets.QFileDialog.getSaveFileName(parent=None,caption="Save CSV file as",dir=expanduser("~"),filter="*.csv")
        file = open(filePath[0],"w")
        for line in printList:
            file.write(line + "\n")
        file.close()
    if form.result == choice3: # report to window
        #----------------------------------------------------------------------
        longPrintLine = ""
        for line in printList:
            longPrintLine = longPrintLine + line + "\n"
        form = DisplayText(longPrintLine)
#
#OS: Windows 10 Version 2009
#Word size of FreeCAD: 64-bit
#Version: 0.21.0.31513 (Git)
#Build type: Release
#Branch: master
#Hash: b2ab8edba4bfd71681e639f8c3f1105066bed4c7
#Python 3.10.8, Qt 5.15.4, Coin 4.0.0, Vtk 9.1.0, OCC 7.6.3
#
#thus ends the macro...