mirror of
https://git.salome-platform.org/gitpub/modules/geom.git
synced 2025-01-14 10:40:35 +05:00
468 lines
13 KiB
Python
468 lines
13 KiB
Python
# -*- 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 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 libGEOM_Swig import GEOM_Swig
|
|
import SalomePyQt
|
|
|
|
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, GEOM_SELECTED_SUBSHAPE
|
|
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)
|
|
|
|
# A widget to show selected sub-shapes
|
|
self._sel_subshape_widget = self.create_sel_subshape_widget()
|
|
|
|
# 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
|
|
|
|
# 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)
|
|
self.child_layout.addWidget(self._sel_subshape_widget, 2, 0)
|
|
if child_widget:
|
|
self.child_layout.addWidget(child_widget, 3, 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
|
|
self._is_level_changed = False
|
|
|
|
# Connect selection manager
|
|
salome_pyqt = SalomePyQt.SalomePyQt()
|
|
self._sel_manager = salome_pyqt.getSelection()
|
|
self._sel_connection = \
|
|
self._sel_manager.currentSelectionChanged.connect(self.on_select_object)
|
|
self._sel_subshape_connection = \
|
|
self._sel_manager.currentSelectionChanged.connect(self.on_select_subshape)
|
|
|
|
# Check if we already have selected object
|
|
self.on_select_object()
|
|
|
|
# Default args for execution
|
|
self._is_dump_on = True # enables Python dump
|
|
self._is_copy_on = True # enables passing copy of object into algo script
|
|
|
|
|
|
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
|
|
|
|
args_dict = self.get_args()
|
|
if args_dict:
|
|
execute(self._selected_object,
|
|
self._algo_name,
|
|
args_dict,
|
|
self._is_dump_on,
|
|
self._is_copy_on)
|
|
|
|
|
|
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()
|
|
return package_dir.joinpath(algo_name)
|
|
else:
|
|
return 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 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.
|
|
|
|
Args:
|
|
selection_level - GEOM selection level.
|
|
|
|
Returns:
|
|
None.
|
|
"""
|
|
|
|
# Set the flag to process local selection properly
|
|
if self._selection_level == selection_level:
|
|
self._is_level_changed = False
|
|
else:
|
|
self._is_level_changed = True
|
|
|
|
self._selection_level = selection_level
|
|
|
|
# Update selection for current object
|
|
self.on_select_object()
|
|
|
|
|
|
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()
|
|
|
|
# Set level of selection for specific entry
|
|
if entry:
|
|
# Init it again if the level was changed
|
|
if self._is_level_changed:
|
|
geom_swig.closeLocalSelection()
|
|
|
|
# Init it here
|
|
sel_level = geomBuilder.EnumToLong(self._selection_level)
|
|
geom_swig.initLocalSelection(entry, sel_level)
|
|
else:
|
|
# No entry - no local selection
|
|
geom_swig.closeLocalSelection()
|
|
|
|
# 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.
|
|
|
|
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)
|
|
|
|
# Check if we selected other object in a browser -
|
|
# we need to set a level flag to init a local selection again
|
|
prev_entry = ObjectToID(self._selected_object, EDITOR.study)
|
|
if prev_entry != entry:
|
|
self._is_level_changed = True
|
|
|
|
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 get_local_selection(self):
|
|
"""
|
|
Returns selected sub-shapes ids.
|
|
|
|
Args:
|
|
None.
|
|
|
|
Returns:
|
|
List of ids.
|
|
"""
|
|
|
|
geom_swig = GEOM_Swig()
|
|
selected_ids = geom_swig.getLocalSelection()
|
|
logger.debug('selected_ids: %s', selected_ids)
|
|
|
|
return selected_ids
|
|
|
|
|
|
def is_selection_valid(self, selected_ids, min_selected):
|
|
"""
|
|
Checks number of sub-shapes ids.
|
|
|
|
Args:
|
|
selected_ids - list of selected.
|
|
|
|
Returns:
|
|
True if we have valid number of ids.
|
|
"""
|
|
|
|
if len(selected_ids) < min_selected:
|
|
QMessageBox.warning(
|
|
None,
|
|
'Warning',
|
|
'The algorithm needs at least {} selected sub-shapes!\n'
|
|
'Operation was canceled.'.format(min_selected)
|
|
)
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def create_sel_subshape_widget(self):
|
|
"""
|
|
Returns a widget that lists preliminarily selected for processing sub_shapes.
|
|
|
|
Args:
|
|
None.
|
|
|
|
Returns:
|
|
A new widget.
|
|
"""
|
|
|
|
sel_subshape_widget = DlgRef_1Sel_QTD()
|
|
sel_subshape_widget.TextLabel1.setText(GEOM_SELECTED_SUBSHAPE)
|
|
sel_subshape_widget.PushButton1.clicked.connect(self.on_select_subshape)
|
|
|
|
return sel_subshape_widget
|
|
|
|
|
|
def on_select_subshape(self):
|
|
"""
|
|
Updates pre selected widget.
|
|
|
|
Args:
|
|
None.
|
|
|
|
Returns:
|
|
None.
|
|
"""
|
|
|
|
selected_ids = self.get_local_selection()
|
|
selected_ids_str = ', '.join(str(id) for id in selected_ids)
|
|
self._sel_subshape_widget.LineEdit1.setText(selected_ids_str)
|
|
|
|
|
|
def closeEvent(self, event):
|
|
"""
|
|
Overrides default close envent to reset selection level.
|
|
"""
|
|
|
|
super().closeEvent(event)
|
|
|
|
# Clean up all selection changes
|
|
self._sel_manager.currentSelectionChanged.disconnect(self._sel_connection)
|
|
self._sel_manager.currentSelectionChanged.disconnect(self._sel_subshape_connection)
|
|
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_())
|