Metahuman_DNA_Calibration/dna_viewer/ui/elements_creator.py
2022-11-29 14:25:24 +01:00

403 lines
12 KiB
Python

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)