FEM: Animation of Results (#18496)

This commit is contained in:
mac-the-bike
2025-02-17 16:24:09 +00:00
committed by GitHub
parent 4698042deb
commit 6fcbafb121
5 changed files with 592 additions and 43 deletions

View File

@@ -32,6 +32,7 @@ SET(FemBaseModules_SRCS
InitGui.py
ObjectsFem.py
TestFemApp.py
CreateLabels.py
)
SET(FemCommands_SRCS

185
src/Mod/Fem/CreateLabels.py Normal file
View File

@@ -0,0 +1,185 @@
# ***************************************************************************
# * Copyright (c) 2024 Peter McB
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * 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__ = "FreeCAD result mechanical task panel"
__author__ = "PMcB"
__url__ = "https://www.freecad.org"
## @package view_result_mechanical
# \ingroup FEM
# \brief Create and Place Labels on the screen with an optional image.
import FreeCADGui
from pivy import coin
"""
Place Label on the screen with an optional image.
positions on screen:
(-.9, .9, 0) top left
(.9, .9, 0) upper right
(-.9, -.9, 0) lower left
(.9, -.9, 0) lower right
"""
class createLabel:
def __init__(self, trans, text, image="") -> None:
self.textSep = coin.SoSeparator()
self.cam = coin.SoOrthographicCamera()
self.cam.aspectRatio = 1
self.cam.viewportMapping = coin.SoCamera.LEAVE_ALONE
self.trans = coin.SoTranslation()
self.trans.translation = trans
self.translation = trans
self.color = coin.SoBaseColor()
self.color.rgb = (0, 0, 0)
self.myFont = coin.SoFont()
self.myFont.name = "Arial,FreeSans,sans"
# self.myFont.name = "FreeMono,FreeSans,sans"
self.size = 20 # 24
self.myFont.size.setValue(self.size)
self.SoText2 = coin.SoText2()
self.SoText2.string = text
self.textSep.addChild(self.cam)
self.textSep.addChild(self.trans)
self.textSep.addChild(self.color)
self.textSep.addChild(self.myFont)
self.textSep.addChild(self.SoText2)
self.activeDoc = FreeCADGui.ActiveDocument
self.view = self.activeDoc.ActiveView
self.viewer = self.view.getViewer()
self.render = self.viewer.getSoRenderManager()
self.isimage = False
if image != "":
self.add_image(image)
self.isimage = True
self.sup = self.render.addSuperimposition(self.textSep)
self.displayed = True
# get position of label
def get_position(self):
return self.translation
# set position of label
def set_position(self, trans):
self.trans.translation = trans
self.translation = trans
# set font
def set_font(self, font):
self.myFont.name = font
# set font size
def set_font_size(self, font_size):
self.myFont.size.setValue(font_size)
# set text of label
def set_text(self, text):
self.SoText2.string = text
# set colour of label
def set_colour(self, colour):
self.color.rgb = colour
self.textSep.addChild(self.color)
# add or change the image with the label
def add_image(self, image):
self.remove_image()
self.isimage = True
self.myImage = coin.SoImage()
self.myImage.filename.setValue(image)
self.textSep.addChild(self.myImage)
# remove the image
def remove_image(self):
if self.isimage:
# self.myImage = coin.SoImage()
self.textSep.removeChild(self.myImage)
self.isimage = False
# display the label
def display(self):
if not self.displayed:
# display the Superimposition layer with :
self.sup = self.render.addSuperimposition(self.textSep)
self.displayed = True
# hide the label
def hide(self):
# hide the Superimposition layer with :
if self.displayed:
self.render.removeSuperimposition(self.sup)
self.displayed = False
# help
def help(self):
print(
"""
Place Label on the screen with an optional image.
Create an instance:
import CreateLabels as CL
label = CL.creatLabel(<position of label>, <title of label>, <optional image>)
e.g.:
label1 = CL.createLabel( (-0.7, -0.90, 0), "this is text")
label2 = CL.createLabel( (+0.5, -0.90, 0), "this is a image", image = "mesh.jpg")
Positions on screen:
(-.9, .9, 0) top left
(.9, .9, 0) upper right
(-.9, -.9, 0) lower left
(.9, -.9, 0) lower right
The following function are available:
label1.set_position((0.7, 0.50, 0))
pos= label1.get_position()
label1.set_font("FreeMono,FreeSans,sans")
label1.set_font_size(20)
label1.set_text("hello world")
label1.set_colour((0,1,0)) # colour of text
label1.add_image("opera.jpg")
label1.remove_image()
label1.hide() - hide the label
label1.display() - display the label, after hide
"""
)
if __name__ == "__main__":
label1 = createLabel((-0.98, 0.90, 0), "this a new text")
"""
Examples:
import CreateLabels as CL
label1 = CL.createLabel( (+0.5, -0.90, 0), "this is a image", image = "mesh.jpg")
label2 = CL.createLabel( (-0.7, -0.90, 0), "this is text")
label1.set_colour((0,1,0))
label1.remove_image()
label1.add_image("opera.jpg")
label1.set_text("hello world")
label1.set_position((+0.7, 0.50, 0))
label1.set_font("FreeMono,FreeSans,sans")
label1.hide()
"""

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>335</width>
<height>548</height>
<width>477</width>
<height>798</height>
</rect>
</property>
<property name="windowTitle">
@@ -16,6 +16,12 @@
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="gb_result_type">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Result type</string>
</property>
@@ -158,6 +164,13 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="show_histogram">
<property name="text">
<string>Histogram</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -199,13 +212,6 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="show_histogram">
<property name="text">
<string>Histogram</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@@ -213,8 +219,14 @@
</item>
<item>
<widget class="QGroupBox" name="gb_displacement">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>180</height>
</size>
</property>
<property name="title">
<string>Displacement</string>
<string>Displacement Scaling</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
@@ -241,7 +253,10 @@
</sizepolicy>
</property>
<property name="maximum">
<number>100</number>
<number>1000</number>
</property>
<property name="value">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -251,14 +266,14 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_3">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Factor:</string>
<string>Factor</string>
</property>
</widget>
</item>
@@ -274,24 +289,27 @@
<bool>false</bool>
</property>
<property name="decimals">
<number>1</number>
<number>10</number>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_7">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Slider max:</string>
<string>Slider Max</string>
</property>
</widget>
</item>
@@ -307,7 +325,7 @@
<bool>false</bool>
</property>
<property name="decimals">
<number>1</number>
<number>10</number>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
@@ -319,8 +337,187 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_animation">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>170</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Animation Control</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>435</width>
<height>28</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_1">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>Number of Steps per Cycle</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="steps">
<property name="enabled">
<bool>true</bool>
</property>
<property name="inputMethodHints">
<set>Qt::ImhFormattedNumbersOnly</set>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
</property>
<property name="value">
<double>24.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>435</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_8">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>Number of Cycles</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="loops">
<property name="decimals">
<number>0</number>
</property>
<property name="value">
<double>4.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>435</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>Frame Rate</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="framerate">
<property name="enabled">
<bool>true</bool>
</property>
<property name="inputMethodHints">
<set>Qt::ImhFormattedNumbersOnly</set>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
</property>
<property name="value">
<double>24.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QPushButton" name="startButton">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>435</width>
<height>25</height>
</rect>
</property>
<property name="toolTip">
<string>Toggles between Start and Stop</string>
</property>
<property name="text">
<string>Start Animation</string>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_displacement_2">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>120</height>
</size>
</property>
<property name="title">
<string>User defined equation</string>
</property>

View File

@@ -1,6 +1,7 @@
# ***************************************************************************
# * Copyright (c) 2015 Qingfeng Xia <qingfeng.xia()eng.ox.ac.uk> *
# * Copyright (c) 2016 Bernd Hahnebach <bernd@bimstatik.org> *
# * Copyright (c) 2024 PMcB *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -30,6 +31,9 @@ __url__ = "https://www.freecad.org"
# \ingroup FEM
# \brief task panel for mechanical ResultObjectPython
import CreateLabels
import inspect, sys
try:
import matplotlib
@@ -39,6 +43,7 @@ except Exception:
import matplotlib.pyplot as plt
import numpy as np
import time, math
from PySide import QtCore
from PySide import QtGui
@@ -71,6 +76,12 @@ class _TaskPanel:
self.result_widget = FreeCADGui.PySideUic.loadUi(ui_path + "ResultShow.ui")
self.info_widget = FreeCADGui.PySideUic.loadUi(ui_path + "ResultHints.ui")
self.form = [self.result_widget, self.info_widget]
self.results_name = "No Contour Data"
self.animate_inc = 1
self.startAnimate = False
self.animateText = []
self.slider_max = False
self.recurlim = min(200, sys.getrecursionlimit() / 2)
self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General")
self.restore_result_settings_in_dialog = self.fem_prefs.GetBool("RestoreResultDialog", True)
@@ -79,7 +90,9 @@ class _TaskPanel:
# result type radio buttons
# TODO: move to combo box, to be independent from result types and result types count
QtCore.QObject.connect(
self.result_widget.rb_none, QtCore.SIGNAL("toggled(bool)"), self.none_selected
self.result_widget.rb_none,
QtCore.SIGNAL("toggled(bool)"),
self.none_selected,
)
QtCore.QObject.connect(
self.result_widget.rb_abs_displacement,
@@ -107,13 +120,19 @@ class _TaskPanel:
self.temperature_selected,
)
QtCore.QObject.connect(
self.result_widget.rb_vm_stress, QtCore.SIGNAL("toggled(bool)"), self.vm_stress_selected
self.result_widget.rb_vm_stress,
QtCore.SIGNAL("toggled(bool)"),
self.vm_stress_selected,
)
QtCore.QObject.connect(
self.result_widget.rb_maxprin, QtCore.SIGNAL("toggled(bool)"), self.max_prin_selected
self.result_widget.rb_maxprin,
QtCore.SIGNAL("toggled(bool)"),
self.max_prin_selected,
)
QtCore.QObject.connect(
self.result_widget.rb_minprin, QtCore.SIGNAL("toggled(bool)"), self.min_prin_selected
self.result_widget.rb_minprin,
QtCore.SIGNAL("toggled(bool)"),
self.min_prin_selected,
)
QtCore.QObject.connect(
self.result_widget.rb_max_shear_stress,
@@ -131,11 +150,29 @@ class _TaskPanel:
self.networkpressure_selected,
)
QtCore.QObject.connect(
self.result_widget.rb_peeq, QtCore.SIGNAL("toggled(bool)"), self.peeq_selected
self.result_widget.rb_peeq,
QtCore.SIGNAL("toggled(bool)"),
self.peeq_selected,
)
# stats
self.result_widget.show_histogram.clicked.connect(self.show_histogram_clicked)
# animate
QtCore.QObject.connect(
self.result_widget.hsb_displacement_factor,
QtCore.SIGNAL("valueChanged(int)"),
lambda dummy="", name="scale": self.value_changed(self, dummy, name),
)
QtCore.QObject.connect(
self.result_widget.sb_displacement_factor,
QtCore.SIGNAL("valueChanged(double)"),
lambda dummy="", name="factor": self.value_changed(self, dummy, name),
)
QtCore.QObject.connect(
self.result_widget.startButton,
QtCore.SIGNAL("clicked()"),
lambda dummy="", name="startButton": self.value_changed(self, dummy, name),
)
# displacement
QtCore.QObject.connect(
@@ -223,6 +260,12 @@ class _TaskPanel:
# self.result_widget.hsb_displacement_factor.setValue(df)
self.result_widget.sb_displacement_factor_max.setValue(dfm)
self.result_widget.sb_displacement_factor.setValue(df)
# animate
self.startAnimate = False
if FreeCAD.FEM_dialog["animate"][0] != -1:
self.result_widget.steps.setValue(FreeCAD.FEM_dialog["animate"][0])
self.result_widget.loops.setValue(FreeCAD.FEM_dialog["animate"][1])
self.result_widget.framerate.setValue(FreeCAD.FEM_dialog["animate"][2])
except Exception:
self.restore_initial_result_dialog()
@@ -238,9 +281,10 @@ class _TaskPanel:
# https://github.com/FreeCAD/FreeCAD/commit/3a7772d
FreeCAD.FEM_dialog = {
"results_type": "None",
"show_disp": False,
"disp_factor": 0.0,
"show_disp": True, # False,
"disp_factor": 5.0,
"disp_factor_max": 100.0,
"animate": [-1, -1, -1, -1], # steps, loops, rate, indicator (not used)
}
self.result_widget.sb_displacement_factor_max.setValue(100.0) # init non standard values
@@ -251,6 +295,7 @@ class _TaskPanel:
return resulttools.get_stats(self.result_obj, type_name)
def none_selected(self, state):
self.set_label(self.result_obj.Label, "No Contours")
FreeCAD.FEM_dialog["results_type"] = "None"
self.set_result_stats("mm", 0.0, 0.0)
self.reset_mesh_color()
@@ -303,7 +348,10 @@ class _TaskPanel:
def vm_stress_selected(self, state):
if len(self.result_obj.vonMises) > 0:
self.result_selected(
"Sabs", self.result_obj.vonMises, "MPa", translate("FEM", "von Mises Stress")
"Sabs",
self.result_obj.vonMises,
"MPa",
translate("FEM", "von Mises Stress"),
)
else:
self.result_widget.rb_none.setChecked(True)
@@ -312,7 +360,10 @@ class _TaskPanel:
def max_shear_selected(self, state):
if len(self.result_obj.MaxShear) > 0:
self.result_selected(
"MaxShear", self.result_obj.MaxShear, "MPa", translate("FEM", "Max Shear Stress")
"MaxShear",
self.result_obj.MaxShear,
"MPa",
translate("FEM", "Max Shear Stress"),
)
else:
self.result_widget.rb_none.setChecked(True)
@@ -333,7 +384,10 @@ class _TaskPanel:
def temperature_selected(self, state):
if len(self.result_obj.Temperature) > 0:
self.result_selected(
"Temp", self.result_obj.Temperature, "K", translate("FEM", "Temperature")
"Temp",
self.result_obj.Temperature,
"K",
translate("FEM", "Temperature"),
)
else:
self.result_widget.rb_none.setChecked(True)
@@ -342,7 +396,10 @@ class _TaskPanel:
def massflowrate_selected(self, state):
if len(self.result_obj.MassFlowRate) > 0:
self.result_selected(
"MFlow", self.result_obj.MassFlowRate, "kg/s", translate("FEM", "Mass Flow Rate")
"MFlow",
self.result_obj.MassFlowRate,
"kg/s",
translate("FEM", "Mass Flow Rate"),
)
else:
self.result_widget.rb_none.setChecked(True)
@@ -375,7 +432,10 @@ class _TaskPanel:
def peeq_selected(self, state):
if len(self.result_obj.Peeq) > 0:
self.result_selected(
"Peeq", self.result_obj.Peeq, "", translate("FEM", "Equivalent Plastic Strain")
"Peeq",
self.result_obj.Peeq,
"",
translate("FEM", "Equivalent Plastic Strain"),
)
else:
self.result_widget.rb_none.setChecked(True)
@@ -396,7 +456,10 @@ class _TaskPanel:
QtGui.QMessageBox.information(
None,
self.result_obj.Label + " - " + translate("FEM", "Information"),
translate("FEM", "No histogram available.\nPlease select a result type first."),
translate(
"FEM",
"No histogram available.\nPlease select a result type first.",
),
)
def user_defined_text(self, equation):
@@ -404,7 +467,6 @@ class _TaskPanel:
self.result_widget.user_def_eq.toPlainText()
def calculate(self):
# Convert existing result values to numpy array
# scalars
P1 = np.array(self.result_obj.PrincipalMax)
@@ -461,6 +523,7 @@ class _TaskPanel:
self.update()
self.restore_result_dialog()
userdefined_eq = self.result_widget.user_def_eq.toPlainText() # Get equation to be used
self.results_name = "User Defined: " + userdefined_eq
# https://forum.freecad.org/viewtopic.php?f=18&t=42425&start=10#p368774 ff
# https://github.com/FreeCAD/FreeCAD/pull/3020
@@ -533,6 +596,7 @@ class _TaskPanel:
return scalar_list
def result_selected(self, res_type, res_values, res_unit, res_title):
self.results_name = res_title
FreeCAD.FEM_dialog["results_type"] = res_type
(minm, maxm) = self.get_result_stats(res_type)
self.update_colors_stats(res_values, res_unit, minm, maxm)
@@ -555,6 +619,7 @@ class _TaskPanel:
fig_manager.window.setWindowFlag(QtCore.Qt.Tool)
def update_colors_stats(self, res_values, res_unit, minm, maxm):
self.set_label(self.result_obj.Label, self.results_name)
QApplication.setOverrideCursor(Qt.WaitCursor)
if self.suitable_results:
self.mesh_obj.ViewObject.setNodeColorByScalars(self.result_obj.NodeNumbers, res_values)
@@ -596,6 +661,7 @@ class _TaskPanel:
self.update_displacement()
def sb_disp_factor_max_changed(self, value):
self.slider_max = True
FreeCAD.FEM_dialog["disp_factor_max"] = value
if value < self.result_widget.sb_displacement_factor.value():
self.result_widget.sb_displacement_factor.setValue(value)
@@ -605,19 +671,24 @@ class _TaskPanel:
self.result_widget.hsb_displacement_factor.setValue(
round(self.result_widget.sb_displacement_factor.value() / value * 100.0)
)
self.slider_max = False
def sb_disp_factor_changed(self, value):
FreeCAD.FEM_dialog["disp_factor"] = value
if value > self.result_widget.sb_displacement_factor_max.value():
self.result_widget.sb_displacement_factor.setValue(
self.result_widget.sb_displacement_factor_max.value()
)
if self.result_widget.sb_displacement_factor_max.value() == 0.0:
self.result_widget.hsb_displacement_factor.setValue(0.0)
else:
self.result_widget.hsb_displacement_factor.setValue(
round(value / self.result_widget.sb_displacement_factor_max.value() * 100.0)
)
# this bit of code causes:
# RecursionError: maximum recursion depth exceeded
# so check on the depth and don't exceed recurlim
if len(inspect.stack(0)) < self.recurlim:
FreeCAD.FEM_dialog["disp_factor"] = value
if value > self.result_widget.sb_displacement_factor_max.value():
self.result_widget.sb_displacement_factor.setValue(
self.result_widget.sb_displacement_factor_max.value()
)
if self.result_widget.sb_displacement_factor_max.value() == 0.0:
self.result_widget.hsb_displacement_factor.setValue(0.0)
else:
self.result_widget.hsb_displacement_factor.setValue(
round(value / self.result_widget.sb_displacement_factor_max.value() * 100.0)
)
def disable_empty_result_buttons(self):
"""disable radio buttons if result does not exists in result object"""
@@ -706,6 +777,99 @@ class _TaskPanel:
# thus reset edit does not close the dialog, maybe don't call but set in edit instead
FreeCADGui.Control.closeDialog()
FreeCADGui.ActiveDocument.resetEdit()
if len(self.animateText) > 0:
for a in self.animateText:
a.hide()
self.animateText = []
self.startAnimate = False
FreeCAD.FEM_dialog["animate"][0] = self.result_widget.steps.value()
FreeCAD.FEM_dialog["animate"][1] = self.result_widget.loops.value()
FreeCAD.FEM_dialog["animate"][2] = self.result_widget.framerate.value()
# animation start
def animate_displacement(self):
if "result_obj" in FreeCAD.FEM_dialog:
if FreeCAD.FEM_dialog["result_obj"] != self.result_obj:
self.update_displacement()
self.result_widget.cb_show_displacement.setChecked(True)
FreeCAD.FEM_dialog["result_obj"] = self.result_obj
if self.suitable_results:
self.mesh_obj.ViewObject.setNodeDisplacementByVectors(
self.result_obj.NodeNumbers, self.result_obj.DisplacementVectors
)
self.result_widget.startButton.setText("Stop Animation")
frame_rate = 10
self.hsb_displacement_factor = self.result_widget.sb_displacement_factor.value()
frame_rate = self.result_widget.framerate.value()
steps_per_cycle = int(self.result_widget.steps.value())
number_cycles = int(self.result_widget.loops.value())
inc = math.pi / steps_per_cycle * 2.0
self.set_label(self.result_obj.Label, self.results_name)
done = False
for lo in range(0, number_cycles):
for st in range(0, steps_per_cycle):
self.mesh_obj.ViewObject.applyDisplacement(
math.sin(st * inc) * self.hsb_displacement_factor
)
FreeCADGui.updateGui()
if not self.startAnimate:
done = True
break
time.sleep(1.0 / frame_rate) # modify the time here
if done:
break
try:
self.result_widget.startButton.setText("Start Animation")
except:
pass
QtGui.QApplication.restoreOverrideCursor()
self.startAnimate = False
def value_changed(self, dummy, value, myType):
# the only actions are:
if myType == "startButton":
if not self.startAnimate:
self.startAnimate = True
self.animate_displacement()
else:
self.startAnimate = False
# # this is taken care of in the "ui"
# # set the scale - scroll bar - Show
# elif myType == "scale" and not self.slider_max:
# if self.animate_inc == 0:
# if self.result_widget.hsb_displacement_factor.value() > 1:
# self.result_widget.sb_displacement_factor.setValue(
# self.result_widget.hsb_displacement_factor.value()
# )
# self.animate_inc = 1 - self.animate_inc
# # set the factor - spin - Factor
# elif myType == "factor" and not self.slider_max:
# if self.animate_inc == 0:
# self.result_widget.hsb_displacement_factor.setValue(
# int(self.result_widget.sb_displacement_factor.value())
# )
# self.animate_inc = 1 - self.animate_inc
else:
pass
try:
self.hsb_displacement_factor = self.result_widget.sb_displacement_factor.value()
except:
pass
return
def set_label(self, result_name, mesh_data):
if len(self.animateText) == 0:
self.animateText.append(CreateLabels.createLabel((-0.98, 0.90, 0), result_name))
self.animateText.append(CreateLabels.createLabel((-0.98, 0.70, 0), mesh_data))
else:
self.animateText[1].set_text(mesh_data)
pass
# animation end
# helper

View File

@@ -1,6 +1,7 @@
# ***************************************************************************
# * Copyright (c) 2015 Qingfeng Xia <qingfeng.xia()eng.ox.ac.uk> *
# * Copyright (c) 2016 Bernd Hahnebach <bernd@bimstatik.org> *
# * Copyright (c) 2024 PMcB *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -54,7 +55,8 @@ class VPResultMechanical(view_base_femconstraint.VPBaseFemConstraint):
def unsetEdit(self, vobj, mode=0):
FreeCADGui.Control.closeDialog()
# hide the mesh after result viewing is finished, but do not reset the coloring
self.Object.Mesh.ViewObject.hide()
# change this to not hide - PMcB
# self.Object.Mesh.ViewObject.hide()
return True
def claimChildren(self):