Esempio n. 1
0
class FrameLayout(QFrame):
    """
    This class defines the FrameLayout which emulates the behaviour of the Maya Python API's maya.cmds.frameLayout function. 
     The only difference is that the render setup version also provides a color bar and background color to differentiate layers, 
     collections, and overrides.
    """
    def __init__(self, item, parent):
        super(FrameLayout, self).__init__(parent)
        self.item = weakref.ref(item)
        self.isCollapsed = False
        self.mainLayout = None
        self.titleFrame = None
        self.contentFrame = None
        self.contentLayout = None

        self.initFrameLayout()

    def addWidget(self, widget):
        self.contentLayout.addWidget(widget)

    def getWidget(self, index):
        return self.contentLayout.itemAt(
            index).widget() if self.contentLayout.count() > index else None

    def initMainLayout(self):
        self.mainLayout = QVBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.setLayout(self.mainLayout)

    def initTitleFrame(self):
        self.titleFrame = TitleFrame(self.item(), self)
        self.mainLayout.addWidget(self.titleFrame)

    def initContentFrame(self):
        self.contentFrame = QFrame()
        self.contentFrame.setContentsMargins(0, 0, 0, 0)

        self.contentLayout = QVBoxLayout()
        self.contentLayout.setContentsMargins(0, 0, 0, 0)
        self.contentLayout.setSpacing(0)
        self.contentFrame.setLayout(self.contentLayout)
        self.mainLayout.addWidget(self.contentFrame)

    def toggleCollapsed(self):
        """ When the title frame is clicked, this function is called to toggle the collapsed state """
        self.isCollapsed = not self.isCollapsed
        self.contentFrame.setVisible(self.isCollapsed)
        self.titleFrame.arrowAndTitle.setArrow(self.isCollapsed)
        self.update()

    def initFrameLayout(self):
        self.setContentsMargins(0, 0, 0, 0)

        self.initMainLayout()
        self.initTitleFrame()
        self.initContentFrame()

        self.titleFrame.clicked.connect(self.toggleCollapsed)
Esempio n. 2
0
class GridTransformerDialog(QDialog):
    def __init__(self, parent=None):
        super(GridTransformerDialog, self).__init__(
            parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint
            | QtCore.Qt.WindowCloseButtonHint)
        self.setWindowTitle("Grid Transformer")

        self.setWindowIcon(qta.icon('fa.th'))

        # dict to store inputs that will get passed to the grid transformer
        # this gets validated to ensure all the bits of info are in it before
        # the run button gets enabled
        self.grid_transformer_inputs = {}

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        self._add_inputs()
        self._add_output()
        self._add_process()

        close_layout = QHBoxLayout()
        close_layout.addStretch()
        button_close = QPushButton("Close")
        button_close.clicked.connect(self.close_dialog)
        close_layout.addWidget(button_close)
        self.layout.addLayout(close_layout)

    def _add_inputs(self):
        inputs_groupbox = QGroupBox("Inputs")
        inputs_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        inputs_layout = QVBoxLayout()
        inputs_layout.setSpacing(0)
        inputs_groupbox.setLayout(inputs_layout)

        for input_band_name in input_band_names:
            inputband = GridTransformerInputBand(input_band_name)
            inputs_layout.addWidget(inputband)
            inputband.band_selected.connect(self._band_selected)
            inputband.log_message.connect(self._log_message)

        self.layout.addWidget(inputs_groupbox)

    def _band_selected(self, band_details):
        band_name, filename, band_index = band_details
        self.grid_transformer_inputs[band_name] = (filename, band_index)
        self.validate()

    def validate(self):
        self.run_button.setEnabled(self._is_valid())

    def _is_valid(self) -> bool:
        '''Is this ready to run, as in has the user specified all inputs needed
        for the grid transformer'''
        for band_name in input_band_names:
            if band_name not in self.grid_transformer_inputs:
                return False
            if self.grid_transformer_inputs[band_name] is None:
                return False
        if 'output' not in self.grid_transformer_inputs:
            return False

        return True

    def _log_message(self, message):
        self.log_messages.appendPlainText(message)

    def _add_output(self):
        output_groupbox = QGroupBox("Output")
        output_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        output_layout = QVBoxLayout()
        output_layout.setSpacing(0)
        output_groupbox.setLayout(output_layout)

        output_file_layout = QHBoxLayout()
        output_file_layout.setSpacing(4)
        self.output_file_input = QLineEdit()
        self.output_file_input.textChanged.connect(
            self._on_output_filename_changed)
        self.output_file_input.setMinimumWidth(400)
        self.output_file_input.setSizePolicy(QSizePolicy.Expanding,
                                             QSizePolicy.Expanding)
        output_file_layout.addWidget(self.output_file_input)
        output_layout.addLayout(output_file_layout)

        self.open_output_file_button = QPushButton()
        output_file_layout.addWidget(self.open_output_file_button)
        self.open_output_file_button.setIcon(qta.icon('fa.folder-open'))
        self.open_output_file_button.setToolTip("Select output file location")
        self.open_output_file_button.clicked.connect(self._click_open_output)

        self.layout.addWidget(output_groupbox)

    def _on_output_filename_changed(self, filename):
        self.grid_transformer_inputs['output'] = filename
        self.validate()

    def _set_output_filename(self, filename):
        self.output_file_input.setText(filename)
        self.grid_transformer_inputs['output'] = filename
        self.validate()

    def _click_open_output(self):
        filters = ("GeoTIFF (*.tif *.tiff)")
        filename, _ = QFileDialog.getSaveFileName(
            self, f"Select output file",
            GuiSettings.settings().value(output_folder_settings), filters)
        if filename is None:
            return
        last_open_folder = os.path.dirname(filename)
        if os.path.exists(last_open_folder):
            GuiSettings.settings().setValue(output_folder_settings,
                                            last_open_folder)

        self._set_output_filename(filename)

    def _add_process(self):
        process_groupbox = QGroupBox("Process")
        process_groupbox.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)
        process_layout = QVBoxLayout()
        process_layout.setSpacing(0)
        process_groupbox.setLayout(process_layout)

        pbar_frame = QFrame()
        pbar_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        pbar_hbox = QHBoxLayout()
        pbar_hbox.setContentsMargins(QtCore.QMargins(0, 0, 0, 16))
        pbar_hbox.setSpacing(16)

        # Run and stop buttons
        hbox = QHBoxLayout()
        hbox.setSpacing(8)
        self.run_button = QPushButton()
        # is only enabled when validation passes
        self.run_button.setEnabled(False)
        self.run_button.setText("Run")
        self.run_button.setFixedWidth(100)
        run_icon = qta.icon('fa.play', color='green')
        self.run_button.setIcon(run_icon)
        self.run_button.clicked.connect(self._click_run)
        hbox.addWidget(self.run_button)

        self.stop_button = QPushButton()
        self.stop_button.setEnabled(False)
        self.stop_button.setText("Stop")
        self.stop_button.setFixedWidth(100)
        stop_icon = qta.icon('fa.stop', color='red')
        self.stop_button.setIcon(stop_icon)
        self.stop_button.clicked.connect(self._click_stop)
        hbox.addWidget(self.stop_button)

        self.progress_bar = QProgressBar()
        self.progress_bar.setTextVisible(True)
        self.progress_bar.setAlignment(QtCore.Qt.AlignCenter)
        self.progress_bar.setValue(0)
        self.progress_bar.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Expanding)

        pbar_hbox.addLayout(hbox)
        pbar_hbox.addWidget(self.progress_bar)
        pbar_frame.setLayout(pbar_hbox)
        process_layout.addWidget(pbar_frame)

        self.warning_frame = QFrame()
        self.warning_frame.setVisible(False)
        self.warning_frame.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        hbox = QHBoxLayout()

        warning_icon_widget = qta.IconWidget('fa.warning', color='red')
        warning_icon_widget.setIconSize(QtCore.QSize(48, 48))
        warning_icon_widget.update()
        hbox.addWidget(warning_icon_widget)
        warning_label = QLabel(
            "Grid Transformer did not complete successfully. Please refer to "
            "log output.")
        warning_label.setStyleSheet("QLabel { color: red; }")
        warning_label.setWordWrap(True)
        warning_label.setSizePolicy(QSizePolicy.Expanding,
                                    QSizePolicy.Preferred)
        hbox.addWidget(warning_label)
        self.warning_frame.setLayout(hbox)
        process_layout.addWidget(self.warning_frame)

        self.success_frame = QFrame()
        self.success_frame.setVisible(False)
        self.success_frame.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        hbox = QHBoxLayout()

        success_icon_widget = qta.IconWidget('fa.check', color='green')
        success_icon_widget.setIconSize(QtCore.QSize(48, 48))
        success_icon_widget.update()
        hbox.addWidget(success_icon_widget)
        success_label = QLabel("Grid Transformer completed successfully.")
        success_label.setStyleSheet("QLabel { color: green; }")
        success_label.setWordWrap(True)
        success_label.setSizePolicy(QSizePolicy.Expanding,
                                    QSizePolicy.Preferred)
        hbox.addWidget(success_label)
        self.success_frame.setLayout(hbox)
        process_layout.addWidget(self.success_frame)

        log_layout = QVBoxLayout()
        log_layout.setSpacing(4)
        log_label = QLabel("Log messages")
        log_label.setStyleSheet("QLabel { color: grey; }")
        log_layout.addWidget(log_label)

        self.log_messages = QPlainTextEdit()
        log_font = QFont("monospace")
        log_font.setStyleHint(QFont.TypeWriter)
        self.log_messages.setFont(log_font)
        self.log_messages.setReadOnly(True)
        self.log_messages.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Expanding)
        # self.log_messages.sizePolicy.setVerticalStretch(1)
        log_layout.addWidget(self.log_messages)
        process_layout.addLayout(log_layout)

        self.layout.addWidget(process_groupbox)

    def _on_progress(self, progress):
        self.progress_bar.setValue(int(progress * 100))

    def _on_complete(self, successful: bool):
        self.warning_frame.setVisible(not successful)
        self.success_frame.setVisible(successful)
        self.stop_button.setEnabled(False)
        self.run_button.setEnabled(True)

        run_time = time.perf_counter() - self.start_time
        self._log_message(
            f"Total grid transformation time = {run_time:.2f} sec")
        self._log_message("\n\n")

    def _click_run(self):
        self.warning_frame.setVisible(False)
        self.success_frame.setVisible(False)
        self.stop_button.setEnabled(True)
        self.run_button.setEnabled(False)

        self.gt_executor = QtGridTransformerThread(
            self.grid_transformer_inputs)

        self.gt_executor.progress.connect(self._on_progress)
        self.gt_executor.message.connect(self._log_message)
        self.gt_executor.complete.connect(self._on_complete)

        self.start_time = time.perf_counter()
        self._log_message("\n\nStarting Grid Transformer process")
        self.gt_executor.start()

    def _click_stop(self):
        if self.gt_executor is not None:
            self.gt_executor.stop()

    def close_dialog(self):
        self.close()
Esempio n. 3
0
class RunTab(QtWidgets.QWidget):

    run_checks = QtCore.Signal()

    def __init__(self, prj: QAXProject):
        super(RunTab, self).__init__()
        self.prj = prj
        self.check_executor = None

        self.vbox = QtWidgets.QVBoxLayout()
        self.setLayout(self.vbox)

        self._add_check_outputs()
        self._add_process()

        # final setup
        self.set_run_stop_buttons_enabled(False)

    def _add_check_outputs(self):
        co_groupbox = QGroupBox("Check outputs")
        co_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        co_layout = QVBoxLayout()
        co_layout.setSpacing(16)
        co_groupbox.setLayout(co_layout)

        self.qajson_spatial_checkbox = QCheckBox(
            "Include summary spatial output in QAJSON. "
            "Supports QAX visualisation.")
        self.qajson_spatial_checkbox.setCheckState(
            QtCore.Qt.CheckState.Checked)
        co_layout.addWidget(self.qajson_spatial_checkbox)

        export_layout = QVBoxLayout()
        export_layout.setSpacing(4)
        self.export_spatial_checkbox = QCheckBox(
            "Export detailed spatial outputs to file. "
            "Supports visualisation in other geospatial applications.")
        self.export_spatial_checkbox.stateChanged.connect(
            self._on_export_spatial_changed)
        export_layout.addWidget(self.export_spatial_checkbox)

        output_folder_layout = QHBoxLayout()
        output_folder_layout.setSpacing(4)
        output_folder_layout.addSpacerItem(QtWidgets.QSpacerItem(37, 20))
        self.output_folder_label = QLabel(
            "Detailed spatial output folder location:")
        output_folder_layout.addWidget(self.output_folder_label)
        self.output_folder_input = QLineEdit()
        self.output_folder_input.setText(
            GuiSettings.settings().value("spatial_outputs"))
        self.output_folder_input.setMinimumWidth(300)
        self.output_folder_input.setSizePolicy(QSizePolicy.Expanding,
                                               QSizePolicy.Expanding)
        output_folder_layout.addWidget(self.output_folder_input)

        self.open_output_folder_button = QPushButton()
        output_folder_layout.addWidget(self.open_output_folder_button)
        self.open_output_folder_button.setIcon(qta.icon('fa.folder-open'))
        self.open_output_folder_button.setToolTip(
            f"Select file containing data")
        self.open_output_folder_button.clicked.connect(
            self._click_open_spatial_export_folder)
        export_layout.addLayout(output_folder_layout)

        co_layout.addLayout(export_layout)

        self._on_export_spatial_changed()
        self.vbox.addWidget(co_groupbox)

    def _click_open_spatial_export_folder(self):
        output_folder = QFileDialog.getExistingDirectory(
            self, f"Select folder for spatial outputs",
            GuiSettings.settings().value("spatial_outputs"),
            QFileDialog.ShowDirsOnly)

        if os.path.exists(output_folder):
            GuiSettings.settings().setValue("spatial_outputs", output_folder)

        self.output_folder_input.setText(output_folder)

    def _on_export_spatial_changed(self):
        is_export = self.export_spatial_checkbox.isChecked()
        self.output_folder_label.setEnabled(is_export)
        self.output_folder_input.setEnabled(is_export)
        self.open_output_folder_button.setEnabled(is_export)

    def _add_process(self):
        process_groupbox = QGroupBox("Process")
        process_groupbox.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)
        process_layout = QVBoxLayout()
        process_layout.setSpacing(0)
        process_groupbox.setLayout(process_layout)

        pbar_frame = QFrame()
        pbar_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        pbar_hbox = QHBoxLayout()
        pbar_hbox.setContentsMargins(QtCore.QMargins(0, 0, 0, 16))
        pbar_hbox.setSpacing(16)

        # Run and stop buttons
        hbox = QHBoxLayout()
        hbox.setSpacing(8)
        self.run_button = QPushButton()
        # is only enabled when validation passes
        self.run_button.setEnabled(False)
        self.run_button.setText("Run")
        self.run_button.setToolTip("Start check execution")
        self.run_button.setFixedWidth(100)
        run_icon = qta.icon('fa.play', color='green')
        self.run_button.setIcon(run_icon)
        self.run_button.clicked.connect(self._click_run)
        hbox.addWidget(self.run_button)

        self.stop_button = QPushButton()
        self.stop_button.setEnabled(False)
        self.stop_button.setText("Stop")
        self.stop_button.setToolTip("Stop check execution")
        self.stop_button.setFixedWidth(100)
        stop_icon = qta.icon('fa.stop', color='red')
        self.stop_button.setIcon(stop_icon)
        self.stop_button.clicked.connect(self._click_stop)
        hbox.addWidget(self.stop_button)

        self.progress_bar = QProgressBar()
        self.progress_bar.setTextVisible(True)
        self.progress_bar.setAlignment(QtCore.Qt.AlignCenter)
        self.progress_bar.setValue(0)
        self.progress_bar.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Expanding)

        pbar_hbox.addLayout(hbox)
        pbar_hbox.addWidget(self.progress_bar)
        pbar_frame.setLayout(pbar_hbox)
        process_layout.addWidget(pbar_frame)

        vbox = QVBoxLayout()
        vbox.setSpacing(8)
        vbox.setContentsMargins(QtCore.QMargins(0, 0, 0, 16))
        process_layout.addLayout(vbox)
        hbox = QHBoxLayout()
        vbox.addLayout(hbox)
        check_name_label = QLabel("Check:")
        check_name_label.setFixedWidth(80)
        hbox.addWidget(check_name_label)
        self.check_name_text_label = QLabel("n/a")
        hbox.addWidget(self.check_name_text_label)

        hbox = QHBoxLayout()
        vbox.addLayout(hbox)
        status_name_label = QLabel("Status:")
        status_name_label.setFixedWidth(80)
        hbox.addWidget(status_name_label)
        self.status_name_text_label = QLabel("Not started")
        hbox.addWidget(self.status_name_text_label)

        self.warning_frame = QFrame()
        self.warning_frame.setVisible(False)
        self.warning_frame.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        hbox = QHBoxLayout()

        warning_icon_widget = qta.IconWidget('fa.warning', color='red')
        warning_icon_widget.setIconSize(QtCore.QSize(48, 48))
        warning_icon_widget.update()
        hbox.addWidget(warning_icon_widget)
        warning_label = QLabel(
            "Grid Transformer did not complete successfully. Please refer to "
            "log output.")
        warning_label.setStyleSheet("QLabel { color: red; }")
        warning_label.setWordWrap(True)
        warning_label.setSizePolicy(QSizePolicy.Expanding,
                                    QSizePolicy.Preferred)
        hbox.addWidget(warning_label)
        self.warning_frame.setLayout(hbox)
        process_layout.addWidget(self.warning_frame)

        self.success_frame = QFrame()
        self.success_frame.setVisible(False)
        self.success_frame.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        hbox = QHBoxLayout()

        success_icon_widget = qta.IconWidget('fa.check', color='green')
        success_icon_widget.setIconSize(QtCore.QSize(48, 48))
        success_icon_widget.update()
        hbox.addWidget(success_icon_widget)
        success_label = QLabel("All checks completed successfully.")
        success_label.setStyleSheet("QLabel { color: green; }")
        success_label.setWordWrap(True)
        success_label.setSizePolicy(QSizePolicy.Expanding,
                                    QSizePolicy.Preferred)
        hbox.addWidget(success_label)
        self.success_frame.setLayout(hbox)
        process_layout.addWidget(self.success_frame)

        log_layout = QVBoxLayout()
        log_layout.setSpacing(4)
        log_label = QLabel("Log messages")
        log_label.setStyleSheet("QLabel { color: grey; }")
        log_layout.addWidget(log_label)

        self.log_messages = QPlainTextEdit()
        log_font = QFont("monospace")
        log_font.setStyleHint(QFont.TypeWriter)
        self.log_messages.setFont(log_font)
        self.log_messages.setReadOnly(True)
        self.log_messages.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Expanding)
        log_layout.addWidget(self.log_messages)
        process_layout.addLayout(log_layout)

        self.vbox.addWidget(process_groupbox)

    def set_run_stop_buttons_enabled(self, is_running: bool) -> NoReturn:
        if is_running:
            self.run_button.setEnabled(False)
            self.stop_button.setEnabled(True)
        else:
            self.run_button.setEnabled(True)
            self.stop_button.setEnabled(False)

    def _log_message(self, message):
        self.log_messages.appendPlainText(message)

    def run_executor(self, check_executor: QtCheckExecutorThread):
        # we pass the check_executor into the run tab as this is where the UI
        # components are that will display the execution status.
        self.set_run_stop_buttons_enabled(True)

        self._log_message("Check execution started")
        self.start_time = time.perf_counter()

        self.check_executor = check_executor
        self.check_executor.options = self.get_options()
        self.check_executor.check_tool_started.connect(
            self._on_check_tool_started)
        self.check_executor.progress.connect(self._on_progress)
        self.check_executor.qajson_updated.connect(self._on_qajson_update)
        self.check_executor.checks_complete.connect(self._on_checks_complete)
        self.check_executor.status_changed.connect(self._on_status_change)
        self.check_executor.start()

    def get_options(self) -> Dict:
        ''' Gets a list of options based on user entered data. eg; the spatial
        output specifications.
        '''
        return {
            CheckOption.spatial_output_qajson:
            self.qajson_spatial_checkbox.isChecked(),
            CheckOption.spatial_output_export:
            self.export_spatial_checkbox.isChecked(),
            CheckOption.spatial_output_export_location:
            self.output_folder_input.text()
        }

    def _click_run(self):
        self.run_checks.emit()

    def _click_stop(self):
        if self.check_executor is None:
            logger.warn("Check executor does not exist, cannot stop")
            return
        self._log_message("Stopping check execution")
        self.check_executor.stop()

    @QtCore.Slot(float)
    def _on_progress(self, progress):
        self.progress_bar.setValue(int(progress * 100))

    @QtCore.Slot()
    def _on_qajson_update(self):
        self.prj.qa_json = self.check_executor.qa_json

    @QtCore.Slot(object)
    def _on_check_tool_started(self, tpl):
        check_tool_name, check_number, total_check_count = tpl
        self.check_name_text_label.setText("{} ({}/{})".format(
            check_tool_name, check_number, total_check_count))

    @QtCore.Slot()
    def _on_checks_complete(self):
        run_time = time.perf_counter() - self.start_time
        self._log_message(
            f"Execution time for all checks = {run_time:.2f} sec")
        self._log_message("\n\n")

        self.set_run_stop_buttons_enabled(False)
        self.prj.qa_json = self.check_executor.qa_json

    @QtCore.Slot(str)
    def _on_status_change(self, status):
        self.status_name_text_label.setText(status)