diff --git a/src/ert/ensemble_evaluator/state.py b/src/ert/ensemble_evaluator/state.py index b8d17cf8bab..49426bc924a 100644 --- a/src/ert/ensemble_evaluator/state.py +++ b/src/ert/ensemble_evaluator/state.py @@ -1,12 +1,13 @@ from typing import Final -COLOR_FAILED: Final = (255, 200, 200) -COLOR_FINISHED: Final = (127, 201, 127) -COLOR_PENDING: Final = (190, 174, 212) -COLOR_RUNNING: Final = (255, 255, 153) -COLOR_UNKNOWN: Final = (128, 128, 128) -COLOR_WAITING: Final = (164, 200, 255) -COLOR_CANCELLED: Final = (235, 242, 246) +COLOR_QUEUED: Final = (183, 191, 199) +COLOR_FAILED: Final = (207, 34, 46) +COLOR_FINISHED: Final = (45, 164, 78) +COLOR_PENDING: Final = COLOR_QUEUED +COLOR_RUNNING: Final = (214, 178, 42) +COLOR_UNKNOWN: Final = (140, 149, 159) +COLOR_WAITING: Final = COLOR_QUEUED +COLOR_CANCELLED: Final = (234, 238, 242) COLOR_WARNING: Final = (255, 103, 0) ENSEMBLE_STATE_CANCELLED: Final = "Cancelled" diff --git a/src/ert/gui/experiments/view/progress_widget.py b/src/ert/gui/experiments/view/progress_widget.py index f51ed16eb0e..ba515efdd84 100644 --- a/src/ert/gui/experiments/view/progress_widget.py +++ b/src/ert/gui/experiments/view/progress_widget.py @@ -11,7 +11,11 @@ QVBoxLayout, ) -from ert.ensemble_evaluator.state import ENSEMBLE_STATE_FAILED, REAL_STATE_TO_COLOR +from ert.ensemble_evaluator.state import ( + ENSEMBLE_STATE_FAILED, + REAL_STATE_TO_COLOR, + REALIZATION_STATE_WAITING, +) class ProgressWidget(QFrame): @@ -21,7 +25,7 @@ def __init__(self) -> None: self._vertical_layout = QVBoxLayout(self) self._vertical_layout.setContentsMargins(0, 0, 0, 0) - self._vertical_layout.setSpacing(0) + self._vertical_layout.setSpacing(2) self.setLayout(self._vertical_layout) self._waiting_progress_bar = QProgressBar(self) @@ -39,36 +43,47 @@ def __init__(self) -> None: self._legend_frame = QFrame(self) self._vertical_layout.addWidget(self._legend_frame) - self._legend_frame.setFixedHeight(30) + self._legend_frame.setFixedHeight(24) self._horizontal_legend_layout = QHBoxLayout(self._legend_frame) self._horizontal_legend_layout.setContentsMargins(0, 0, 0, 0) - self._horizontal_legend_layout.setSpacing(0) + self._horizontal_legend_layout.setSpacing(6) self._status: dict[str, int] = {} self._realization_count = 0 self._progress_label_map: dict[str, QLabel] = {} self._legend_map_text = {} - for state, color in REAL_STATE_TO_COLOR.items(): + color_name = QColor(*color).name() + label = QLabel(self) label.setVisible(False) label.setObjectName(f"progress_{state}") - label.setStyleSheet(f"background-color : {QColor(*color).name()}") + label.setStyleSheet(f"background-color : {color_name}") self._progress_label_map[state] = label self._horizontal_layout.addWidget(label) - label = QLabel(self) - label.setFixedSize(20, 20) - label.setStyleSheet( - f"background-color : {QColor(*color).name()}; border: 1px solid black;" - ) - self._horizontal_legend_layout.addWidget(label) + if state == REALIZATION_STATE_WAITING: + marker_style = ( + "background-color: transparent;" + "border-radius: 7px;" + f"border: 2px solid {color_name};" + ) + else: + marker_style = f"background-color: {color_name};border-radius: 7px;" + + legend_marker = QLabel(self) + legend_marker.setFixedSize(14, 14) + legend_marker.setStyleSheet(marker_style) + self._horizontal_legend_layout.addWidget(legend_marker) label = QLabel(self) label.setObjectName(f"progress_label_text_{state}") - label.setText(f" {state} ({0}/{0})") + label.setText(f"{state} ({0}/{0})") self._legend_map_text[state] = label self._horizontal_legend_layout.addWidget(label) + self._horizontal_legend_layout.addSpacing(16) + + self._horizontal_legend_layout.addStretch() def repaint_components(self) -> None: if self._realization_count > 0: @@ -83,7 +98,7 @@ def repaint_components(self) -> None: for state, label in self._legend_map_text.items(): label.setText( - f" {state} ({self._status.get(state, 0)}/{self._realization_count})" + f"{state} ({self._status.get(state, 0)}/{self._realization_count})" ) def stop_waiting_progress_bar(self) -> None: diff --git a/src/ert/gui/experiments/view/realization.py b/src/ert/gui/experiments/view/realization.py index 5c396653e0e..97228af3d00 100644 --- a/src/ert/gui/experiments/view/realization.py +++ b/src/ert/gui/experiments/view/realization.py @@ -7,6 +7,7 @@ QModelIndex, QObject, QPoint, + QRect, QSize, Qt, ) @@ -23,12 +24,14 @@ QWidget, ) +from ert.ensemble_evaluator import state from ert.gui.model.real_list import RealListModel from ert.gui.model.snapshot import ( CallbackStatusMessageRole, FMStepColorHint, MemoryUsageRole, RealIens, + StatusRole, ) from ert.shared.status.utils import byte_with_unit @@ -40,7 +43,7 @@ def __init__(self, it: int, parent: QWidget | None = None) -> None: super().__init__(parent) self._iter = it - self._delegate_size = QSize(90, 90) + self._delegate_size = QSize(70, 70) self._real_view = QListView(self) self._real_view.setViewMode(QListView.ViewMode.IconMode) @@ -95,15 +98,17 @@ def refresh_current_selection(self) -> None: class RealizationDelegate(QStyledItemDelegate): + _DOT_DIAMETER = 32 + _ARC_MARGIN = 4 + _ARC_WIDTH = 5 + def __init__(self, size: QSize, parent: QObject) -> None: super().__init__(parent) self._size = size parent.installEventFilter(self) - self.adjustment_point_for_job_rect_margin = QPoint(-20, -20) - self._color_black = QColor(0, 0, 0, 180) - self._color_progress = QColor(50, 173, 230, 200) - self._color_lightgray = QColor("LightGray").lighter(120) - self._pen_black = QPen(self._color_black, 2, Qt.PenStyle.SolidLine) + self.adjustment_point_for_job_rect_margin = QPoint(-12, -12) + self._color_track = QColor(0, 0, 0, 35) + self._pen_outline = QPen(QColor(0, 0, 0, 45), 1, Qt.PenStyle.SolidLine) @override def paint( @@ -111,50 +116,63 @@ def paint( ) -> None: if painter is None: return - text = index.data(RealIens) - selected_color, finished_count, total_count = tuple(index.data(FMStepColorHint)) + realization_label = index.data(RealIens) + status = index.data(StatusRole) + status_color, completed_step_count, total_step_count = tuple( + index.data(FMStepColorHint) + ) painter.save() painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, True) painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) - percentage_done = ( - 100 if total_count < 1 else int((finished_count * 100.0) / total_count) - ) - - painter.setPen(self._pen_black) - adjusted_rect = option.rect.adjusted(2, 2, -2, -2) - - painter.setBrush( - self._color_progress if percentage_done == 100 else self._color_lightgray + if option.state & QStyle.StateFlag.State_Selected: + highlight = QColor(option.palette.color(QPalette.ColorRole.Highlight)) + highlight.setAlpha(60) + painter.setPen(Qt.PenStyle.NoPen) + painter.setBrush(highlight) + painter.drawRoundedRect(option.rect.adjusted(2, 2, -2, -2), 6, 6) + + status_dot_rect = QRect( + option.rect.center().x() - self._DOT_DIAMETER // 2, + option.rect.top() + 8, + self._DOT_DIAMETER, + self._DOT_DIAMETER, ) - painter.drawEllipse(adjusted_rect) - - if 0 < percentage_done < 100: - painter.setBrush(self._color_progress) - painter.drawPie(adjusted_rect, 1440, -int(percentage_done * 57.6)) - if option.state & QStyle.StateFlag.State_Selected: - factor: int = ( - 125 - if selected_color.lighter(125).getRgb() != (255, 255, 255, 255) - else 110 + if status == state.REALIZATION_STATE_RUNNING and total_step_count > 0: + progress_ring_rect = status_dot_rect.adjusted( + -self._ARC_MARGIN, + -self._ARC_MARGIN, + self._ARC_MARGIN, + self._ARC_MARGIN, + ) + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.setPen(QPen(self._color_track, self._ARC_WIDTH)) + painter.drawEllipse(progress_ring_rect) + progress_pen = QPen(status_color, self._ARC_WIDTH) + progress_pen.setCapStyle(Qt.PenCapStyle.RoundCap) + painter.setPen(progress_pen) + painter.drawArc( + progress_ring_rect, + 90 * 16, + -int(360 * 16 * completed_step_count / total_step_count), ) - selected_color = selected_color.lighter(factor) - - painter.setBrush(selected_color) - adjusted_rect = option.rect.adjusted(7, 7, -7, -7) - painter.drawEllipse(adjusted_rect) - font = painter.font() - font.setBold(True) - painter.setFont(font) + painter.setPen(self._pen_outline) + if status == state.REALIZATION_STATE_WAITING: + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.setPen(QPen(status_color, 2)) + painter.drawEllipse(status_dot_rect.adjusted(1, 1, -1, -1)) + else: + painter.setBrush(status_color) + painter.drawEllipse(status_dot_rect) - adj_rect = option.rect.adjusted(0, 20, 0, 0) - painter.drawText(adj_rect, Qt.AlignmentFlag.AlignHCenter, text) - adj_rect = option.rect.adjusted(0, 45, 0, 0) + painter.setPen(option.palette.color(QPalette.ColorRole.Text)) painter.drawText( - adj_rect, Qt.AlignmentFlag.AlignHCenter, f"{finished_count} / {total_count}" + option.rect.adjusted(0, self._DOT_DIAMETER + 14, 0, 0), + Qt.AlignmentFlag.AlignHCenter, + realization_label, ) painter.restore() diff --git a/tests/ert/unit_tests/gui/experiments/view/test_legend.py b/tests/ert/unit_tests/gui/experiments/view/test_legend.py index 119023afb24..355fceab2a4 100644 --- a/tests/ert/unit_tests/gui/experiments/view/test_legend.py +++ b/tests/ert/unit_tests/gui/experiments/view/test_legend.py @@ -31,7 +31,7 @@ def test_marker_label_text_correct(qtbot, status: dict[str, int]): assert label_marker count = status.get(state, 0) - assert f" {state} ({count}/{realization_count})" in label_marker.text() + assert f"{state} ({count}/{realization_count})" in label_marker.text() @given(