mirror of
https://git.salome-platform.org/gitpub/modules/geom.git
synced 2025-01-13 10:10:34 +05:00
[bos #38044][EDF] (2023-T3) Support for automatic reparation. Added execution chain and generic GUI using merge faces plugin as an example.
This commit is contained in:
parent
5f12362860
commit
5995197696
@ -25,6 +25,7 @@ SET(SUBDIRS_COMMON
|
|||||||
GEOMImpl GEOM_I GEOMClient GEOM_I_Superv GEOM_SWIG GEOM_PY
|
GEOMImpl GEOM_I GEOMClient GEOM_I_Superv GEOM_SWIG GEOM_PY
|
||||||
AdvancedEngine
|
AdvancedEngine
|
||||||
STLPlugin BREPPlugin STEPPlugin IGESPlugin XAOPlugin Tools
|
STLPlugin BREPPlugin STEPPlugin IGESPlugin XAOPlugin Tools
|
||||||
|
RepairGUIAdv
|
||||||
)
|
)
|
||||||
|
|
||||||
##
|
##
|
||||||
|
70
src/RepairGUIAdv/CMakeLists.txt
Normal file
70
src/RepairGUIAdv/CMakeLists.txt
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Copyright (C) 2012-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
|
||||||
|
IF(SALOME_BUILD_GUI)
|
||||||
|
INCLUDE(UsePyQt)
|
||||||
|
|
||||||
|
# We're already using these ui templates for C++ RepairGUI
|
||||||
|
SET(geom_dlg_ref ../DlgRef)
|
||||||
|
|
||||||
|
# scripts / static
|
||||||
|
SET(plugin_SCRIPTS
|
||||||
|
geomrepairadv_plugins.py
|
||||||
|
)
|
||||||
|
|
||||||
|
# base scripts
|
||||||
|
SET(_base_SCRIPTS
|
||||||
|
geomrepairadv_common.py
|
||||||
|
geomrepairadv_execute.py
|
||||||
|
geomrepairadv_logger.py
|
||||||
|
geomrepairadv_progress.py
|
||||||
|
geomrepairadv_worker.py
|
||||||
|
locate_subshapes.py
|
||||||
|
merge_faces.py
|
||||||
|
merge_faces_algo.py
|
||||||
|
union_edges.py
|
||||||
|
)
|
||||||
|
|
||||||
|
# gui scripts
|
||||||
|
SET(_gui_SCRIPTS
|
||||||
|
basedlg.py
|
||||||
|
basedlg.ui
|
||||||
|
${geom_dlg_ref}/DlgRef_1Sel_QTD.ui
|
||||||
|
DlgRef_1Spin_QTD.ui # copied because original was promoted to SalomeApp_DoubleSpinBox
|
||||||
|
)
|
||||||
|
|
||||||
|
# uic files / to be processed by pyuic
|
||||||
|
SET(_pyuic_FILES
|
||||||
|
basedlg.ui
|
||||||
|
${geom_dlg_ref}/DlgRef_1Sel_QTD.ui
|
||||||
|
DlgRef_1Spin_QTD.ui # copied because original was promoted to SalomeApp_DoubleSpinBox
|
||||||
|
)
|
||||||
|
|
||||||
|
# scripts / pyuic wrappings
|
||||||
|
PYQT_WRAP_UIC(_pyuic_SCRIPTS ${_pyuic_FILES} TARGET_NAME _target_name_pyuic)
|
||||||
|
|
||||||
|
# --- rules ---
|
||||||
|
SALOME_INSTALL_SCRIPTS("${plugin_SCRIPTS}" ${SALOME_GEOM_INSTALL_PLUGINS})
|
||||||
|
SALOME_INSTALL_SCRIPTS("${_base_SCRIPTS}" ${SALOME_INSTALL_PYTHON}/salome/geom/geomrepairadv)
|
||||||
|
SALOME_INSTALL_SCRIPTS("${_gui_SCRIPTS}" ${SALOME_INSTALL_PYTHON}/salome/geom/geomrepairadv)
|
||||||
|
SALOME_INSTALL_SCRIPTS("${_pyuic_SCRIPTS}" ${SALOME_INSTALL_PYTHON}/salome/geom/geomrepairadv TARGET_NAME _target_name_pyuic_py)
|
||||||
|
# add dependency of compiled py files on uic files in order
|
||||||
|
# to avoid races problems when compiling in parallel
|
||||||
|
ADD_DEPENDENCIES(${_target_name_pyuic_py} ${_target_name_pyuic})
|
||||||
|
ENDIF()
|
81
src/RepairGUIAdv/DlgRef_1Spin_QTD.ui
Normal file
81
src/RepairGUIAdv/DlgRef_1Spin_QTD.ui
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DlgRef_1Spin_QTD</class>
|
||||||
|
<widget class="QWidget" name="DlgRef_1Spin_QTD">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>114</width>
|
||||||
|
<height>51</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QGroupBox" name="GroupBox1">
|
||||||
|
<property name="title">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="TextLabel1">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>TL1</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="SpinBox_DX"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
332
src/RepairGUIAdv/basedlg.py
Normal file
332
src/RepairGUIAdv/basedlg.py
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from traceback import format_exc
|
||||||
|
|
||||||
|
from qtsalome import Qt, QWidget, QMessageBox, QApplication, QGridLayout
|
||||||
|
|
||||||
|
from salome.gui import helper
|
||||||
|
from salome.kernel.studyedit import EDITOR
|
||||||
|
from salome.kernel.services import IDToObject, ObjectToID
|
||||||
|
from salome.geom import geomBuilder
|
||||||
|
from salome.geom.geomtools import GeomStudyTools
|
||||||
|
from libGEOM_Swig import GEOM_Swig
|
||||||
|
|
||||||
|
from .basedlg_ui import Ui_BaseDlg
|
||||||
|
from .geomrepairadv_execute import execute
|
||||||
|
from .geomrepairadv_logger import logger
|
||||||
|
from .geomrepairadv_common import DlgRef_1Sel_QTD, \
|
||||||
|
GEOM_RESULT_NAME_GRP, NAME_LBL, GEOM_SELECTED_LBL, GEOM_SELECTED_SHAPE
|
||||||
|
import GEOM
|
||||||
|
|
||||||
|
class BaseDlg(Ui_BaseDlg, QWidget):
|
||||||
|
"""
|
||||||
|
Base dialog for all GEOM repair widgets.
|
||||||
|
Manages standard buttons (Apply and Close, Apply, Close, Help) and
|
||||||
|
adds a child widget specific for each algorithm that uses
|
||||||
|
this dialog as a base class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Collection of derived singletons
|
||||||
|
_instances = {}
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns a singleton instance of the plugin's dialog.
|
||||||
|
It is mandatory in order to call show without a parent.
|
||||||
|
"""
|
||||||
|
if cls._instances.get(cls, None) is None:
|
||||||
|
cls._instances[cls] = super(BaseDlg, cls).__new__(cls, *args, **kwargs)
|
||||||
|
|
||||||
|
return BaseDlg._instances[cls]
|
||||||
|
|
||||||
|
def __init__(self, child_widget, window_title, algo_name, is_default_location, selection_level):
|
||||||
|
"""
|
||||||
|
First inits the base part of dialog,
|
||||||
|
then puts in place a widget, implemented for a child class.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
child_widget - object to display algorithm specific UI
|
||||||
|
window_title - string to display in the dialog's title bar
|
||||||
|
algo_name - path to specific algorithm module
|
||||||
|
is_default_location - if True, then algo file in the same directory.
|
||||||
|
"""
|
||||||
|
QWidget.__init__(self)
|
||||||
|
|
||||||
|
# Set up the generic user interface from Designer.
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.setWindowTitle(window_title)
|
||||||
|
|
||||||
|
# Selection widgets are common for every algorithm at the moment
|
||||||
|
|
||||||
|
# Widget for result shape
|
||||||
|
# Prepend a result name with a window title without spaces
|
||||||
|
self._result_name = ''.join(window_title.split()) + '_'
|
||||||
|
self._result_widget = DlgRef_1Sel_QTD()
|
||||||
|
self._result_widget.GroupBox1.setTitle(GEOM_RESULT_NAME_GRP)
|
||||||
|
self._result_widget.TextLabel1.setText(NAME_LBL)
|
||||||
|
self._result_widget.LineEdit1.setText(self._result_name)
|
||||||
|
self._result_widget.PushButton1.hide()
|
||||||
|
|
||||||
|
# Widget for selected shape
|
||||||
|
self._selected_widget = DlgRef_1Sel_QTD()
|
||||||
|
self._selected_widget.GroupBox1.setTitle(GEOM_SELECTED_LBL)
|
||||||
|
self._selected_widget.TextLabel1.setText(GEOM_SELECTED_SHAPE)
|
||||||
|
self._selected_widget.PushButton1.clicked.connect(self.on_select_object)
|
||||||
|
|
||||||
|
# Keep references to selected object and its temporary copy
|
||||||
|
# that we need to pass for execution instead of original one.
|
||||||
|
# TODO: decide if we really need to pass a copy.
|
||||||
|
self._selected_object = None
|
||||||
|
self._selected_copy = None
|
||||||
|
|
||||||
|
# Put the common widgets and a child widget for a specific algorithm
|
||||||
|
# in a place right above standard buttons (defined by child_placeholder).
|
||||||
|
self.child_layout = QGridLayout(self.child_placeholder)
|
||||||
|
self.child_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.child_layout.addWidget(self._result_widget, 0, 0)
|
||||||
|
self.child_layout.addWidget(self._selected_widget, 1, 0)
|
||||||
|
if child_widget:
|
||||||
|
self.child_layout.addWidget(child_widget, 2, 0)
|
||||||
|
|
||||||
|
# Set basic button's actions
|
||||||
|
self.buttonOk.clicked.connect(self.on_apply_close)
|
||||||
|
self.buttonApply.clicked.connect(self.on_apply)
|
||||||
|
self.buttonClose.clicked.connect(self.close)
|
||||||
|
self.buttonHelp.clicked.connect(self.on_help)
|
||||||
|
|
||||||
|
# Execution module
|
||||||
|
# Name of particular algo module for each repair class
|
||||||
|
self._algo_name = ''
|
||||||
|
self.set_algoname(algo_name, is_default_location)
|
||||||
|
|
||||||
|
# Let it be always on top of the application.
|
||||||
|
# We need it because this dialog will run without parent.
|
||||||
|
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
||||||
|
|
||||||
|
# Default selection level
|
||||||
|
self._selection_level = selection_level
|
||||||
|
|
||||||
|
# Check if we already have selected object
|
||||||
|
self.on_select_object()
|
||||||
|
|
||||||
|
|
||||||
|
def on_apply_close(self):
|
||||||
|
"""
|
||||||
|
Calls on pressing Apply and Close button.
|
||||||
|
"""
|
||||||
|
self.execute()
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
def on_apply(self):
|
||||||
|
"""
|
||||||
|
Calls on pressing Apply button.
|
||||||
|
"""
|
||||||
|
self.execute()
|
||||||
|
|
||||||
|
|
||||||
|
def on_help(self):
|
||||||
|
"""
|
||||||
|
Calls on pressing Help button.
|
||||||
|
"""
|
||||||
|
QMessageBox.about(None, "Help", "Not implemented yet")
|
||||||
|
|
||||||
|
|
||||||
|
def get_args(self):
|
||||||
|
"""
|
||||||
|
Collects arguments for a repair execution algorithm into a dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with arguments for execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
"""
|
||||||
|
Executes actual algorithm.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._selected_object:
|
||||||
|
QMessageBox.warning(
|
||||||
|
None,
|
||||||
|
'Warning',
|
||||||
|
'You must select an object to repair!'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Make copy to prevent unintentional changing of a source object from the algo script
|
||||||
|
builder = geomBuilder.New()
|
||||||
|
self._selected_copy = builder.MakeCopy(
|
||||||
|
self._selected_object, self.get_result_name() + '_temp')
|
||||||
|
|
||||||
|
args_dict = self.get_args()
|
||||||
|
if args_dict:
|
||||||
|
# Add the copy object first
|
||||||
|
args_dict['source_solid'] = self._selected_copy
|
||||||
|
|
||||||
|
execute(self._algo_name, args_dict)
|
||||||
|
# TODO: do we need to handle here a case if the algo failed?
|
||||||
|
|
||||||
|
# Delete a copy object in any case
|
||||||
|
copy_entry = ObjectToID(self._selected_copy)
|
||||||
|
tools = GeomStudyTools()
|
||||||
|
tools.deleteShape(copy_entry)
|
||||||
|
self._selected_copy = None
|
||||||
|
|
||||||
|
|
||||||
|
def set_algoname(self, algo_name, is_default_location):
|
||||||
|
"""
|
||||||
|
Sets the path to the algorithm.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
algo_name - an algorithm's name.
|
||||||
|
is_default_location - if True, then algo file in the same directory.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
|
||||||
|
if is_default_location:
|
||||||
|
package_dir = Path(__file__).parent.absolute()
|
||||||
|
self._algo_name = package_dir.joinpath(algo_name)
|
||||||
|
else:
|
||||||
|
self._algo_name = algo_name
|
||||||
|
|
||||||
|
|
||||||
|
def set_result_name(self, name):
|
||||||
|
"""
|
||||||
|
Sets a name of the result shape.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name - a provided name.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._result_widget.LineEdit1.setText(name)
|
||||||
|
|
||||||
|
|
||||||
|
def get_result_name(self):
|
||||||
|
"""
|
||||||
|
Sets a name of the result shape.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A name in the related edit line of the dialog.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._result_widget.LineEdit1.text()
|
||||||
|
|
||||||
|
|
||||||
|
def set_selection(self, entry = None):
|
||||||
|
"""
|
||||||
|
Sets selection level to self._selection_level or resets it.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
entry - an item currently selected in the objects browser.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._selection_level:
|
||||||
|
return
|
||||||
|
|
||||||
|
geom_swig = GEOM_Swig()
|
||||||
|
|
||||||
|
# Resets selection level
|
||||||
|
geom_swig.closeLocalSelection()
|
||||||
|
|
||||||
|
# Set level of selection for specific entry
|
||||||
|
if entry:
|
||||||
|
sel_level = geomBuilder.EnumToLong(self._selection_level)
|
||||||
|
geom_swig.initLocalSelection(entry, sel_level)
|
||||||
|
|
||||||
|
|
||||||
|
def on_select_object(self):
|
||||||
|
"""
|
||||||
|
Adds selected object to a dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get selected object
|
||||||
|
sobject, entry = helper.getSObjectSelected()
|
||||||
|
|
||||||
|
# Update selected widget and object
|
||||||
|
if sobject and entry:
|
||||||
|
source_name = EDITOR.getName(sobject)
|
||||||
|
self.set_result_name(self._result_name + source_name)
|
||||||
|
self._selected_widget.LineEdit1.setText(source_name)
|
||||||
|
self._selected_object = IDToObject(entry, EDITOR.study)
|
||||||
|
else:
|
||||||
|
self.set_result_name(self._result_name)
|
||||||
|
self._selected_widget.LineEdit1.clear()
|
||||||
|
self._selected_object = None
|
||||||
|
entry = None
|
||||||
|
|
||||||
|
# Selection level
|
||||||
|
self.set_selection(entry)
|
||||||
|
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""
|
||||||
|
Overrides default close envent to reset selection level.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().closeEvent(event)
|
||||||
|
self.set_selection(None)
|
||||||
|
|
||||||
|
|
||||||
|
# For testing run as a module from geomrepairadv parent directory in
|
||||||
|
# Salome INSTALL, because the dialog needs a generated Ui_BaseDlg class
|
||||||
|
# that we don't have in the SOURCE.
|
||||||
|
# Example:
|
||||||
|
# $ python -m geomrepairadv.basedlg
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
dlg = BaseDlg(None, 'Test base dialog', 'test_algo', True, None)
|
||||||
|
dlg.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
111
src/RepairGUIAdv/basedlg.ui
Normal file
111
src/RepairGUIAdv/basedlg.ui
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>BaseDlg</class>
|
||||||
|
<widget class="QDialog" name="BaseDlg">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>411</width>
|
||||||
|
<height>278</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QGroupBox" name="GroupButtons">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonOk">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Apply and Close</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonApply">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Apply</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Expanding</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>91</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonClose">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Close</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonHelp">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Help</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QFrame" name="child_placeholder">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>buttonOk</tabstop>
|
||||||
|
<tabstop>buttonApply</tabstop>
|
||||||
|
<tabstop>buttonClose</tabstop>
|
||||||
|
<tabstop>buttonHelp</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
59
src/RepairGUIAdv/geomrepairadv_common.py
Normal file
59
src/RepairGUIAdv/geomrepairadv_common.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
from qtsalome import QWidget
|
||||||
|
from SalomePyQt import SalomePyQt
|
||||||
|
|
||||||
|
from salome.geom.geomrepairadv.DlgRef_1Sel_QTD_ui import Ui_DlgRef_1Sel_QTD
|
||||||
|
from salome.geom.geomrepairadv.DlgRef_1Spin_QTD_ui import Ui_DlgRef_1Spin_QTD
|
||||||
|
|
||||||
|
# Constants from /src/GEOMGUI/GEOM_msg_en.ts
|
||||||
|
GEOM_RESULT_NAME_GRP = 'Result name'
|
||||||
|
NAME_LBL = 'Name'
|
||||||
|
GEOM_SELECTED_LBL = 'Name'
|
||||||
|
GEOM_SELECTED_SHAPE = 'Selected shape'
|
||||||
|
|
||||||
|
class DlgRef_1Sel_QTD(Ui_DlgRef_1Sel_QTD, QWidget):
|
||||||
|
"""
|
||||||
|
Helper class to set up a widget for any related dialog.
|
||||||
|
We need it because a class generated from ui file is derived from an object and
|
||||||
|
cannot be added as a widget to a dialog's layout.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
# Set up the user interface from Designer.
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
# Set 'select' icon
|
||||||
|
icon = SalomePyQt.loadIcon('GEOM', 'select1.png')
|
||||||
|
self.PushButton1.setIcon(icon)
|
||||||
|
|
||||||
|
|
||||||
|
class DlgRef_1Spin_QTD(Ui_DlgRef_1Spin_QTD, QWidget):
|
||||||
|
"""
|
||||||
|
Helper class to set up a widget for any related dialog.
|
||||||
|
We need it because a class generated from ui file is derived from an object and
|
||||||
|
cannot be added as a widget to a dialog's layout.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
# Set up the user interface from Designer.
|
||||||
|
self.setupUi(self)
|
146
src/RepairGUIAdv/geomrepairadv_execute.py
Normal file
146
src/RepairGUIAdv/geomrepairadv_execute.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
from .geomrepairadv_progress import RepairProgressDialog
|
||||||
|
from .geomrepairadv_logger import logger
|
||||||
|
|
||||||
|
from qtsalome import Qt, QApplication, QFileDialog
|
||||||
|
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
import salome
|
||||||
|
from salome.geom import geomBuilder
|
||||||
|
|
||||||
|
|
||||||
|
def module_from_filename(filename):
|
||||||
|
"""
|
||||||
|
Create and execute a module by filename.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename - a given python filename.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get the module from the filename
|
||||||
|
basename = os.path.basename(filename)
|
||||||
|
module_name, _ = os.path.splitext(basename)
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location(module_name, filename)
|
||||||
|
if not spec:
|
||||||
|
logger.error('Could not get a spec for %s file!', filename)
|
||||||
|
return None
|
||||||
|
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
if not module:
|
||||||
|
logger.error('Could not get a module for %s file!', filename)
|
||||||
|
return None
|
||||||
|
|
||||||
|
sys.modules[module_name] = module
|
||||||
|
|
||||||
|
if not spec.loader:
|
||||||
|
logger.error('spec.loader is None for %s module!', module_name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def execute(algo_name, args_dict):
|
||||||
|
"""
|
||||||
|
Executes GEOM advanced repair algorithm.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
algo_name - path to the algo module
|
||||||
|
args_dict - dictionary with arguments those are specific for each algo.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
False if the algo failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug('execute() start')
|
||||||
|
|
||||||
|
# Find a module to execute
|
||||||
|
algo_module = module_from_filename(algo_name)
|
||||||
|
logger.debug('algo_module: %s', algo_module)
|
||||||
|
if not algo_module:
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.debug('Create RepairProgressDialog...')
|
||||||
|
progress_dlg = RepairProgressDialog(parent=None, target=algo_module.run, args=args_dict)
|
||||||
|
result = progress_dlg.exec()
|
||||||
|
logger.info('result: %s', result)
|
||||||
|
|
||||||
|
|
||||||
|
def test_execution():
|
||||||
|
"""
|
||||||
|
Tests execution of repair algo script.
|
||||||
|
It uses PartitionCube.brep file to run merge_faces algorithm.
|
||||||
|
|
||||||
|
Because of relative import must be run from a parent dir as a module:
|
||||||
|
$ python -m RepairGUIAdv.geomrepairadv_execute
|
||||||
|
"""
|
||||||
|
|
||||||
|
salome.salome_init()
|
||||||
|
geompy = geomBuilder.New()
|
||||||
|
|
||||||
|
cube_file, _ = QFileDialog.getOpenFileName(None, 'Open brep', '/home', 'Brep Files (*.brep)')
|
||||||
|
if not cube_file:
|
||||||
|
return
|
||||||
|
|
||||||
|
# cube_file = "PartitionCube.brep"
|
||||||
|
source_solid = geompy.ImportBREP(cube_file)
|
||||||
|
|
||||||
|
# Récupération des faces à fusionner
|
||||||
|
face_a = geompy.GetFaceNearPoint(source_solid, geompy.MakeVertex(-143, -127, 250))
|
||||||
|
face_b = geompy.GetFaceNearPoint(source_solid, geompy.MakeVertex(49,-127,250))
|
||||||
|
|
||||||
|
geompy.addToStudy(source_solid, "source_solid")
|
||||||
|
geompy.addToStudyInFather(source_solid, face_a, "face_a")
|
||||||
|
geompy.addToStudyInFather(source_solid, face_b, "face_b")
|
||||||
|
|
||||||
|
|
||||||
|
args_dict = {
|
||||||
|
'source_solid': source_solid,
|
||||||
|
'face_a': face_a,
|
||||||
|
'face_b': face_b,
|
||||||
|
'result_name': 'MergeFaces_result'
|
||||||
|
}
|
||||||
|
|
||||||
|
current_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
algo_filename, _ = QFileDialog.getOpenFileName(
|
||||||
|
None, 'Open alogrithm script', current_dir, 'Python Files (*.py)')
|
||||||
|
if not algo_filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
execute(algo_filename, args_dict)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
test_execution()
|
||||||
|
sys.exit(app.exec_())
|
63
src/RepairGUIAdv/geomrepairadv_logger.py
Normal file
63
src/RepairGUIAdv/geomrepairadv_logger.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qtsalome import QPlainTextEdit, pyqtSignal, QObject, pyqtSlot
|
||||||
|
|
||||||
|
from salome.kernel.logger import Logger
|
||||||
|
logger = Logger("salome.geom.geomrepairadv", level = logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
class QTextEditLogger(QObject, logging.Handler):
|
||||||
|
"""
|
||||||
|
Provides a QPlainTextEdit text_widget that automaticly filled
|
||||||
|
by logs text from the logger handler.
|
||||||
|
"""
|
||||||
|
|
||||||
|
append_text = pyqtSignal(str)
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent)
|
||||||
|
super(logging.Handler).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
self.text_widget = QPlainTextEdit(parent)
|
||||||
|
self.text_widget.setReadOnly(True)
|
||||||
|
self.append_text.connect(self.write_log)
|
||||||
|
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
'%(levelname)s : %(asctime)s : [%(filename)s:%(funcName)s:%(lineno)s] : %(message)s')
|
||||||
|
self.setFormatter(formatter)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
msg = self.format(record)
|
||||||
|
self.append_text.emit(msg)
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def write_log(self, log_text):
|
||||||
|
"""
|
||||||
|
Appends a given log to the text widget.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.text_widget.appendPlainText(log_text)
|
||||||
|
self.text_widget.centerCursor() # scroll to the bottom
|
69
src/RepairGUIAdv/geomrepairadv_plugins.py
Normal file
69
src/RepairGUIAdv/geomrepairadv_plugins.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import salome_pluginsmanager
|
||||||
|
|
||||||
|
# Plugins entry points
|
||||||
|
# For new plugins create a function that shows related dialog,
|
||||||
|
# then add it into plugin manager below.
|
||||||
|
|
||||||
|
def locate_subshapes(context):
|
||||||
|
"""
|
||||||
|
Opens Locate Subshapes plugin's dialog.
|
||||||
|
"""
|
||||||
|
from salome.geom.geomrepairadv.locate_subshapes import LocateSubShapesDlg
|
||||||
|
dialog = LocateSubShapesDlg()
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def merge_faces(context):
|
||||||
|
"""
|
||||||
|
Opens Merge Faces plugin's dialog.
|
||||||
|
"""
|
||||||
|
from salome.geom.geomrepairadv.merge_faces import MergeFacesDlg
|
||||||
|
dialog = MergeFacesDlg()
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def union_edges(context):
|
||||||
|
"""
|
||||||
|
Opens Union Edges plugin's dialog.
|
||||||
|
"""
|
||||||
|
from salome.geom.geomrepairadv.union_edges import UnionEdgesDlg
|
||||||
|
dialog = UnionEdgesDlg()
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
|
||||||
|
# Add plugins to a manager with a given menu titles and tooltips
|
||||||
|
|
||||||
|
salome_pluginsmanager.AddFunction(
|
||||||
|
'Locate Subshapes',
|
||||||
|
'Locates the sub-shapes of a compound by length, area or volume depending on whether it is an '
|
||||||
|
'EDGE, a FACE or a SOLID',
|
||||||
|
locate_subshapes)
|
||||||
|
|
||||||
|
salome_pluginsmanager.AddFunction(
|
||||||
|
'Merge Faces',
|
||||||
|
'Merges selected faces with a given precision',
|
||||||
|
merge_faces)
|
||||||
|
|
||||||
|
salome_pluginsmanager.AddFunction(
|
||||||
|
'Union Edges',
|
||||||
|
'Merges edges of selected face',
|
||||||
|
union_edges)
|
207
src/RepairGUIAdv/geomrepairadv_progress.py
Normal file
207
src/RepairGUIAdv/geomrepairadv_progress.py
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
from qtsalome import QApplication, QPlainTextEdit, \
|
||||||
|
QDialog, QVBoxLayout, QProgressDialog, QPushButton
|
||||||
|
|
||||||
|
from .geomrepairadv_logger import QTextEditLogger
|
||||||
|
from .geomrepairadv_worker import Worker
|
||||||
|
|
||||||
|
|
||||||
|
class RepairProgressDialog(QDialog, QPlainTextEdit):
|
||||||
|
"""
|
||||||
|
Dialog to show progress bar and log window during of execution
|
||||||
|
of a shape repair script.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None, target=None, args=None):
|
||||||
|
"""
|
||||||
|
Inits progress dialogs widgets and runs a target function in a thread.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent - parent dialog
|
||||||
|
target - target function to run
|
||||||
|
args - arguments to pass into a target.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.setWindowTitle('Shape Repair')
|
||||||
|
|
||||||
|
# QProgressDialog uses manual layout, then it's not easy to customize it.
|
||||||
|
# So, we use it as a widget that is embedded into the dialog.
|
||||||
|
self.progress = QProgressDialog()
|
||||||
|
self.progress.setLabelText('Operation in progress...')
|
||||||
|
self.progress.setAutoClose(False)
|
||||||
|
self.progress.setAutoReset(False)
|
||||||
|
|
||||||
|
# Override default cancel slot to prevent progress from hiding
|
||||||
|
self.cancel_button = self.progress.findChild(QPushButton)
|
||||||
|
self.cancel_button.clicked.disconnect(self.progress.canceled)
|
||||||
|
self.cancel_button.clicked.connect(self.cancel)
|
||||||
|
|
||||||
|
# Helper flag to decide if we need to change button or close the dialog
|
||||||
|
self.canceled = False
|
||||||
|
|
||||||
|
# Set logger to redirect logs output into the text widget
|
||||||
|
self.log_handler = QTextEditLogger(self)
|
||||||
|
logging.getLogger().addHandler(self.log_handler)
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Layout widgets
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
layout.addWidget(self.log_handler.text_widget)
|
||||||
|
layout.addWidget(self.progress)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
# Setup and run target function in a working thread
|
||||||
|
self.thread = Worker(parent=self, target=target, args=args)
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
"""
|
||||||
|
Replicates QProgressDialog.cancel() method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# We need to keep the dialog opened on the first click
|
||||||
|
# so we can see the log output.
|
||||||
|
# After that we can close it with a second click.
|
||||||
|
if self.canceled:
|
||||||
|
self.close()
|
||||||
|
else:
|
||||||
|
# Terminates the execution of the thread.
|
||||||
|
# TODO: find out if we can do it with requestInterruption()
|
||||||
|
self.thread.terminate()
|
||||||
|
|
||||||
|
self.progress.setLabelText('Canceled!')
|
||||||
|
self.progress.setCancelButtonText('Close')
|
||||||
|
|
||||||
|
# Next click we exit
|
||||||
|
self.canceled = True
|
||||||
|
|
||||||
|
|
||||||
|
def on_failed(self):
|
||||||
|
"""
|
||||||
|
Decided what to do if opreation failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.progress.setLabelText('Operation failed!')
|
||||||
|
self.progress.setCancelButtonText('Close')
|
||||||
|
|
||||||
|
self.canceled = True
|
||||||
|
|
||||||
|
|
||||||
|
def on_completed(self):
|
||||||
|
"""
|
||||||
|
Decided what to do when opreation completed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.progress.setLabelText('Completed!')
|
||||||
|
self.progress.setCancelButtonText('Close')
|
||||||
|
|
||||||
|
self.canceled = True
|
||||||
|
|
||||||
|
|
||||||
|
def value(self):
|
||||||
|
"""
|
||||||
|
Replicates QProgressDialog.value() method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.progress.value()
|
||||||
|
|
||||||
|
|
||||||
|
def setValue(self, progress):
|
||||||
|
"""
|
||||||
|
Replicates QProgressDialog.setValue() method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
progress - a new value for a progress bar.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.progress.setValue(progress)
|
||||||
|
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
Process a close event to remove a log handler.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logging.getLogger().removeHandler(self.log_handler)
|
||||||
|
|
||||||
|
super().close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_thread():
|
||||||
|
"""
|
||||||
|
Tests running a test function in a thread while
|
||||||
|
show a progress with RepairProgressDialog dialog.
|
||||||
|
|
||||||
|
Because of relative import must be run from a parent dir as a module:
|
||||||
|
$ python -m RepairGUIAdv.geomrepairadv_progress
|
||||||
|
"""
|
||||||
|
|
||||||
|
progress_dlg = RepairProgressDialog(parent=None, target=test, args=None)
|
||||||
|
result = progress_dlg.exec()
|
||||||
|
logging.info('result: %s', result)
|
||||||
|
|
||||||
|
|
||||||
|
def test(args, progress_emitter):
|
||||||
|
"""
|
||||||
|
Tests logging and progress update with RepairProgressDialog.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if args:
|
||||||
|
pass
|
||||||
|
|
||||||
|
progress_emitter.emit()
|
||||||
|
logging.debug('debug msg')
|
||||||
|
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
progress_emitter.emit()
|
||||||
|
logging.info('info msg')
|
||||||
|
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
progress_emitter.emit()
|
||||||
|
logging.warning('warning msg')
|
||||||
|
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
logging.error('error msg')
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
test_thread()
|
||||||
|
sys.exit(app.exec_())
|
119
src/RepairGUIAdv/geomrepairadv_worker.py
Normal file
119
src/RepairGUIAdv/geomrepairadv_worker.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import inspect
|
||||||
|
from traceback import format_exc
|
||||||
|
|
||||||
|
from qtsalome import QApplication, pyqtSignal, QThread, Qt
|
||||||
|
from .geomrepairadv_logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
class Worker(QThread):
|
||||||
|
"""
|
||||||
|
Creates a tread to run a given target function with a progress dialog as a parent.
|
||||||
|
"""
|
||||||
|
|
||||||
|
progress_update = pyqtSignal(int)
|
||||||
|
thread_failed = pyqtSignal()
|
||||||
|
work_completed = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None, target=None, args=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
# Set target function and it's arguments
|
||||||
|
self.target = target
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
# Update a progress bar each time we receive an update signal
|
||||||
|
self.progress_update.connect(parent.setValue)
|
||||||
|
self.thread_failed.connect(parent.on_failed)
|
||||||
|
self.work_completed.connect(parent.on_completed)
|
||||||
|
|
||||||
|
# Calculate total amount of lines in executed function
|
||||||
|
source_lines = inspect.getsourcelines(target)
|
||||||
|
total_lines = len(source_lines[0])
|
||||||
|
first_line = source_lines[1]
|
||||||
|
|
||||||
|
# Set a progress emitter to update the progress from the target
|
||||||
|
self.progress_emitter = ProgressEmitter(self.progress_update, total_lines, first_line)
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
Runs the given target function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Wait mode cursor
|
||||||
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
||||||
|
|
||||||
|
self.target(self.args, self.progress_emitter)
|
||||||
|
|
||||||
|
# Reset the progress when finished
|
||||||
|
self.progress_update.emit(100)
|
||||||
|
self.work_completed.emit()
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
logger.error(format_exc())
|
||||||
|
self.thread_failed.emit()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
QApplication.restoreOverrideCursor()
|
||||||
|
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
"""
|
||||||
|
Overrides default terminate() to add some clean up.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().terminate()
|
||||||
|
|
||||||
|
# Termination doesn't call a final block inside run()
|
||||||
|
QApplication.restoreOverrideCursor()
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressEmitter():
|
||||||
|
"""
|
||||||
|
Helper class to reduce code repetition while update progress
|
||||||
|
from a function executed in a separated thread.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, progress_update, total_lines, first_line):
|
||||||
|
self.progress_update = progress_update
|
||||||
|
self.first_line = first_line
|
||||||
|
|
||||||
|
self.progress_percent = total_lines / 100.0
|
||||||
|
logger.debug('self.progress_percent: %f', self.progress_percent)
|
||||||
|
|
||||||
|
|
||||||
|
def emit(self):
|
||||||
|
"""
|
||||||
|
Call this methid in a target function to update a progress value
|
||||||
|
based on a currently executed line number.
|
||||||
|
"""
|
||||||
|
|
||||||
|
line = inspect.getframeinfo(inspect.stack()[1][0]).lineno
|
||||||
|
logger.debug('line: %d', line)
|
||||||
|
progress_value = (line - self.first_line) / self.progress_percent
|
||||||
|
logger.debug('progress_value: %d', progress_value)
|
||||||
|
|
||||||
|
self.progress_update.emit(int(progress_value))
|
55
src/RepairGUIAdv/locate_subshapes.py
Normal file
55
src/RepairGUIAdv/locate_subshapes.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from qtsalome import QGridLayout, QFrame, QApplication
|
||||||
|
|
||||||
|
from salome.geom.geomrepairadv.basedlg import BaseDlg
|
||||||
|
import GEOM
|
||||||
|
|
||||||
|
class LocateSubShapesDlg(BaseDlg):
|
||||||
|
"""
|
||||||
|
Dialog for Locate Subshapes plugin that selects the sub-shapes of a compound
|
||||||
|
by length, area or volume depending on whether it is an EDGE, a FACE or a SOLID.
|
||||||
|
"""
|
||||||
|
def __init__(self, selection_level = GEOM.COMPOUND):
|
||||||
|
# Implement widget's content here
|
||||||
|
main_widget = QFrame()
|
||||||
|
layout = QGridLayout(main_widget)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
BaseDlg.__init__(
|
||||||
|
self, main_widget, 'Locate Subshapes', 'locate_subshapes_algo.py', False, selection_level)
|
||||||
|
|
||||||
|
|
||||||
|
# For testing run as a module from geomrepairadv parent directory in
|
||||||
|
# Salome INSTALL, because the dialog needs a generated Ui_BaseDlg class
|
||||||
|
# that we don't have in the SOURCE.
|
||||||
|
# Example:
|
||||||
|
# $ python -m geomrepairadv.locate_subshapes
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
dlg = LocateSubShapesDlg(None)
|
||||||
|
dlg.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
114
src/RepairGUIAdv/merge_faces.py
Normal file
114
src/RepairGUIAdv/merge_faces.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from qtsalome import QGridLayout, QFrame, QMessageBox, QApplication
|
||||||
|
|
||||||
|
from libGEOM_Swig import GEOM_Swig
|
||||||
|
from salome.geom import geomBuilder
|
||||||
|
from .geomrepairadv_logger import logger
|
||||||
|
from .basedlg import BaseDlg
|
||||||
|
from .geomrepairadv_common import DlgRef_1Spin_QTD
|
||||||
|
import GEOM
|
||||||
|
|
||||||
|
class MergeFacesDlg(BaseDlg):
|
||||||
|
"""
|
||||||
|
Dialog for Merge Faces plugin that merges selected faces with a given precision.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, selection_level = GEOM.FACE):
|
||||||
|
# Make layout for new widgets
|
||||||
|
main_widget = QFrame()
|
||||||
|
layout = QGridLayout(main_widget)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# Precision widget
|
||||||
|
self._precision_widget = DlgRef_1Spin_QTD()
|
||||||
|
self._precision_widget.TextLabel1.setText('Precision')
|
||||||
|
layout.addWidget(self._precision_widget, 0, 0)
|
||||||
|
|
||||||
|
BaseDlg.__init__(
|
||||||
|
self, main_widget, 'Merge Faces', 'merge_faces_algo.py', True, selection_level)
|
||||||
|
|
||||||
|
|
||||||
|
def get_precision(self):
|
||||||
|
"""
|
||||||
|
Returns current precision value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Double.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._precision_widget.SpinBox_DX.value()
|
||||||
|
|
||||||
|
|
||||||
|
def get_args(self):
|
||||||
|
"""
|
||||||
|
Collects arguments for a repair execution algorithm into a dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with arguments for execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
geom_swig = GEOM_Swig()
|
||||||
|
faces_ids = geom_swig.getLocalSelection()
|
||||||
|
logger.debug('faces_ids: %s', faces_ids)
|
||||||
|
|
||||||
|
if len(faces_ids) < 2:
|
||||||
|
QMessageBox.warning(
|
||||||
|
None,
|
||||||
|
'Warning',
|
||||||
|
'The algorithm needs at least two selected faces!\nMerging was canceled.'
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get faces from a temporary copy object
|
||||||
|
builder = geomBuilder.New()
|
||||||
|
faces = builder.SubShapes(self._selected_copy, faces_ids)
|
||||||
|
logger.debug('faces: %s', faces)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'face_a': faces[0],
|
||||||
|
'face_b': faces[1],
|
||||||
|
'result_name': self.get_result_name(),
|
||||||
|
'precision': self.get_precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# For testing run as a module from geomrepairadv parent directory in
|
||||||
|
# Salome INSTALL, because the dialog needs a generated Ui_BaseDlg class
|
||||||
|
# that we don't have in the SOURCE.
|
||||||
|
# Example:
|
||||||
|
# $ python -m geomrepairadv.merge_faces
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
dlg = MergeFacesDlg(None)
|
||||||
|
dlg.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
175
src/RepairGUIAdv/merge_faces_algo.py
Executable file
175
src/RepairGUIAdv/merge_faces_algo.py
Executable file
@ -0,0 +1,175 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
"""Example of algorithm script for GEOM Merge Faces plugin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import salome
|
||||||
|
|
||||||
|
from salome.geom import geomBuilder
|
||||||
|
from qtsalome import QFileDialog, QApplication, pyqtSignal
|
||||||
|
|
||||||
|
|
||||||
|
salome.salome_init()
|
||||||
|
geompy = geomBuilder.New()
|
||||||
|
|
||||||
|
|
||||||
|
def run(args_dict, progress_emitter):
|
||||||
|
"""
|
||||||
|
Helper function to call run() with arguments parsed from dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args_dict - arguments as pairs string : any type value
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string with result description.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logging.info('Run Merge Faces algorithm.')
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
|
||||||
|
if ('source_solid' not in args_dict or
|
||||||
|
'face_a' not in args_dict or
|
||||||
|
'face_b' not in args_dict or
|
||||||
|
'result_name' not in args_dict):
|
||||||
|
|
||||||
|
logging.info('Cant execute an algo because the arguments are empty!')
|
||||||
|
return False
|
||||||
|
|
||||||
|
source_solid = args_dict['source_solid']
|
||||||
|
face_a = args_dict['face_a']
|
||||||
|
face_b = args_dict['face_b']
|
||||||
|
result_name = args_dict['result_name']
|
||||||
|
|
||||||
|
|
||||||
|
logging.info('Creating of two faces...')
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
# Fusion des deux faces
|
||||||
|
partition = geompy.MakePartition([face_a, face_b],[])
|
||||||
|
points = [geompy.GetVertexNearPoint(partition, geompy.MakeVertex(-298, 29, 250)),
|
||||||
|
geompy.GetVertexNearPoint(partition, geompy.MakeVertex(178, 29, 250)),
|
||||||
|
geompy.GetVertexNearPoint(partition, geompy.MakeVertex(178, -282, 250)),
|
||||||
|
geompy.GetVertexNearPoint(partition, geompy.MakeVertex(-298, -282, 250))]
|
||||||
|
wire = geompy.MakePolyline(points,True)
|
||||||
|
fused_face = geompy.MakeFaceWires([wire], True)
|
||||||
|
geompy.addToStudy(fused_face, "fused_face")
|
||||||
|
|
||||||
|
logging.info('Creating of a new geometry from the source brep...')
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
sleep(5)
|
||||||
|
|
||||||
|
# Fusion des deux faces au sein de la boite + nettoyage de la boite
|
||||||
|
points = [geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(-298, 29, 250)),
|
||||||
|
geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(178, 29, 250)),
|
||||||
|
geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(178, -282, 250)),
|
||||||
|
geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(-298, -282, 250)),
|
||||||
|
geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(-298, 29, 0)),
|
||||||
|
geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(178, 29, 0)),
|
||||||
|
geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(178, -282, 0)),
|
||||||
|
geompy.GetVertexNearPoint(source_solid, geompy.MakeVertex(-298, -282, 0))]
|
||||||
|
# ### Fusion des deux faces
|
||||||
|
wire = geompy.MakePolyline(points[:4],True)
|
||||||
|
faces = [geompy.MakeFaceWires([wire], True)]
|
||||||
|
|
||||||
|
logging.info('Cleaning of the new geometry...')
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
sleep(5)
|
||||||
|
|
||||||
|
# Uncomment to simulate exception handling in a thread worker class
|
||||||
|
# raise Exception
|
||||||
|
|
||||||
|
# ### Nettoyage des 4 faces latérales
|
||||||
|
wire = geompy.MakePolyline([points[3], points[2], points[6], points[7]],True)
|
||||||
|
faces.append(geompy.MakeFaceWires([wire], True))
|
||||||
|
wire = geompy.MakePolyline([points[0], points[3], points[7], points[4]],True)
|
||||||
|
faces.append(geompy.MakeFaceWires([wire], True))
|
||||||
|
wire = geompy.MakePolyline([points[1], points[0], points[4], points[5]],True)
|
||||||
|
faces.append(geompy.MakeFaceWires([wire], True))
|
||||||
|
wire = geompy.MakePolyline([points[2], points[1], points[5], points[6]],True)
|
||||||
|
faces.append(geompy.MakeFaceWires([wire], True))
|
||||||
|
|
||||||
|
# ### Récupération de la dernière face
|
||||||
|
faces.append(geompy.GetFaceNearPoint(source_solid, geompy.MakeVertex(-59, -127, 0)))
|
||||||
|
|
||||||
|
logging.info('Creating a solid...')
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
sleep(5)
|
||||||
|
|
||||||
|
# ### Création du solide
|
||||||
|
shell = geompy.MakeShell(faces)
|
||||||
|
solid = geompy.MakeSolid(shell)
|
||||||
|
|
||||||
|
geompy.addToStudy(solid, result_name)
|
||||||
|
|
||||||
|
logging.info('Merge Faces algorithm was completed successfully.')
|
||||||
|
progress_emitter.emit()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def test():
|
||||||
|
"""
|
||||||
|
Tests execution of repair algo script.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cube_file, _ = QFileDialog.getOpenFileName(None, 'Open brep', '/home', 'Brep Files (*.brep)')
|
||||||
|
if not cube_file:
|
||||||
|
return
|
||||||
|
|
||||||
|
# cube_file = "PartitionCube.brep"
|
||||||
|
source_solid = geompy.ImportBREP(cube_file)
|
||||||
|
|
||||||
|
# Récupération des faces à fusionner
|
||||||
|
face_a = geompy.GetFaceNearPoint(source_solid, geompy.MakeVertex(-143, -127, 250))
|
||||||
|
face_b = geompy.GetFaceNearPoint(source_solid, geompy.MakeVertex(49,-127,250))
|
||||||
|
|
||||||
|
geompy.addToStudy(source_solid, "source_solid")
|
||||||
|
geompy.addToStudyInFather(source_solid, face_a, "face_a")
|
||||||
|
geompy.addToStudyInFather(source_solid, face_b, "face_b")
|
||||||
|
|
||||||
|
|
||||||
|
args_dict = {
|
||||||
|
'source_solid': source_solid,
|
||||||
|
'face_a': face_a,
|
||||||
|
'face_b': face_b,
|
||||||
|
'result_name': 'MergeFaces_result'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dummy emitter
|
||||||
|
# TODO: doesn't work
|
||||||
|
progress_emitter = pyqtSignal()
|
||||||
|
|
||||||
|
run(args_dict, progress_emitter)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
test()
|
||||||
|
sys.exit(app.exec_())
|
54
src/RepairGUIAdv/union_edges.py
Normal file
54
src/RepairGUIAdv/union_edges.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2024 EDF
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
||||||
|
#
|
||||||
|
# Author : Konstantin Leontev (OpenCascade S.A.S)
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from qtsalome import QGridLayout, QFrame, QApplication
|
||||||
|
|
||||||
|
from salome.geom.geomrepairadv.basedlg import BaseDlg
|
||||||
|
import GEOM
|
||||||
|
|
||||||
|
class UnionEdgesDlg(BaseDlg):
|
||||||
|
"""
|
||||||
|
Dialog for Union Edges plugin that unifies edges of selected face.
|
||||||
|
"""
|
||||||
|
def __init__(self, selection_level = GEOM.COMPOUND):
|
||||||
|
# Implement widget's content here
|
||||||
|
main_widget = QFrame()
|
||||||
|
layout = QGridLayout(main_widget)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
BaseDlg.__init__(
|
||||||
|
self, main_widget, 'Union Edges', 'union_edges_algo.py', False, selection_level)
|
||||||
|
|
||||||
|
|
||||||
|
# For testing run as a module from geomrepairadv parent directory in
|
||||||
|
# Salome INSTALL, because the dialog needs a generated Ui_BaseDlg class
|
||||||
|
# that we don't have in the SOURCE.
|
||||||
|
# Example:
|
||||||
|
# $ python -m geomrepairadv.union_edges
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
dlg = UnionEdgesDlg(None)
|
||||||
|
dlg.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
Loading…
Reference in New Issue
Block a user