Support macros and console logs in Assembly

This commit is contained in:
bgbsww
2024-09-19 20:35:46 -04:00
committed by Chris Hennes
parent 3395a8d4a7
commit 48c65aed76
14 changed files with 212 additions and 37 deletions

View File

@@ -56,6 +56,18 @@ For a temporary document it returns its transient directory.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getUniqueObjectName">
<Documentation>
<UserDocu>getUniqueObjectName(objName) -> objName
Return the same name, or the name made unique, for Example Box -> Box002 if there are conflicting name
already in the document.
ObjName : str
Object name.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="mergeProject">
<Documentation>
<UserDocu>Merges this document with another project file</UserDocu>

View File

@@ -217,6 +217,18 @@ PyObject* DocumentPy::getFileName(PyObject* args)
return Py::new_reference_to(Py::String(fn));
}
PyObject* DocumentPy::getUniqueObjectName(PyObject *args)
{
char *sName;
if (!PyArg_ParseTuple(args, "s", &sName))
return nullptr;
PY_TRY {
auto newName = getDocumentPtr()->getUniqueObjectName(sName);
return Py::new_reference_to(Py::String(newName));
}
PY_CATCH;
}
PyObject* DocumentPy::mergeProject(PyObject * args)
{
char* filename;

View File

@@ -159,7 +159,7 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn
}
if(geoFeature)
*geoFeature = geo;
if(!obj || (filter && geo!=filter))
if(filter && geo!=filter)
return nullptr;
if(!element || !element[0]) {
if(append)

View File

@@ -28,8 +28,6 @@
#include <map>
#include <string>
#define putpix()
#include <App/Application.h>
class QCloseEvent;
@@ -340,6 +338,8 @@ public:
static PyObject* sDoCommand (PyObject *self,PyObject *args);
static PyObject* sDoCommandGui (PyObject *self,PyObject *args);
static PyObject* sDoCommandEval (PyObject *self,PyObject *args);
static PyObject* sDoCommandSkip (PyObject *self,PyObject *args);
static PyObject* sAddModule (PyObject *self,PyObject *args);
static PyObject* sShowDownloads (PyObject *self,PyObject *args);

View File

@@ -316,6 +316,19 @@ PyMethodDef Application::Methods[] = {
"but doesn't record it in macros.\n"
"\n"
"cmd : str"},
{"doCommandEval", (PyCFunction) Application::sDoCommandEval, METH_VARARGS,
"doCommandEval(cmd) -> PyObject\n"
"\n"
"Runs the given string without showing in the python console or recording in\n"
"macros, and returns the result.\n"
"\n"
"cmd : str"},
{"doCommandSkip", (PyCFunction) Application::sDoCommandSkip, METH_VARARGS,
"doCommandSkip(cmd) -> None\n"
"\n"
"Record the given string in the Macro but comment it out in the console\n"
"\n"
"cmd : str"},
{"addModule", (PyCFunction) Application::sAddModule, METH_VARARGS,
"addModule(mod) -> None\n"
"\n"
@@ -1352,6 +1365,43 @@ PyObject* Application::sDoCommandGui(PyObject * /*self*/, PyObject *args)
return PyRun_String(sCmd, Py_file_input, dict, dict);
}
PyObject* Application::sDoCommandEval(PyObject * /*self*/, PyObject *args)
{
char *sCmd = nullptr;
if (!PyArg_ParseTuple(args, "s", &sCmd))
return nullptr;
Gui::Command::LogDisabler d1;
Gui::SelectionLogDisabler d2;
PyObject *module, *dict;
Base::PyGILStateLocker locker;
module = PyImport_AddModule("__main__");
if (!module)
return nullptr;
dict = PyModule_GetDict(module);
if (!dict)
return nullptr;
return PyRun_String(sCmd, Py_eval_input, dict, dict);
}
PyObject* Application::sDoCommandSkip(PyObject * /*self*/, PyObject *args)
{
char *sCmd = nullptr;
if (!PyArg_ParseTuple(args, "s", &sCmd))
return nullptr;
Gui::Command::LogDisabler d1;
Gui::SelectionLogDisabler d2;
Gui::Command::printPyCaller();
Gui::Application::Instance->macroManager()->addLine(MacroManager::App, sCmd);
return Py::None().ptr();
}
PyObject* Application::sAddModule(PyObject * /*self*/, PyObject *args)
{
char *pstr;

View File

@@ -70,15 +70,24 @@ class CommandCreateAssembly:
App.setActiveTransaction("Create assembly")
activeAssembly = UtilsAssembly.activeAssembly()
Gui.addModule("UtilsAssembly")
if activeAssembly:
assembly = activeAssembly.newObject("Assembly::AssemblyObject", "Assembly")
commands = (
"activeAssembly = UtilsAssembly.activeAssembly()\n"
'assembly = activeAssembly.newObject("Assembly::AssemblyObject", "Assembly")\n'
)
else:
assembly = App.ActiveDocument.addObject("Assembly::AssemblyObject", "Assembly")
commands = (
'assembly = App.ActiveDocument.addObject("Assembly::AssemblyObject", "Assembly")\n'
)
assembly.Type = "Assembly"
commands = commands + 'assembly.Type = "Assembly"\n'
commands = commands + 'assembly.newObject("Assembly::JointGroup", "Joints")'
Gui.doCommand(commands)
if not activeAssembly:
Gui.ActiveDocument.setEdit(assembly)
assembly.newObject("Assembly::JointGroup", "Joints")
Gui.doCommandGui("Gui.ActiveDocument.setEdit(assembly)")
App.closeActiveTransaction()

View File

@@ -324,11 +324,16 @@ class TaskAssemblyCreateBom(QtCore.QObject):
def createBomObject(self):
assembly = UtilsAssembly.activeAssembly()
Gui.addModule("UtilsAssembly")
if assembly is not None:
bom_group = UtilsAssembly.getBomGroup(assembly)
self.bomObj = bom_group.newObject("Assembly::BomObject", "Bill of Materials")
commands = (
"bom_group = UtilsAssembly.getBomGroup(assembly)\n"
'bomObj = bom_group.newObject("Assembly::BomObject", "Bill of Materials")'
)
else:
self.bomObj = App.activeDocument().addObject("Assembly::BomObject", "Bill of Materials")
commands = 'bomObj = App.activeDocument().addObject("Assembly::BomObject", "Bill of Materials")'
Gui.doCommand(commands)
self.bomObj = Gui.doCommandEval("bomObj")
def export(self):
self.bomObj.recompute()

View File

@@ -58,8 +58,10 @@ def activateJoint(index):
if JointObject.activeTask:
JointObject.activeTask.reject()
panel = TaskAssemblyCreateJoint(index)
dialog = Gui.Control.showDialog(panel)
Gui.addModule("JointObject") # NOLINT
Gui.doCommand(f"panel = JointObject.TaskAssemblyCreateJoint({index})")
Gui.doCommandGui("dialog = Gui.Control.showDialog(panel)")
dialog = Gui.doCommandEval("dialog")
if dialog is not None:
dialog.setAutoCloseOnTransactionChange(True)
dialog.setDocumentName(App.ActiveDocument.Name)
@@ -476,16 +478,21 @@ class CommandGroupGearBelt:
def createGroundedJoint(obj):
assembly = UtilsAssembly.activeAssembly()
if not assembly:
if not UtilsAssembly.activeAssembly():
return
joint_group = UtilsAssembly.getJointGroup(assembly)
ground = joint_group.newObject("App::FeaturePython", "GroundedJoint")
JointObject.GroundedJoint(ground, obj)
JointObject.ViewProviderGroundedJoint(ground.ViewObject)
return ground
Gui.addModule("UtilsAssembly")
Gui.addModule("JointObject")
commands = (
f'obj = App.ActiveDocument.getObject("{obj.Name}")\n'
"assembly = UtilsAssembly.activeAssembly()\n"
"joint_group = UtilsAssembly.getJointGroup(assembly)\n"
'ground = joint_group.newObject("App::FeaturePython", "GroundedJoint")\n'
"JointObject.GroundedJoint(ground, obj)"
)
Gui.doCommand(commands)
Gui.doCommandGui("JointObject.ViewProviderGroundedJoint(ground.ViewObject)")
return Gui.doCommandEval("ground")
class CommandToggleGrounded:
@@ -540,9 +547,12 @@ class CommandToggleGrounded:
ungrounded = False
for joint in joint_group.Group:
if hasattr(joint, "ObjectToGround") and joint.ObjectToGround == moving_part:
doc = App.ActiveDocument
doc.removeObject(joint.Name)
doc.recompute()
commands = (
"doc = App.ActiveDocument\n"
f'doc.removeObject("{joint.Name}")\n'
"doc.recompute()\n"
)
Gui.doCommand(commands)
ungrounded = True
break
if ungrounded:

View File

@@ -74,8 +74,10 @@ class CommandCreateView:
if not assembly:
return
self.panel = TaskAssemblyCreateView()
Gui.Control.showDialog(self.panel)
Gui.addModule("CommandCreateView") # NOLINT
Gui.doCommand("panel = CommandCreateView.TaskAssemblyCreateView()")
self.panel = Gui.doCommandEval("panel")
Gui.doCommandGui("Gui.Control.showDialog(panel)")
######### Exploded View Object ###########
@@ -524,6 +526,11 @@ class TaskAssemblyCreateView(QtCore.QObject):
UtilsAssembly.restoreAssemblyPartsPlacements(self.assembly, self.initialPlcs)
for move in self.viewObj.Moves:
move.Visibility = False
commands = f'obj = App.ActiveDocument.getObject("{self.viewObj.Name}")\n'
for move in self.viewObj.Moves:
more = UtilsAssembly.generatePropertySettings("obj.Moves[0]", move)
commands = commands + more
Gui.doCommand(commands[:-1]) # Don't use the last \n
App.closeActiveTransaction()
return True
@@ -695,16 +702,27 @@ class TaskAssemblyCreateView(QtCore.QObject):
self.blockDraggerMove = False
def createExplodedViewObject(self):
view_group = UtilsAssembly.getViewGroup(self.assembly)
self.viewObj = view_group.newObject("App::FeaturePython", "Exploded View")
ExplodedView(self.viewObj)
ViewProviderExplodedView(self.viewObj.ViewObject)
Gui.addModule("UtilsAssembly")
commands = (
f'assembly = App.ActiveDocument.getObject("{self.assembly.Name}")\n'
"view_group = UtilsAssembly.getViewGroup(assembly)\n"
'viewObj = view_group.newObject("App::FeaturePython", "Exploded View")\n'
"CommandCreateView.ExplodedView(viewObj)"
)
Gui.doCommand(commands)
self.viewObj = Gui.doCommandEval("viewObj")
Gui.doCommandGui("CommandCreateView.ViewProviderExplodedView(viewObj.ViewObject)")
def createExplodedStepObject(self, moveType_index=0):
self.currentStep = self.assembly.newObject("App::FeaturePython", "Move")
ExplodedViewStep(self.currentStep, moveType_index)
ViewProviderExplodedViewStep(self.currentStep.ViewObject)
commands = (
f'assembly = App.ActiveDocument.getObject("{self.assembly.Name}")\n'
'currentStep = assembly.newObject("App::FeaturePython", "Move")\n'
f"CommandCreateView.ExplodedViewStep(currentStep, {moveType_index})"
)
Gui.doCommand(commands)
self.currentStep = Gui.doCommandEval("currentStep")
Gui.doCommandGui("CommandCreateView.ViewProviderExplodedViewStep(currentStep.ViewObject)")
self.currentStep.MovementTransform = App.Placement()
@@ -727,7 +745,7 @@ class TaskAssemblyCreateView(QtCore.QObject):
for obj, init_plc in zip(self.selectedObjs, self.selectedObjsInitPlc):
obj.Placement = init_plc
self.currentStep.Document.removeObject(self.currentStep.Name)
Gui.doCommand(f'App.ActiveDocument.removeObject("{self.currentStep.Name}")')
self.currentStep = None
Gui.Selection.clearSelection()

View File

@@ -74,7 +74,7 @@ class CommandExportASMT:
)
if filePath:
assembly.exportAsASMT(filePath)
Gui.doCommand(f'assembly.exportAsASMT("{filePath}")')
if App.GuiUp:

View File

@@ -80,7 +80,6 @@ class CommandInsertLink:
if not assembly:
return
view = Gui.activeDocument().activeView()
self.panel = TaskAssemblyInsertLink(assembly, view)
Gui.Control.showDialog(self.panel)
@@ -125,7 +124,32 @@ class TaskAssemblyInsertLink(QtCore.QObject):
# if self.partMoving:
# self.endMove()
Gui.addModule("UtilsAssembly")
commands = "assembly = UtilsAssembly.activeAssembly()\n"
for insertionItem in self.insertionStack:
object = insertionItem["addedObject"]
translation = insertionItem["translation"]
commands = commands + (
f'item = assembly.newObject("App::Link", "{object.Name}")\n'
f'item.LinkedObject = App.ActiveDocument.getObject("{object.LinkedObject.Name}")\n'
f'item.Label = "{object.Label}"\n'
)
if translation != App.Vector():
commands = commands + (
f"item.Placement.base = App.Vector({translation.x}."
f"{translation.y},"
f"{translation.z})\n"
)
# Ground the first item if that happened
if self.groundedObj:
commands = (
commands
+ f'CommandCreateJoint.createGroundedJoint(App.ActiveDocument.getObject("{self.groundedObj.Name}"))\n'
)
Gui.doCommandSkip(commands[:-1]) # Get rid of last \n
App.closeActiveTransaction()
return True

View File

@@ -67,8 +67,9 @@ class CommandSolveAssembly:
if not assembly:
return
Gui.addModule("UtilsAssembly")
App.setActiveTransaction("Solve assembly")
assembly.solve()
Gui.doCommand("UtilsAssembly.activeAssembly().solve()")
App.closeActiveTransaction()

View File

@@ -1417,6 +1417,9 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
else:
self.joint.Document.removeObject(self.joint.Name)
cmds = UtilsAssembly.generatePropertySettings("obj", self.joint)
Gui.doCommand(cmds)
App.closeActiveTransaction()
return True

View File

@@ -1170,3 +1170,34 @@ def getParentPlacementIfNeeded(part):
return linkGroup.Placement
return Base.Placement()
def generatePropertySettings(objectName, documentObject):
commands = []
if hasattr(documentObject, "Name"):
commands.append(f'{objectName} = App.ActiveDocument.getObject("{documentObject.Name}")')
for propertyName in documentObject.PropertiesList:
propertyValue = documentObject.getPropertyByName(propertyName)
propertyType = documentObject.getTypeIdOfProperty(propertyName)
# Note: OpenCascade precision is 1e-07, angular precision is 1e-05. For purposes of creating a Macro,
# we are forcing a reduction in precision so as to get round numbers like 0 instead of tiny near 0 values
if propertyType == "App::PropertyFloat":
commands.append(f"{objectName}.{propertyName} = {propertyValue:.5f}")
elif propertyType == "App::PropertyInt" or propertyType == "App::PropertyBool":
commands.append(f"{objectName}.{propertyName} = {propertyValue}")
elif propertyType == "App::PropertyString" or propertyType == "App::PropertyEnumeration":
commands.append(f'{objectName}.{propertyName} = "{propertyValue}"')
elif propertyType == "App::PropertyPlacement":
commands.append(
f"{objectName}.{propertyName} = App.Placement("
f"App.Vector({propertyValue.Base.x:.5f},{propertyValue.Base.y:.5f},{propertyValue.Base.z:.5f}),"
f"App.Rotation(*{[round(n,5) for n in propertyValue.Rotation.getYawPitchRoll()]}))"
)
elif propertyType == "App::PropertyXLinkSubHidden":
commands.append(
f'{objectName}.{propertyName} = [App.ActiveDocument.getObject("{propertyValue[0].Name}"), {propertyValue[1]}]'
)
else:
# print("Not processing properties of type ", propertyType)
pass
return "\n".join(commands) + "\n"