import webbrowser from PySide2 import QtCore from PySide2.QtWidgets import ( QCheckBox, QHBoxLayout, QLabel, QMainWindow, QMessageBox, QProgressBar, QPushButton, QVBoxLayout, QWidget, ) from ..const.ui import ( HELP_URL, MARGIN_BODY_LEFT, MARGIN_BODY_RIGHT, MARGIN_BODY_TOP, MARGIN_BOTTOM, MARGIN_HEADER_BOTTOM, MARGIN_HEADER_LEFT, MARGIN_HEADER_RIGHT, MARGIN_HEADER_TOP, SPACING, ) from ..reader.dna import load_dna from ..ui.build_options_widget import BuildOptionsWidget from ..ui.elements import Elements from ..ui.file_chooser import FileChooser from ..ui.mesh_tree_list import MeshTreeList from ..version import __version__ class ElementsCreator: """ Used for creating elements that get added to the main window Attributes ---------- @type window: QMainWindow @param window: The main UI window @type elements: Elements @param elements: The container with references to the UI elements @type body: QVBoxLayout @param body: The body layout @type header: QHBoxLayout @param header: The header layout """ def __init__(self, window: QMainWindow, elements: Elements) -> None: self.window = window self.elements = elements self.body: QVBoxLayout = None self.header: QHBoxLayout = None def create_body(self) -> QVBoxLayout: """ Creates the main body layout and adds needed widgets @rtype: QVBoxLayout @returns: The created vertical box layout with the widgets added """ self.body = QVBoxLayout() self.body.setContentsMargins( MARGIN_BODY_LEFT, MARGIN_BODY_TOP, MARGIN_BODY_RIGHT, MARGIN_BOTTOM ) self.body.setSpacing(SPACING) self.create_body_widgets() return self.body def create_body_widgets(self) -> None: """Creates and adds all needed widgets to the body""" self.create_dna_selector() self.elements.mesh_tree_list = self.create_mesh_selector() self.create_build_options() self.elements.select_gui_path = self.create_gui_selector() self.elements.select_analog_gui_path = self.create_analog_gui_selector() self.elements.select_aas_path = self.create_aas_selector() self.elements.process_btn = self.create_process_btn(self.window) self.elements.progress_bar = self.create_progress_bar(self.window) def create_header(self) -> QHBoxLayout: """ Creates and adds to the header widget @rtype: QHBoxLayout @returns: The created horizontal box layout with the widgets added """ self.header = QHBoxLayout() self.create_header_widgets() return self.header def create_header_widgets(self) -> None: """Creates and adds all needed widgets to the header""" label = QLabel("v" + __version__) btn = self.create_help_btn() self.header.addWidget(label) self.header.addStretch(1) self.header.addWidget(btn) self.header.setContentsMargins( MARGIN_HEADER_LEFT, MARGIN_HEADER_TOP, MARGIN_HEADER_RIGHT, MARGIN_HEADER_BOTTOM, ) self.header.setSpacing(SPACING) def create_help_btn(self) -> QWidget: """ Creates the help button widget @rtype: QHBoxLayout @returns: The created horizontal box layout with the widgets added """ btn = QPushButton(self.window) btn.setText(" ? ") btn.setToolTip("Help") btn.clicked.connect(self.on_help) return btn def on_help(self) -> None: """The method that gets called when the help button is clicked""" if HELP_URL: webbrowser.open(HELP_URL) else: QMessageBox.about( self.window, "About", "Sorry, this application does not have documentation yet.", ) def create_dna_selector(self) -> QWidget: """ Creates and adds the DNA selector widget @rtype: QWidget @returns: The created DNA selector widget """ widget = QWidget() self.elements.select_dna_path = self.create_dna_chooser() self.elements.load_dna_btn = self.create_load_dna_button( self.elements.select_dna_path ) self.elements.select_dna_path.fc_text_field.textChanged.connect( lambda: self.on_dna_selected(self.elements.select_dna_path) ) layout = QVBoxLayout() layout.addWidget(self.elements.select_dna_path) layout.addWidget(self.elements.load_dna_btn) layout.setContentsMargins(0, 0, 0, 0) widget.setLayout(layout) self.body.addWidget(widget) return widget def on_dna_selected(self, input: FileChooser) -> None: """ The method that gets called when a DNA file gets selected @type input: FileChooser @param input: The file chooser object corresponding to the DNA selector widget """ enabled = Elements.get_file_path(input) is not None self.elements.load_dna_btn.setEnabled(enabled) self.elements.process_btn.setEnabled(False) def create_dna_chooser(self) -> FileChooser: """ Creates and adds the DNA chooser widget @rtype: FileChooser @returns: Dna chooser widget """ return self.create_file_chooser( "Path:", "Select a DNA file", "DNA files (*.dna)" ) def create_load_dna_button(self, dna_input: FileChooser) -> QWidget: """ Creates and adds the load DNA button widget @type input: FileChooser @param input: The file chooser object corresponding to the DNA selector widget @rtype: QWidget @returns: The created load DNA button widget """ btn = QPushButton("Load DNA") btn.setEnabled(False) btn.clicked.connect(lambda: self.on_load_dna_clicked(dna_input)) return btn def on_load_dna_clicked(self, input: FileChooser) -> None: """ The method that gets called when a DNA file gets selected @type input: FileChooser @param input: The file chooser object corresponding to the DNA selector widget """ self.elements.main_widget.setEnabled(False) QtCore.QCoreApplication.processEvents() dna_file_path = Elements.get_file_path(input) if dna_file_path: self.elements.dna = load_dna(dna_file_path) lod_count = self.elements.dna.descriptor.lod_count meshes = self.elements.dna.definition.meshes self.elements.mesh_tree_list.fill_mesh_list(lod_count, meshes) self.elements.joints_cb.setEnabled(True) self.elements.process_btn.setEnabled(False) self.elements.main_widget.setEnabled(True) def create_mesh_selector(self) -> MeshTreeList: """ Creates and adds a mesh tree list where the entries are grouped by lods, this is used for selecting the meses that need to be processed @rtype: MeshTreeList @returns: The created mesh tree list widget """ widget = MeshTreeList(self.elements) self.body.addWidget(widget) return widget def create_file_chooser(self, label: str, caption: str, filter: str) -> FileChooser: """ Creates a file chooser widget that is used for selecting file paths @type label: str @param label: The label in the FileDialog that pops up @type caption: str @param caption: The caption in the FileDialog that pops up @type filter: str @param filter: The file filter that is used in the FileDialog @rtype: FileChooser @returns: The created file chooser object """ widget = FileChooser( label, self.window, dialog_caption=caption, dialog_filter=filter, on_changed=self.on_generic_changed, ) self.body.addWidget(widget) return widget def create_gui_selector(self) -> FileChooser: """ Creates the gui selector widget @rtype: FileChooser @returns: Gui selector widget """ return self.create_file_chooser( "Gui path:", "Select the gui file", "gui files (*.ma)" ) def create_aas_selector(self) -> FileChooser: """ Creates and adds the additional assembly script selector widget @rtype: FileChooser @returns: Additional assembly script selector widget """ return self.create_file_chooser( "After assembly script path:", "Select the aas file", "python script (*.py)" ) def create_analog_gui_selector(self) -> FileChooser: """ Creates and adds the analog gui selector widget @rtype: FileChooser @returns: Analog gui selector widget """ return self.create_file_chooser( "Analog gui path:", "Select the analog gui file", "analog gui files (*.ma)" ) def create_build_options(self) -> None: """Creates and adds the widget containing the build options checkboxes""" widget = BuildOptionsWidget( self.elements, on_generic_changed=self.on_generic_changed ) self.body.addWidget(widget) def create_process_btn(self, window: QMainWindow) -> QPushButton: """ Creates and adds a process button @type window: QMainWindow @param window: The instance of the window object @rtype: QPushButton @returns: The created process button """ btn = QPushButton("Process") btn.setEnabled(False) btn.clicked.connect(window.process) self.body.addWidget(btn) return btn def create_progress_bar(self, window: QMainWindow) -> QProgressBar: """ Creates and adds progress bar @type window: QMainWindow @param window: The instance of the window object @rtype: QProgressBar @returns: The created progress bar """ progress = QProgressBar(window) progress.setRange(0, 100) progress.setValue(0) progress.setTextVisible(True) progress.setFormat("") self.body.addWidget(progress) return progress def on_generic_changed(self, state: int) -> None: # pylint: disable=unused-argument """ Method that gets called when the checkbox is changed @type state: int @param state: The changed state of the checkbox """ self.set_riglogic_cb_enabled() def is_enabled_and_checked(self, check_box: QCheckBox) -> bool: """ Method that checks if check box is enabled in same time @type check_box: QCheckBox @param check_box: The checkbox instance to check """ return ( check_box is not None and bool(check_box.isEnabled()) and bool(check_box.isChecked()) ) def set_riglogic_cb_enabled(self) -> None: """Method that sets enable state of riglogic check box""" all_total_meshes = False if self.elements.dna and self.is_enabled_and_checked( self.elements.blend_shapes_cb ): if ( len(self.elements.mesh_tree_list.get_selected_meshes()) == self.elements.dna.get_mesh_count() ): all_total_meshes = True enabled = ( self.is_enabled_and_checked(self.elements.joints_cb) and self.is_enabled_and_checked(self.elements.blend_shapes_cb) and all_total_meshes and self.is_enabled_and_checked(self.elements.skin_cb) and self.elements.get_file_path(self.elements.select_gui_path) is not None and self.elements.get_file_path(self.elements.select_analog_gui_path) is not None and self.elements.get_file_path(self.elements.select_aas_path) is not None ) self.elements.rig_logic_cb.setEnabled(enabled)