From bc8219cc36b4a6030bcbff6453efcfa6e9da0d12 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 23 Jan 2018 06:39:18 +0100 Subject: [PATCH] FEM: beam rotation object, add this new object --- src/Mod/Fem/CMakeLists.txt | 2 + src/Mod/Fem/Gui/CMakeLists.txt | 1 + .../Fem/Gui/Resources/ui/ElementRotation1D.ui | 143 +++++++++++ src/Mod/Fem/Gui/Workbench.cpp | 2 + src/Mod/Fem/ObjectsFem.py | 11 + .../_ViewProviderFemElementRotation1D.py | 228 ++++++++++++++++++ .../Fem/PyObjects/_FemElementRotation1D.py | 41 ++++ src/Mod/Fem/femcommands/commands.py | 17 ++ src/Mod/Fem/femtest/testfemcommon.py | 5 + 9 files changed, 450 insertions(+) create mode 100644 src/Mod/Fem/Gui/Resources/ui/ElementRotation1D.ui create mode 100644 src/Mod/Fem/PyGui/_ViewProviderFemElementRotation1D.py create mode 100644 src/Mod/Fem/PyObjects/_FemElementRotation1D.py diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index ee392eae7e..c2c4153712 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -167,6 +167,7 @@ SET(FemObjectsScripts_SRCS PyObjects/_FemElementFluid1D.py PyObjects/_FemElementGeometry1D.py PyObjects/_FemElementGeometry2D.py + PyObjects/_FemElementRotation1D.py PyObjects/_FemMaterial.py PyObjects/_FemMaterialMechanicalNonlinear.py PyObjects/_FemMeshBoundaryLayer.py @@ -238,6 +239,7 @@ SET(FemGuiScripts_SRCS PyGui/_ViewProviderFemElementFluid1D.py PyGui/_ViewProviderFemElementGeometry1D.py PyGui/_ViewProviderFemElementGeometry2D.py + PyGui/_ViewProviderFemElementRotation1D.py PyGui/_ViewProviderFemMaterial.py PyGui/_ViewProviderFemMaterialMechanicalNonlinear.py PyGui/_ViewProviderFemMeshBoundaryLayer.py diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt index 098bd9daee..2506626909 100755 --- a/src/Mod/Fem/Gui/CMakeLists.txt +++ b/src/Mod/Fem/Gui/CMakeLists.txt @@ -388,6 +388,7 @@ SET(FemGuiPythonUI_SRCS Resources/ui/ElementFluid1D.ui Resources/ui/ElementGeometry1D.ui Resources/ui/ElementGeometry2D.ui + Resources/ui/ElementRotation1D.ui Resources/ui/FlowVelocity.ui Resources/ui/InitialFlowVelocity.ui Resources/ui/Material.ui diff --git a/src/Mod/Fem/Gui/Resources/ui/ElementRotation1D.ui b/src/Mod/Fem/Gui/Resources/ui/ElementRotation1D.ui new file mode 100644 index 0000000000..803d23c103 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/ui/ElementRotation1D.ui @@ -0,0 +1,143 @@ + + + Form + + + + 0 + 0 + 350 + 455 + + + + Beam section rotation + + + + + + Cross section parameter + + + + 9 + + + 6 + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + 0 + 0 + + + + + 80 + 20 + + + + Qt::LeftToRight + + + 0.0 + + + 360 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1.000000000000000 + + + 1000000000.000000000000000 + + + degree + + + 2 + + + 0.000000000000000 + + + + + + + Rotation: + + + + + + + + + + + + References + + + + + + Leave blank to choose all remaining shapes + + + + + + + Add reference + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + l_label_text_1 + pushButton_Reference + list_References + verticalSpacer + + + + + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
+ + +
diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index 7d63dc0b0f..f556df948d 100755 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -69,6 +69,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "FEM_MaterialFluid" << "FEM_MaterialMechanicalNonlinear" << "FEM_ElementGeometry1D" + << "FEM_ElementRotation1D" << "FEM_ElementGeometry2D" << "FEM_ElementFluid1D"; @@ -199,6 +200,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "FEM_MaterialFluid" << "FEM_MaterialMechanicalNonlinear" << "FEM_ElementGeometry1D" + << "FEM_ElementRotation1D" << "FEM_ElementGeometry2D" << "FEM_ElementFluid1D" << "Separator" diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py index c7da1677a3..0747ba8705 100644 --- a/src/Mod/Fem/ObjectsFem.py +++ b/src/Mod/Fem/ObjectsFem.py @@ -223,6 +223,17 @@ def makeElementGeometry2D(doc, thickness=20.0, name="ElementGeometry2D"): return obj +def makeElementRotation1D(doc, name="ElementRotation1D"): + '''makeElementRotation1D(document, [name]): creates an 1D geometry rotation element object to rotate a 1D cross section''' + obj = doc.addObject("Fem::FeaturePython", name) + from PyObjects import _FemElementRotation1D + _FemElementRotation1D._FemElementRotation1D(obj) + if FreeCAD.GuiUp: + from PyGui import _ViewProviderFemElementRotation1D + _ViewProviderFemElementRotation1D._ViewProviderFemElementRotation1D(obj.ViewObject) + return obj + + ########## material objects ########## def makeMaterialFluid(doc, name="FluidMaterial"): '''makeMaterialFluid(document, [name]): makes a FEM Material for fluid''' diff --git a/src/Mod/Fem/PyGui/_ViewProviderFemElementRotation1D.py b/src/Mod/Fem/PyGui/_ViewProviderFemElementRotation1D.py new file mode 100644 index 0000000000..87c2b94a40 --- /dev/null +++ b/src/Mod/Fem/PyGui/_ViewProviderFemElementRotation1D.py @@ -0,0 +1,228 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2017 - Bernd Hahnebach * +# * * +# * 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__ = "_ViewProviderFemElementRotation1D" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package ViewProviderFemElementRotation1D +# \ingroup FEM + +import FreeCAD +import FreeCADGui + + +# for the panel +from PySide import QtCore +from PySide import QtGui + + +class _ViewProviderFemElementRotation1D: + "A View Provider for the FemElementRotation1D object" + def __init__(self, vobj): + vobj.Proxy = self + + def getIcon(self): + return ":/icons/fem-beam-section.svg" + + def attach(self, vobj): + from pivy import coin + self.ViewObject = vobj + self.Object = vobj.Object + self.standard = coin.SoGroup() + vobj.addDisplayMode(self.standard, "Standard") + + def getDisplayModes(self, obj): + return ["Standard"] + + def getDefaultDisplayMode(self): + return "Standard" + + def updateData(self, obj, prop): + return + + def onChanged(self, vobj, prop): + return + + def setEdit(self, vobj, mode=0): + taskd = _TaskPanelFemElementRotation1D(self.Object) + taskd.obj = vobj.Object + FreeCADGui.Control.showDialog(taskd) + return True + + def unsetEdit(self, vobj, mode=0): + FreeCADGui.Control.closeDialog() + return + + def doubleClicked(self, vobj): + doc = FreeCADGui.getDocument(vobj.Object.Document) + if not doc.getInEdit(): + doc.setEdit(vobj.Object.Name) + else: + FreeCAD.Console.PrintError('Active Task Dialog found! Please close this one first!\n') + return True + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + +class _TaskPanelFemElementRotation1D: + '''The TaskPanel for editing References property of FemElementRotation1D objects''' + def __init__(self, obj): + FreeCADGui.Selection.clearSelection() + self.sel_server = None + self.obj = obj + self.obj_notvisible = [] + + self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/ElementRotation1D.ui") + QtCore.QObject.connect(self.form.if_rotation, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.rotation_changed) + QtCore.QObject.connect(self.form.pushButton_Reference, QtCore.SIGNAL("clicked()"), self.add_references) + self.form.list_References.itemSelectionChanged.connect(self.select_clicked_reference_shape) + self.form.list_References.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.form.list_References.connect(self.form.list_References, QtCore.SIGNAL("customContextMenuRequested(QPoint)"), self.references_list_right_clicked) + + self.get_beamsection_props() + self.update() + + def accept(self): + self.setback_listobj_visibility() + self.set_beamrotation_props() + if self.sel_server: + FreeCADGui.Selection.removeObserver(self.sel_server) + FreeCADGui.ActiveDocument.resetEdit() + FreeCAD.ActiveDocument.recompute() + return True + + def reject(self): + self.setback_listobj_visibility() + if self.sel_server: + FreeCADGui.Selection.removeObserver(self.sel_server) + FreeCADGui.ActiveDocument.resetEdit() + return True + + def get_beamsection_props(self): + self.references = [] + if self.obj.References: + self.tuplereferences = self.obj.References + self.get_references() + self.Rotation = self.obj.Rotation + + def set_beamrotation_props(self): + self.obj.References = self.references + self.obj.Rotation = self.Rotation + + def update(self): + 'fills the widgets' + self.form.if_rotation.setText(self.Rotation.UserString) + self.rebuild_list_References() + + def rotation_changed(self, base_quantity_value): + self.Rotation = base_quantity_value + + def get_references(self): + for ref in self.tuplereferences: + for elem in ref[1]: + self.references.append((ref[0], elem)) + + def references_list_right_clicked(self, QPos): + self.form.contextMenu = QtGui.QMenu() + menu_item = self.form.contextMenu.addAction("Remove Reference") + if not self.references: + menu_item.setDisabled(True) + self.form.connect(menu_item, QtCore.SIGNAL("triggered()"), self.remove_reference) + parentPosition = self.form.list_References.mapToGlobal(QtCore.QPoint(0, 0)) + self.form.contextMenu.move(parentPosition + QPos) + self.form.contextMenu.show() + + def remove_reference(self): + if not self.references: + return + currentItemName = str(self.form.list_References.currentItem().text()) + for ref in self.references: + refname_to_compare_listentry = ref[0].Name + ':' + ref[1] + if refname_to_compare_listentry == currentItemName: + self.references.remove(ref) + self.rebuild_list_References() + + def add_references(self): + '''Called if Button add_reference is triggered''' + # in constraints EditTaskPanel the selection is active as soon as the taskpanel is open + # here the addReference button EditTaskPanel has to be triggered to start selection mode + self.setback_listobj_visibility() + FreeCADGui.Selection.clearSelection() + # start SelectionObserver and parse the function to add the References to the widget + print_message = "Select Edges by single click on them to add them to the list" + if not self.sel_server: + # if we do not check, we would start a new SelectionObserver on every click on addReference button + # but close only one SelectionObserver on leaving the task panel + from . import FemSelectionObserver + self.sel_server = FemSelectionObserver.FemSelectionObserver(self.selectionParser, print_message) + + def selectionParser(self, selection): + # print('selection: ', selection[0].Shape.ShapeType, ' ', selection[0].Name, ' ', selection[1]) + if hasattr(selection[0], "Shape"): + if selection[1]: + elt = selection[0].Shape.getElement(selection[1]) + if elt.ShapeType == 'Edge': + if selection not in self.references: + self.references.append(selection) + self.rebuild_list_References() + else: + FreeCAD.Console.PrintMessage(selection[0].Name + ' --> ' + selection[1] + ' is in reference list already!\n') + + def rebuild_list_References(self): + self.form.list_References.clear() + items = [] + for ref in self.references: + item_name = ref[0].Name + ':' + ref[1] + items.append(item_name) + for listItemName in sorted(items): + self.form.list_References.addItem(listItemName) + + def select_clicked_reference_shape(self): + self.setback_listobj_visibility() + if self.sel_server: + FreeCADGui.Selection.removeObserver(self.sel_server) + self.sel_server = None + if not self.sel_server: + if not self.references: + return + currentItemName = str(self.form.list_References.currentItem().text()) + for ref in self.references: + refname_to_compare_listentry = ref[0].Name + ':' + ref[1] + if refname_to_compare_listentry == currentItemName: + # print( 'found: shape: ' + ref[0].Name + ' element: ' + ref[1]) + if not ref[0].ViewObject.Visibility: + self.obj_notvisible.append(ref[0]) + ref[0].ViewObject.Visibility = True + FreeCADGui.Selection.clearSelection() + FreeCADGui.Selection.addSelection(ref[0], ref[1]) + + def setback_listobj_visibility(self): + '''set back Visibility of the list objects + ''' + for obj in self.obj_notvisible: + obj.ViewObject.Visibility = False + self.obj_notvisible = [] diff --git a/src/Mod/Fem/PyObjects/_FemElementRotation1D.py b/src/Mod/Fem/PyObjects/_FemElementRotation1D.py new file mode 100644 index 0000000000..687a6c6d7a --- /dev/null +++ b/src/Mod/Fem/PyObjects/_FemElementRotation1D.py @@ -0,0 +1,41 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2017 - Bernd Hahnebach * +# * * +# * 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__ = "FemElementRotation1D" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package FemElementRotation1D +# \ingroup FEM + + +class _FemElementRotation1D: + "The FemElementRotation1D object" + + def __init__(self, obj): + obj.addProperty("App::PropertyAngle", "Rotation", "BeamRotation", "set the rotation of beam elements") + obj.addProperty("App::PropertyLinkSubList", "References", "BeamRotation", "List of beam rotation shapes") + obj.Proxy = self + self.Type = "Fem::FemElementRotation1D" + + def execute(self, obj): + return diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py index f26f19a7a5..9c9185d710 100644 --- a/src/Mod/Fem/femcommands/commands.py +++ b/src/Mod/Fem/femcommands/commands.py @@ -192,6 +192,22 @@ class _CommandFemElementGeometry2D(CommandManager): FreeCADGui.doCommand("FemGui.getActiveAnalysis().addObject(ObjectsFem.makeElementGeometry2D(FreeCAD.ActiveDocument))") +class _CommandFemElementRotation1D(CommandManager): + "The Fem_ElementRotation1D command definition" + def __init__(self): + super(_CommandFemElementRotation1D, self).__init__() + self.resources = {'Pixmap': 'fem-beam-section', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("FEM_ElementRotation1D", "Beam rotation"), + 'Accel': "C, R", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("FEM_ElementRotation1D", "Creates a FEM beam rotation")} + self.is_active = 'with_analysis' + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction("Create FemElementRotation1D") + FreeCADGui.addModule("ObjectsFem") + FreeCADGui.doCommand("FemGui.getActiveAnalysis().addObject(ObjectsFem.makeElementRotation1D(FreeCAD.ActiveDocument))") + + class _CommandFemEquationElectrostatic(CommandManager): "The FEM_EquationElectrostatic command definition" def __init__(self): @@ -698,6 +714,7 @@ FreeCADGui.addCommand('FEM_ConstraintSelfWeight', _CommandFemConstraintSelfWeigh FreeCADGui.addCommand('FEM_ElementFluid1D', _CommandFemElementFluid1D()) FreeCADGui.addCommand('FEM_ElementGeometry1D', _CommandFemElementGeometry1D()) FreeCADGui.addCommand('FEM_ElementGeometry2D', _CommandFemElementGeometry2D()) +FreeCADGui.addCommand('FEM_ElementRotation1D', _CommandFemElementRotation1D()) FreeCADGui.addCommand('FEM_EquationElectrostatic', _CommandFemEquationElectrostatic()) FreeCADGui.addCommand('FEM_EquationElasticity', _CommandFemEquationElasticity()) FreeCADGui.addCommand('FEM_EquationFlow', _CommandFemEquationFlow()) diff --git a/src/Mod/Fem/femtest/testfemcommon.py b/src/Mod/Fem/femtest/testfemcommon.py index 0c50b226ce..a434be09f8 100644 --- a/src/Mod/Fem/femtest/testfemcommon.py +++ b/src/Mod/Fem/femtest/testfemcommon.py @@ -373,6 +373,7 @@ class FemTest(unittest.TestCase): analysis.addObject(ObjectsFem.makeElementFluid1D(doc)) analysis.addObject(ObjectsFem.makeElementGeometry1D(doc)) analysis.addObject(ObjectsFem.makeElementGeometry2D(doc)) + analysis.addObject(ObjectsFem.makeElementRotation1D(doc)) analysis.addObject(ObjectsFem.makeMaterialFluid(doc)) mat = analysis.addObject(ObjectsFem.makeMaterialSolid(doc))[0] @@ -429,6 +430,7 @@ class FemTest(unittest.TestCase): self.assertEqual('Fem::FemElementFluid1D', typeOfObj(ObjectsFem.makeElementFluid1D(doc))) self.assertEqual('Fem::FemElementGeometry1D', typeOfObj(ObjectsFem.makeElementGeometry1D(doc))) self.assertEqual('Fem::FemElementGeometry2D', typeOfObj(ObjectsFem.makeElementGeometry2D(doc))) + self.assertEqual('Fem::FemElementRotation1D', typeOfObj(ObjectsFem.makeElementRotation1D(doc))) materialsolid = ObjectsFem.makeMaterialSolid(doc) self.assertEqual('Fem::Material', typeOfObj(ObjectsFem.makeMaterialFluid(doc))) self.assertEqual('Fem::Material', typeOfObj(materialsolid)) @@ -480,6 +482,7 @@ class FemTest(unittest.TestCase): self.assertTrue(isOfTypeNew(ObjectsFem.makeElementFluid1D(doc), 'Fem::FemElementFluid1D')) self.assertTrue(isOfTypeNew(ObjectsFem.makeElementGeometry1D(doc), 'Fem::FemElementGeometry1D')) self.assertTrue(isOfTypeNew(ObjectsFem.makeElementGeometry2D(doc), 'Fem::FemElementGeometry2D')) + self.assertTrue(isOfTypeNew(ObjectsFem.makeElementRotation1D(doc), 'Fem::FemElementRotation1D')) materialsolid = ObjectsFem.makeMaterialSolid(doc) self.assertTrue(isOfTypeNew(ObjectsFem.makeMaterialFluid(doc), 'Fem::Material')) self.assertTrue(isOfTypeNew(materialsolid, 'Fem::Material')) @@ -530,6 +533,7 @@ class FemTest(unittest.TestCase): self.assertTrue(isDerivedFromFem(ObjectsFem.makeElementFluid1D(doc), 'Fem::FemElementFluid1D')) self.assertTrue(isDerivedFromFem(ObjectsFem.makeElementGeometry1D(doc), 'Fem::FemElementGeometry1D')) self.assertTrue(isDerivedFromFem(ObjectsFem.makeElementGeometry2D(doc), 'Fem::FemElementGeometry2D')) + self.assertTrue(isDerivedFromFem(ObjectsFem.makeElementRotation1D(doc), 'Fem::FemElementRotation1D')) materialsolid = ObjectsFem.makeMaterialSolid(doc) self.assertTrue(isDerivedFromFem(ObjectsFem.makeMaterialFluid(doc), 'Fem::Material')) self.assertTrue(isDerivedFromFem(materialsolid, 'Fem::Material')) @@ -579,6 +583,7 @@ class FemTest(unittest.TestCase): self.assertTrue(ObjectsFem.makeElementFluid1D(doc).isDerivedFrom('Fem::FeaturePython')) self.assertTrue(ObjectsFem.makeElementGeometry1D(doc).isDerivedFrom('Fem::FeaturePython')) self.assertTrue(ObjectsFem.makeElementGeometry2D(doc).isDerivedFrom('Fem::FeaturePython')) + self.assertTrue(ObjectsFem.makeElementRotation1D(doc).isDerivedFrom('Fem::FeaturePython')) materialsolid = ObjectsFem.makeMaterialSolid(doc) self.assertTrue(ObjectsFem.makeMaterialFluid(doc).isDerivedFrom('App::MaterialObjectPython')) self.assertTrue(materialsolid.isDerivedFrom('App::MaterialObjectPython'))