diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index b2a11337a3..56fb81cede 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -32,6 +32,7 @@ SET(FemBaseModules_SRCS InitGui.py ObjectsFem.py TestFemApp.py + CreateLabels.py ) SET(FemCommands_SRCS diff --git a/src/Mod/Fem/CreateLabels.py b/src/Mod/Fem/CreateLabels.py new file mode 100644 index 0000000000..75d614a4a3 --- /dev/null +++ b/src/Mod/Fem/CreateLabels.py @@ -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(, , <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() +""" diff --git a/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui b/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui index 496557e63c..d2e43b1a46 100644 --- a/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui +++ b/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui @@ -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> diff --git a/src/Mod/Fem/femtaskpanels/task_result_mechanical.py b/src/Mod/Fem/femtaskpanels/task_result_mechanical.py index 7b93c64232..5bb93ce236 100644 --- a/src/Mod/Fem/femtaskpanels/task_result_mechanical.py +++ b/src/Mod/Fem/femtaskpanels/task_result_mechanical.py @@ -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 diff --git a/src/Mod/Fem/femviewprovider/view_result_mechanical.py b/src/Mod/Fem/femviewprovider/view_result_mechanical.py index 11bd38cca1..a431ac512b 100644 --- a/src/Mod/Fem/femviewprovider/view_result_mechanical.py +++ b/src/Mod/Fem/femviewprovider/view_result_mechanical.py @@ -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):