Window.py 5.88 KiB
# Window.py -- Pamhyr
# Copyright (C) 2023  INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

# -*- coding: utf-8 -*-

from tools import trace, timer

from View.Tools.PamhyrWindow import PamhyrWindow

from PyQt5.QtGui import (
    QKeySequence,
)

from PyQt5.QtCore import (
    Qt, QVariant, QAbstractTableModel,
    QCoreApplication, QModelIndex, QRect, QThread,
    pyqtSlot, pyqtSignal,
)

from PyQt5.QtWidgets import (
    QDialogButtonBox, QPushButton, QLineEdit,
    QFileDialog, QTableView, QAbstractItemView,
    QUndoStack, QShortcut, QAction, QItemDelegate,
    QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
    QProgressBar, QLabel,
)

from View.CheckList.Table import TableModel
from View.CheckList.Worker import Worker
from View.CheckList.Translate import CheckListTranslate

_translate = QCoreApplication.translate


class CheckListWindow(PamhyrWindow):
    _pamhyr_ui = "CheckList"
    _pamhyr_name = "Check list"

    signalStatus = pyqtSignal(str)

    def __init__(self, autorun: bool = True,
                 study=None, config=None,
                 solver=None, parent=None):
        self._autorun = autorun
        self._solver = solver

        name = self._pamhyr_name + " - " + study.name

        super(CheckListWindow, self).__init__(
            title=name,
            study=study,
            config=config,
            trad=CheckListTranslate(),
            options=[],
            parent=parent
        )

        # Add solver to hash computation data
        self._hash_data.append(self._solver)

        self._checker_list = (
            self._study.checkers() +
            self._solver.checkers()
        )

        self.setup_table()
        self.setup_progress_bar()
        self.setup_connections()
        self.setup_thread()
        self.setup_statusbar()

    def setup_table(self):
        table = self.find(QTableView, f"tableView")
        self._table = TableModel(
            table_view=table,
            table_headers=self._trad.get_dict("table_headers"),
            data=self._checker_list,
        )

    def setup_progress_bar(self):
        self._progress = self.find(QProgressBar, f"progressBar")
        self._p = 0             # Progress current step

        self._progress.setRange(0, len(self._checker_list))
        self._progress.setValue(self._p)

    def setup_connections(self):
        self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
        self.find(QPushButton, "pushButton_retry").clicked.connect(self.retry)
        self.find(QPushButton, "pushButton_cancel")\
            .clicked.connect(self.reject)

    def setup_thread(self):
        self._worker = Worker(self._study, self._checker_list)
        self._worker_thread = QThread()
        self._worker.moveToThread(self._worker_thread)

        # Connect any worker signals
        self._worker.signalStatus.connect(self.update)
        self._worker_thread.started.connect(self._worker.process)

        self._worker_thread.start()

    def retry(self):
        self._worker_thread.terminate()
        self._worker_thread.wait()

        self.find(QPushButton, "pushButton_retry").setEnabled(False)
        self.find(QPushButton, "pushButton_ok").setEnabled(False)

        self.setup_thread()

    def _compute_status(self):
        ok = len(list(filter(lambda c: c.is_ok(), self._checker_list)))
        warning = len(
            list(filter(lambda c: c.is_warning(), self._checker_list)))
        error = len(list(filter(lambda c: c.is_error(), self._checker_list)))

        return ok, warning, error

    def _compute_status_label(self):
        ok, warning, error = self._compute_status()
        return (f"<font color=\"Green\">Ok: {ok} </font> |" +
                f"<font color=\"Orange\">Warning: {warning} </font> |" +
                f"<font color=\"Red\">Error: {error}</font>")

    def setup_statusbar(self):
        txt = self._compute_status_label()
        self._status_label = QLabel(txt)
        self.statusbar.addPermanentWidget(self._status_label)

    def update_statusbar(self):
        txt = self._compute_status_label()
        self._status_label.setText(txt)

    def progress(self):
        self._p += 1
        self._progress.setValue(self._p)
        self._table.update()

    def start_compute(self):
        self._p = 0
        self._progress.setValue(self._p)

    def info_compute(self, str):
        self.statusbar.showMessage(str, 3000)

    def end_compute(self):
        self._table.layoutChanged.emit()
        self.find(QPushButton, "pushButton_retry").setEnabled(True)

        errors = any(filter(lambda c: c.is_error(), self._checker_list))
        if not errors:
            self.find(QPushButton, "pushButton_ok").setEnabled(True)
            if self._autorun:
                self._parent.solver_log(self._solver)

        self.update_statusbar()

    def update(self, key: str):
        if key == "start":
            self.start_compute()
            self.info_compute("Starting ...")
        elif key == "end":
            self.info_compute("Finish")
            self.end_compute()
        elif key == "progress":
            self.progress()
        else:
            self.info_compute(key)

        self.update_statusbar()

    def end(self):
        # self._worker.join()b
        self.close()

    def reject(self):
        self.end()

    def accept(self):
        self._parent.solver_log(self._solver)
        # self.end()