diff --git a/src/Mod/Fem/Gui/Resources/ui/MeshGmsh.ui b/src/Mod/Fem/Gui/Resources/ui/MeshGmsh.ui index a8ae88a12b..be66475ca2 100644 --- a/src/Mod/Fem/Gui/Resources/ui/MeshGmsh.ui +++ b/src/Mod/Fem/Gui/Resources/ui/MeshGmsh.ui @@ -49,38 +49,35 @@ - - - - 0 - 0 - - - - - 80 - 20 - - - - 0 mm - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1.000000000000000 - - - 1000000000.000000000000000 + + + true mm - - 2 + + + 100 + 20 + - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + 0.000000000000000 + + + 1000000000000000000000.000000000000000 + + + 1.000000000000000 + + 0.000000000000000 @@ -93,38 +90,35 @@ - - - - 0 - 0 - - - - - 80 - 20 - - - - 0 mm - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1.000000000000000 - - - 1000000000.000000000000000 + + + true mm - - 2 + + + 100 + 20 + - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + 0.000000000000000 + + + 1000000000000000000000.000000000000000 + + + 1.000000000000000 + + 0.000000000000000 @@ -219,9 +213,9 @@ - Gui::InputField - QLineEdit -
Gui/InputField.h
+ Gui::QuantitySpinBox + QWidget +
Gui/QuantitySpinBox.h
diff --git a/src/Mod/Fem/femmesh/gmshtools.py b/src/Mod/Fem/femmesh/gmshtools.py index bbe2bf50f9..0a9bd5419f 100644 --- a/src/Mod/Fem/femmesh/gmshtools.py +++ b/src/Mod/Fem/femmesh/gmshtools.py @@ -46,17 +46,25 @@ class GmshError(Exception): class GmshTools: + + name = "Gmsh" + def __init__(self, gmsh_mesh_obj, analysis=None): # mesh obj self.mesh_obj = gmsh_mesh_obj + self.process = None # analysis if analysis: self.analysis = analysis else: self.analysis = None + self.load_properties() + self.error = False + + def load_properties(self): # part to mesh self.part_obj = self.mesh_obj.Shape @@ -186,7 +194,6 @@ class GmshTools: self.temp_file_geo = "" self.mesh_name = "" self.gmsh_bin = "" - self.error = False def update_mesh_data(self): self.start_logs() @@ -199,6 +206,27 @@ class GmshTools: self.write_part_file() self.write_geo() + def compute(self): + self.load_properties() + self.update_mesh_data() + self.get_tmp_file_paths() + self.get_gmsh_command() + self.write_gmsh_input_files() + + command_list = [self.gmsh_bin, "-", self.temp_file_geo] + self.process = subprocess.Popen( + command_list, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + out, err = self.process.communicate() + if self.process.returncode != 0: + raise RuntimeError(err.decode("utf-8")) + + return True + + def update_properties(self): + self.mesh_obj.FemMesh = Fem.read(self.temp_file_mesh) + def create_mesh(self): try: self.update_mesh_data() @@ -399,7 +427,7 @@ class GmshTools: # if self.group_elements: # Console.PrintMessage(" {}\n".format(self.group_elements)) - def get_gmsh_version(self): + def version(self): self.get_gmsh_command() if os.path.exists(self.gmsh_bin): found_message = "file found: " + self.gmsh_bin @@ -407,7 +435,7 @@ class GmshTools: else: found_message = "file not found: " + self.gmsh_bin Console.PrintError(found_message + "\n") - return (None, None, None), found_message + return found_message command_list = [self.gmsh_bin, "--info"] try: @@ -420,26 +448,10 @@ class GmshTools: ) except Exception as e: Console.PrintMessage(str(e) + "\n") - return (None, None, None), found_message + "\n\n" + "Error: " + str(e) + return found_message + "\n\n" + "Error: " + str(e) gmsh_stdout, gmsh_stderr = p.communicate() - Console.PrintMessage("Gmsh: StdOut:\n" + gmsh_stdout + "\n") - if gmsh_stderr: - Console.PrintError("Gmsh: StdErr:\n" + gmsh_stderr + "\n") - - from re import search - - # use raw string mode to get pep8 quiet - # https://stackoverflow.com/q/61497292 - # https://github.com/MathSci/fecon236/issues/6 - match = search(r"^Version\s*:\s*(\d+)\.(\d+)\.(\d+)", gmsh_stdout) - # return (major, minor, patch), fullmessage - if match: - mess = found_message + "\n\n" + gmsh_stdout - return match.group(1, 2, 3), mess - else: - mess = found_message + "\n\n" + "Warning: Output not recognized\n\n" + gmsh_stdout - return (None, None, None), mess + return gmsh_stdout def get_region_data(self): # mesh regions diff --git a/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py b/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py index 9c35ba266c..32afdba764 100644 --- a/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py +++ b/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py @@ -29,24 +29,17 @@ __url__ = "https://www.freecad.org" # \ingroup FEM # \brief task panel for mesh gmsh object -import sys -import time - from PySide import QtCore -from PySide import QtGui -from PySide.QtCore import Qt -from PySide.QtGui import QApplication import FreeCAD import FreeCADGui -import FemGui -from femtools.femutils import is_of_type -from femtools.femutils import getOutputWinColor -from . import base_femtaskpanel +from femmesh import gmshtools + +from . import base_femmeshtaskpanel -class _TaskPanel(base_femtaskpanel._BaseTaskPanel): +class _TaskPanel(base_femmeshtaskpanel._BaseMeshTaskPanel): """ The TaskPanel for editing References property of MeshGmsh objects and creation of new FEM mesh @@ -58,16 +51,14 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.form = FreeCADGui.PySideUic.loadUi( FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/MeshGmsh.ui" ) - self.Timer = QtCore.QTimer() - self.Timer.start(100) # 100 milli seconds - self.gmsh_runs = False - self.console_message_gmsh = "" + + self.tool = gmshtools.GmshTools(obj) QtCore.QObject.connect( - self.form.if_max, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.max_changed + self.form.qsb_max_size, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.max_changed ) QtCore.QObject.connect( - self.form.if_min, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.min_changed + self.form.qsb_min_size, QtCore.SIGNAL("valueChanged(Base::Quantity)"), self.min_changed ) QtCore.QObject.connect( self.form.cb_dimension, QtCore.SIGNAL("activated(int)"), self.choose_dimension @@ -75,40 +66,16 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): QtCore.QObject.connect( self.form.cb_order, QtCore.SIGNAL("activated(int)"), self.choose_order ) - QtCore.QObject.connect(self.Timer, QtCore.SIGNAL("timeout()"), self.update_timer_text) - QtCore.QObject.connect( - self.form.pb_get_gmsh_version, QtCore.SIGNAL("clicked()"), self.get_gmsh_version - ) - self.form.cb_dimension.addItems(self.obj.getEnumerationsOfProperty("ElementDimension")) self.form.cb_order.addItems(self.obj.getEnumerationsOfProperty("ElementOrder")) + QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.update_timer_text) + QtCore.QObject.connect( + self.form.pb_get_gmsh_version, QtCore.SIGNAL("clicked()"), self.get_version + ) self.get_mesh_params() - self.get_active_analysis() - self.update() - - def getStandardButtons(self): - button_value = ( - QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel - ) - return button_value - # show a OK, a apply and a Cancel button - # def reject() is called on Cancel button - # def clicked(self, button) is needed, to access the apply button - - def accept(self): - self.set_mesh_params() - return super().accept() - - def reject(self): - self.Timer.stop() - return super().reject() - - def clicked(self, button): - if button == QtGui.QDialogButtonBox.Apply: - self.set_mesh_params() - self.run_gmsh() + self.set_widgets() def get_mesh_params(self): self.clmax = self.obj.CharacteristicLengthMax @@ -122,42 +89,21 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.obj.ElementDimension = self.dimension self.obj.ElementOrder = self.order - def update(self): + def set_widgets(self): "fills the widgets" - self.form.if_max.setText(self.clmax.UserString) - self.form.if_min.setText(self.clmin.UserString) + self.form.qsb_max_size.setProperty("value", self.clmax) + FreeCADGui.ExpressionBinding(self.form.qsb_max_size).bind( + self.obj, "CharacteristicLengthMax" + ) + self.form.qsb_min_size.setProperty("value", self.clmin) + FreeCADGui.ExpressionBinding(self.form.qsb_min_size).bind( + self.obj, "CharacteristicLengthMin" + ) index_dimension = self.form.cb_dimension.findText(self.dimension) self.form.cb_dimension.setCurrentIndex(index_dimension) index_order = self.form.cb_order.findText(self.order) self.form.cb_order.setCurrentIndex(index_order) - def console_log(self, message="", outputwin_color_type=None): - self.console_message_gmsh = self.console_message_gmsh + ( - '{:4.1f}: '.format( - getOutputWinColor("Logging"), time.time() - self.Start - ) - ) - if outputwin_color_type: - if outputwin_color_type == "#00AA00": # Success is not part of output window parameters - self.console_message_gmsh += '{}
'.format( - outputwin_color_type, message - ) - else: - self.console_message_gmsh += '{}
'.format( - getOutputWinColor(outputwin_color_type), message - ) - else: - self.console_message_gmsh += message + "
" - self.form.te_output.setText(self.console_message_gmsh) - self.form.te_output.moveCursor(QtGui.QTextCursor.End) - - def update_timer_text(self): - # FreeCAD.Console.PrintMessage("timer1\n") - if self.gmsh_runs: - FreeCAD.Console.PrintMessage("timer2\n") - # FreeCAD.Console.PrintMessage("Time: {0:4.1f}: \n".format(time.time() - self.Start)) - self.form.l_time.setText(f"Time: {time.time() - self.Start:4.1f}: ") - def max_changed(self, base_quantity_value): self.clmax = base_quantity_value @@ -168,81 +114,10 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): if index < 0: return self.form.cb_dimension.setCurrentIndex(index) - self.dimension = str(self.form.cb_dimension.itemText(index)) # form returns unicode + self.dimension = self.form.cb_dimension.itemText(index) def choose_order(self, index): if index < 0: return self.form.cb_order.setCurrentIndex(index) - self.order = str(self.form.cb_order.itemText(index)) # form returns unicode - - def get_gmsh_version(self): - from femmesh import gmshtools - - version, full_message = gmshtools.GmshTools(self.obj, self.analysis).get_gmsh_version() - if version[0] and version[1] and version[2]: - messagebox = QtGui.QMessageBox.information - else: - messagebox = QtGui.QMessageBox.warning - messagebox(None, "Gmsh - Information", full_message) - - def run_gmsh(self): - from femmesh import gmshtools - - gmsh_mesh = gmshtools.GmshTools(self.obj, self.analysis) - QApplication.setOverrideCursor(Qt.WaitCursor) - part = self.obj.Shape - if ( - self.obj.MeshRegionList - and part.Shape.ShapeType == "Compound" - and ( - is_of_type(part, "FeatureBooleanFragments") - or is_of_type(part, "FeatureSlice") - or is_of_type(part, "FeatureXOR") - ) - ): - gmsh_mesh.outputCompoundWarning() - self.Start = time.time() - self.form.l_time.setText(f"Time: {time.time() - self.Start:4.1f}: ") - self.console_message_gmsh = "" - self.gmsh_runs = True - self.console_log("We are going to start ...") - self.get_active_analysis() - self.console_log("Start Gmsh ...") - error = "" - try: - error = gmsh_mesh.create_mesh() - except Exception: - error = sys.exc_info()[1] - FreeCAD.Console.PrintError(f"Unexpected error when creating mesh: {error}\n") - if error: - FreeCAD.Console.PrintWarning("Gmsh had warnings:\n") - FreeCAD.Console.PrintWarning(f"{error}\n") - self.console_log("Gmsh had warnings ...", "Warning") - self.console_log(error, "Error") - else: - FreeCAD.Console.PrintMessage("Clean run of Gmsh\n") - self.console_log("Clean run of Gmsh", "#00AA00") - self.console_log("Gmsh done!") - self.form.l_time.setText(f"Time: {time.time() - self.Start:4.1f}: ") - self.Timer.stop() - self.update() - QApplication.restoreOverrideCursor() - - def get_active_analysis(self): - analysis = FemGui.getActiveAnalysis() - if not analysis: - FreeCAD.Console.PrintLog("No active analysis, means no group meshing.\n") - self.analysis = None # no group meshing - else: - for m in analysis.Group: - if m.Name == self.obj.Name: - FreeCAD.Console.PrintLog(f"Active analysis found: {analysis.Name}\n") - self.analysis = analysis # group meshing - break - else: - FreeCAD.Console.PrintLog( - "Mesh is not member of active analysis, means no group meshing.\n" - ) - self.analysis = None # no group meshing - return + self.order = self.form.cb_order.itemText(index)