# Execution module
# Name of particular algo module for each repair class
- self._algo_name = ''
- self.set_algoname(algo_name, is_default_location)
+ 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.
# Default selection level
self._selection_level = selection_level
self._is_level_changed = False
- self._is_local_selection = False
# Connect selection manager
salome_pyqt = SalomePyQt.SalomePyQt()
if is_default_location:
package_dir = Path(__file__).parent.absolute()
- self._algo_name = package_dir.joinpath(algo_name)
+ return package_dir.joinpath(algo_name)
else:
- self._algo_name = algo_name
+ return algo_name
def set_result_name(self, name):
return self._result_widget.LineEdit1.text()
+ def get_selection_level(self):
+ """
+ Return current selection level.
+
+ Args:
+ None.
+
+ Returns:
+ Selection level.
+ """
+
+ return self._selection_level
+
+
def set_selection_level(self, selection_level):
"""
Sets selection level.
# Init it again if the level was changed
if self._is_level_changed:
geom_swig.closeLocalSelection()
- self._is_local_selection = False
- # We need to init a local selection only once
- if not self._is_local_selection:
# Init it here
sel_level = geomBuilder.EnumToLong(self._selection_level)
geom_swig.initLocalSelection(entry, sel_level)
-
- self._is_local_selection = True
else:
# No entry - no local selection
geom_swig.closeLocalSelection()
- self._is_local_selection = False
# We don't need the flag after selection was set
self._is_level_changed = False
+
def on_select_object(self):
"""
Adds selected object to a dialog.
QComboBox, QLabel, QPushButton, QMessageBox
from salome.geom.geomrepairadv.basedlg import BaseDlg
+from salome.geom import geomBuilder
from .geomrepairadv_common import DlgRef_1Spin_QTD
+from .geomrepairadv_execute import execute
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.
"""
+
+ SUBSHAPES_LABEL_TEXT = 'Sub-shapes: '
+
def __init__(self, selection_level = GEOM.EDGE):
+ # Path to Min/max script
+ self._minmax_algo = self.set_algoname('locate_subshapes_limits.py', True)
+
# Implement widget's content here
main_widget = QFrame()
layout = QGridLayout(main_widget)
self._type_widget.setToolTip('Select a type of shape measurement')
self._type_widget.currentIndexChanged.connect(self.on_measurment_type_changed)
+ # Add Min/Max button
+ self._minmax_button = QPushButton("Compute Min/Max")
+ self._minmax_button.clicked.connect(self.on_minmax_button_clicked)
+
# Min/max values widgets
decimals = 2
max_value = sys.float_info.max
self._min_widget = DlgRef_1Spin_QTD('Min', 0, decimals, max_value)
- self._max_widget = DlgRef_1Spin_QTD('Max', 100, decimals, max_value)
+ self._max_widget = DlgRef_1Spin_QTD('Max', 1000, decimals, max_value)
+ self._min_widget.SpinBox_DX.valueChanged.connect(self.on_limit_changed)
+ self._max_widget.SpinBox_DX.valueChanged.connect(self.on_limit_changed)
+
+ # Sub-shapes number
+ self._subshapes_selected = 0
+ self._subshapes_total = 0
+ self._subshapes_label = QLabel()
# Add select button
self._select_button = QPushButton("Show Selected Sub-shapes")
# Add the widgets to layout
layout.addWidget(type_label, 0, 0)
- layout.addWidget(self._type_widget, 1, 0)
- layout.addWidget(self._min_widget, 2, 0)
- layout.addWidget(self._max_widget, 3, 0)
- layout.addWidget(self._select_button, 4, 0)
+ layout.addWidget(self._minmax_button, 1, 0)
+ layout.addWidget(self._type_widget, 2, 0)
+ layout.addWidget(self._min_widget, 3, 0)
+ layout.addWidget(self._max_widget, 4, 0)
+ layout.addWidget(self._subshapes_label, 5, 0)
+ layout.addWidget(self._select_button, 6, 0)
# Init base dialog
BaseDlg.__init__(
self._max_widget.SpinBox_DX.value()]
+ def set_limits(self, min_value, max_value):
+ """
+ Sets given values for min/max limits.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ self._min_widget.SpinBox_DX.setValue(min_value)
+ self._max_widget.SpinBox_DX.setValue(max_value)
+
+
def get_measurment_type(self, index):
"""
Returns selection level based on current measurment type.
return measurment_types[index]
+ def set_subshapes_counters(self, selected, total):
+ """
+ Set counters for selected and total subshapes.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ self._subshapes_selected = selected
+ self._subshapes_total = total
+
+
+ def update_subshapes_label(self):
+ """
+ Updates a text of Sub-Shapes label.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ selected = str(self._subshapes_selected)
+ total = str(self._subshapes_total)
+
+ self._subshapes_label.setText(self.SUBSHAPES_LABEL_TEXT + selected + '/' + total)
+
+
+ def update_subshapes_info(self):
+ """
+ Updates all info about Sub-Shapes in the dialog.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ if not self._selected_object:
+ return
+
+ # Update counters
+ geompy = geomBuilder.New()
+ all_ids = geompy.SubShapeAllIDs(self._selected_object, self.get_selection_level())
+ selected_ids = self.get_local_selection()
+ self.set_subshapes_counters(len(selected_ids), len(all_ids))
+
+ # Update label
+ self.update_subshapes_label()
+
+
def on_measurment_type_changed(self, index):
"""
Changes selection level on type changed.
# Clear pre selected sub-shapes list
self.on_select_subshape()
+ self.update_subshapes_info()
+
+
+ def select_subshapes_in_limits(self):
+ """
+ Updates a text of Sub-Shapes label.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ if not self._selected_object:
+ return
+
+ # Get all sub-shapes
+ geompy = geomBuilder.New()
+ selection_level = self.get_selection_level()
+ subshapes_ids = geompy.SubShapeAllIDs(self._selected_object, selection_level)
+
+ # Iterate over ids to check if it fits to limits
+ # TODO: implement selections
+ limits = self.get_limits()
+ for id in subshapes_ids:
+ # Get a sub-shape by id
+ pass
+
+ # Get related parameter to check it later
+ param = None
+ if selection_level == GEOM.EDGE:
+ # Get a lenght of an edge
+ pass
+ elif selection_level == GEOM.FACE:
+ # Get an area of a face
+ pass
+ elif selection_level == GEOM.SOLID:
+ # Get a volume of a solid
+ pass
+ else:
+ # We shouldn't fall here
+ QMessageBox.warning(
+ None, 'Warning', 'Wrong selection level: %s!' % (selection_level))
+ return
+
+ # Check if it fits to the limits
+ if param >= limits[0] and param <= limits[1]:
+ # Select sub-shape
+ pass
+ else:
+ # Deselect sub-shape
+ pass
+
+ # Update displayed info
+ self.update_subshapes_info()
+
+
+ def on_limit_changed(self):
+ """
+ One of the limits was changed.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ # TODO: Do we need an interactive change here?
+ # self.select_subshapes_in_limits()
+
def on_select_button_clicked(self):
"""
None.
"""
- #TODO: what are we going to do on this click?
- # Should it do a separated script?
- QMessageBox.warning(None, 'Warning', 'Not implemented yet')
+ # Doesn't make any sence without selected object
+ if not self._selected_object:
+ QMessageBox.warning(
+ None, 'Warning', 'You must select an object to see sub-shapes selected!')
+ return
+
+ self.select_subshapes_in_limits()
+
+
+ def on_minmax_button_clicked(self):
+ """
+ Compute Min/Max limits on button click.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ # Doesn't make any sence without selected object
+ if not self._selected_object:
+ QMessageBox.warning(None, 'Warning', 'You must select an object to compute!')
+ return
+
+ # Execute a separated script the same way as it is expected for on_apply() but without dump
+ args = {
+ 'result_name': 'dummy',
+ 'selection_level': self.get_selection_level()
+ }
+
+ limits = execute(self._selected_object, self._minmax_algo, args, False)
+ if len(limits) >= 2:
+ self.set_limits(limits[0], limits[1])
def get_args(self):
Dictionary with arguments for execution.
"""
+ # Update selection with a current values
+ # TODO: should we call it here?
+ # In a worst case scenario we can run it twice
+ # if a user has just pressed selection button.
+ self.select_subshapes_in_limits()
+
+ # Collect current values for the execution
selected_ids = self.get_local_selection()
- current_index = self._type_widget.currentIndex()
- selection_level = self.get_measurment_type(current_index)
- limits = self.get_limits()
- min_selected = 1
+ selection_level = self.get_selection_level()
+ min_selected = 0
if self.is_selection_valid(selected_ids, min_selected):
return {
'selected_ids': selected_ids,
'result_name': self.get_result_name(),
- 'selection_level': selection_level,
- 'min_limit': limits[0],
- 'max_limit': limits[1]
+ 'selection_level': selection_level
}
return None
+ def on_select_object(self):
+ """
+ Override parent's method to display sub-shapes info.
+
+ Args:
+ None.
+
+ Returns:
+ None.
+ """
+
+ # Call parent method first
+ super().on_select_object()
+
+ # Update displayed info
+ self.update_subshapes_info()
+
+
# 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.
if ('source_solid' not in args_dict or
'selected_ids' not in args_dict or
'result_name' not in args_dict or
- 'selection_level' not in args_dict or
- 'min_limit' not in args_dict or
- 'max_limit' not in args_dict):
+ 'selection_level' not in args_dict):
logging.info('Cant execute an algo because the arguments are empty!')
return False
selected_ids = args_dict['selected_ids']
result_name = args_dict['result_name']
selection_level = args_dict['selection_level']
- min_limit = args_dict['min_limit']
- max_limit = args_dict['max_limit']
# Replace the lines below with an actual algorithm
logging.info('Received arguments:')
logging.info('\tselected_ids: %s', selected_ids)
logging.info('\tresult_name: %s', result_name)
logging.info('\tselection_level: %s', selection_level)
- logging.info('\tmin_limit: %s', min_limit)
- logging.info('\tmax_limit: %s', max_limit)
+
progress_emitter.emit()
- sleep(1)
+ sleep(0.1)
+
+ # Make a group
+ group = geompy.CreateGroup(source_solid, selection_level, theName = result_name)
- logging.warning('The algo script is not implemented! Return the copy of the source object...')
- solid = geompy.MakeCopy(source_solid, result_name)
+ # Iterate all over the group's ids and remove unselected
+ group_ids = geompy.GetObjectIDs(group)
+ logging.info('Group Sub-shapes ids: %s', group_ids)
+
+ for subshape_id in group_ids:
+ if subshape_id not in selected_ids:
+ geompy.RemoveObject(group, subshape_id)
+ logging.info('\tSub-shape %s was removed!', subshape_id)
progress_emitter.emit()
- logging.info('Done.')
+ logging.info('Group of selected sub-shapes was created.')
progress_emitter.emit()
- return solid
+ return group
def test():
source_solid = geompy.ImportBREP(test_file)
geompy.addToStudy(source_solid, "source_solid")
+ selection_level = GEOM.EDGE
+
# TODO: Implement for actual algorithm
# Here we just use all ids.
- all_subshapes = geompy.SubShapeAllIDs(source_solid, GEOM.EDGE)
+ all_subshapes = geompy.SubShapeAllIDs(source_solid, selection_level)
args_dict = {
'source_solid': source_solid,
'selected_ids': all_subshapes,
'result_name': 'LocateSubshapes_result',
- 'selection_level': GEOM.EDGE,
- 'min_limit': 0.0,
- 'max_limit': 99.99
+ 'selection_level': selection_level
}
# Dummy emitter
--- /dev/null
+# -*- 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 Locate Subshapes plugin.
+"""
+
+import sys
+import logging
+from time import sleep
+
+import salome
+
+from salome.geom import geomBuilder
+from qtsalome import QFileDialog, QApplication, pyqtSignal
+import GEOM
+
+
+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 result object.
+ """
+
+ logging.info('Run Locate Subshapes algorithm.')
+ progress_emitter.emit()
+
+
+ if ('source_solid' not in args_dict or
+ 'selection_level' not in args_dict):
+
+ logging.info('Cant execute an algo because the arguments are empty!')
+ return False
+
+ source_solid = args_dict['source_solid']
+ selection_level = args_dict['selection_level']
+
+ # Replace the lines below with an actual algorithm
+ logging.info('Received arguments:')
+ logging.info('\tsource_solid: %s', source_solid)
+ logging.info('\tselection_level: %s', selection_level)
+ progress_emitter.emit()
+
+ sleep(1)
+
+ logging.warning('The algo script is not implemented! Return default values...')
+ limits = [0.0, 100.0]
+
+ logging.info('Done.')
+ progress_emitter.emit()
+
+ return limits
+
+
+def test():
+ """
+ Tests execution of repair algo script.
+ """
+
+ logging.basicConfig(level=logging.DEBUG)
+
+ test_file, _ = QFileDialog.getOpenFileName(None, 'Open brep', '/home', 'Brep Files (*.brep)')
+ if not test_file:
+ return
+
+ # test_file = "PartitionCube.brep"
+ source_solid = geompy.ImportBREP(test_file)
+ geompy.addToStudy(source_solid, "source_solid")
+
+ args_dict = {
+ 'source_solid': source_solid,
+ 'selection_level': GEOM.EDGE
+ }
+
+ # Dummy emitter
+ # TODO: doesn't work
+ # progress_emitter = pyqtSignal()
+ progress_emitter = type('DummyEmitter', (object,), {'emit': lambda self: sleep(0.1)})()
+
+ run(args_dict, progress_emitter)
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ test()
+ sys.exit(app.exec_())