Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 29 additions & 111 deletions src/ert/gui/plotting/plot_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,11 @@
from PyQt6.QtCore import pyqtSlot as Slot
from PyQt6.QtWidgets import (
QApplication,
QButtonGroup,
QCheckBox,
QDialog,
QDockWidget,
QGroupBox,
QHBoxLayout,
QLabel,
QMainWindow,
QRadioButton,
QSplitter,
QStyle,
QTabWidget,
QTextEdit,
Expand Down Expand Up @@ -59,8 +55,10 @@
from .utils import PlotConfig, PlotContext
from .utils.observation_locations import transform_observation_locations
from .utils.plot_types import ObservationPlotLocations
from .utils.qt_creator import create_group_box, create_group_layout, create_side_panel
from .widgets.data_type_keys_widget import DataTypeKeysWidget
from .widgets.everest_control_selection_widget import EverestControlSelectionWidget
from .widgets.plot_controls import EverestControlsPlotOptions, MisfitsOptions
from .widgets.plot_ensemble_selection_widget import EnsembleSelectionWidget
from .widgets.plot_widget import Plotter, PlotWidget

Expand Down Expand Up @@ -291,7 +289,6 @@ def __init__(

self._data_type_keys_widget = DataTypeKeysWidget(self._key_definitions)
self._data_type_keys_widget.dataTypeKeySelected.connect(self.keySelected)
self.addDock("Navigation", "View data type", self._data_type_keys_widget)

self._ensemble_selection_widget = EnsembleSelectionWidget(
plot_case_objects,
Expand All @@ -314,21 +311,6 @@ def __init__(
self.updatePlot
)

def create_group_layout(
widgets: list[QWidget] | None = None,
) -> QVBoxLayout:
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
for w in widgets or []:
layout.addWidget(w)
return layout

def create_group_box(title: str, layout: QVBoxLayout) -> QGroupBox:
group_box = QGroupBox(title)
group_box.setStyleSheet("QGroupBox { font-style: italic; }")
group_box.setLayout(layout)
return group_box

self._everest_controls_group = create_group_box(
"Select control(s)",
create_group_layout([self._everest_control_selection_widget]),
Expand All @@ -338,77 +320,38 @@ def create_group_box(title: str, layout: QVBoxLayout) -> QGroupBox:
create_group_layout([self._ensemble_selection_widget]),
)

self._display_over_batches_radio = QRadioButton("batches")
self._display_over_batches_radio.setObjectName("display_over_batches_radio")
self._display_over_batches_radio.setChecked(True)
self._display_over_controls_radio = QRadioButton("controls")
self._display_over_controls_radio.setObjectName(
"display_over_controls_radio"
)
self._display_over_button_group = QButtonGroup(self)
self._display_over_button_group.addButton(self._display_over_batches_radio)
self._display_over_button_group.addButton(self._display_over_controls_radio)
self._display_over_button_group.buttonClicked.connect(self.updatePlot)

self._display_over_group = create_group_box(
"X-axis:",
create_group_layout(
[
self._display_over_batches_radio,
self._display_over_controls_radio,
]
),
self._everest_controls_plot_options = EverestControlsPlotOptions(
self.updatePlot
)

# Misfits plot options
self._toggle_mean = QCheckBox("Show mean")
self._toggle_mean.setChecked(True)
self._toggle_mean.stateChanged.connect(self.updatePlot)
self._toggle_outliers = QCheckBox("Show outliers")
self._toggle_outliers.setChecked(True)
self._toggle_outliers.stateChanged.connect(self.updatePlot)
self._toggle_scatter_plot = QCheckBox("Show scatter")
self._toggle_scatter_plot.setChecked(False)
self._toggle_scatter_plot.stateChanged.connect(self.updatePlot)
self._toggle_box = QCheckBox("Show box plot")
self._toggle_box.setChecked(True)
self._toggle_box.stateChanged.connect(self.updatePlot)

self._misfits_options_group = create_group_box(
"Plot options",
create_group_layout(
[
self._toggle_scatter_plot,
self._toggle_box,
self._toggle_mean,
self._toggle_outliers,
]
),
)
self._misfits_options = MisfitsOptions(self.updatePlot)

right_container = QWidget()
right_layout = create_group_layout(
[
self._ensemble_group,
self._display_over_group,
self._everest_controls_plot_options.get_widget(),
self._everest_controls_group,
self._misfits_options_group,
self._misfits_options.get_widget(),
]
)
right_container.setLayout(right_layout)

self.addDock(
"PlotControls",
"Plot controls",
right_container,
area=Qt.DockWidgetArea.RightDockWidgetArea,
)

self._everest_controls_group.setVisible(False)
self._display_over_group.setVisible(False)
self._misfits_options_group.setVisible(False)
self._everest_controls_plot_options.get_widget().setVisible(False)
self._misfits_options.get_widget().setVisible(False)
self._data_type_keys_widget.selectDefault()

splitter = QSplitter(Qt.Orientation.Horizontal)
splitter.addWidget(
create_side_panel("View data type", self._data_type_keys_widget)
)
splitter.addWidget(self._central_tab)
splitter.addWidget(create_side_panel("Plot controls", right_container))
splitter.setStretchFactor(1, 1)

self.setCentralWidget(splitter)

if self.getSelectedKey() is None:
self._show_no_data_message()

Expand Down Expand Up @@ -462,7 +405,7 @@ def updatePlot(self, layer: int | None = None) -> None:
):
key = key.replace("BREAKTHROUGH:", "")

self._misfits_options_group.setVisible(plot_widget.name == MISFITS)
self._misfits_options.get_widget().setVisible(plot_widget.name == MISFITS)

is_gradient_plot = plot_widget.name == EVEREST_GRADIENTS_PLOT
is_controls_plot = plot_widget.name == EVEREST_CONTROLS_PLOT
Expand All @@ -473,7 +416,7 @@ def updatePlot(self, layer: int | None = None) -> None:

is_everest_ensemble = plot_widget.name == ENSEMBLE and self.is_everest
self._everest_controls_group.setVisible(is_gradient_plot or is_controls_plot)
self._display_over_group.setVisible(is_controls_plot)
self._everest_controls_plot_options.get_widget().setVisible(is_controls_plot)
self._ensemble_selection_widget.apply_ensemble_filtering(
require_func_eval=is_objective_plot
or is_everest_ensemble
Expand Down Expand Up @@ -613,12 +556,14 @@ def fetch_data(
key,
layer,
)
plot_context.by_batch = self._display_over_batches_radio.isChecked()
plot_context.by_batch = (
self._everest_controls_plot_options.is_batches_selected()
)

plot_context.scatter_plot = self._toggle_scatter_plot.isChecked()
plot_context.box_plot = self._toggle_box.isChecked()
plot_context.mean = self._toggle_mean.isChecked()
plot_context.outliers = self._toggle_outliers.isChecked()
plot_context.scatter_plot = self._misfits_options.scatter_checkbox_state
plot_context.box_plot = self._misfits_options.box_checkbox_state
plot_context.mean = self._misfits_options.mean_checkbox_state
plot_context.outliers = self._misfits_options.outliers_checkbox_state

# Check if key is a history key.
# If it is it already has the data it needs
Expand Down Expand Up @@ -711,33 +656,6 @@ def addPlotWidget(
self._plot_widgets.append(plot_widget)
self._central_tab.setTabEnabled(index, enabled)

def addDock(
self,
name: str,
title: str,
widget: QWidget,
area: Qt.DockWidgetArea = Qt.DockWidgetArea.LeftDockWidgetArea,
allowed_areas: Qt.DockWidgetArea = Qt.DockWidgetArea.AllDockWidgetAreas,
) -> QDockWidget:
dock_widget = QDockWidget(name)
dock_widget.setObjectName(f"{name}Dock")

title_label = QLabel(title)
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
title_label.setStyleSheet("padding-bottom: 7px;")
title_label.setCursor(Qt.CursorShape.SizeAllCursor) # drag/move cursor

dock_widget.setTitleBarWidget(title_label)
dock_widget.setWidget(widget)
dock_widget.setAllowedAreas(allowed_areas)
dock_widget.setFeatures(
QDockWidget.DockWidgetFeature.DockWidgetMovable
| QDockWidget.DockWidgetFeature.DockWidgetFloatable
)

self.addDockWidget(area, dock_widget)
return dock_widget

@showWaitCursorWhileWaiting
def keySelected(self) -> None:
key_def = self.getSelectedKey()
Expand Down
36 changes: 36 additions & 0 deletions src/ert/gui/plotting/utils/qt_creator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
QGroupBox,
QLabel,
QVBoxLayout,
QWidget,
)


def create_side_panel(title: str, widget: QWidget) -> QWidget:
panel = QWidget()
layout = QVBoxLayout(panel)
layout.setContentsMargins(0, 0, 0, 0)

title_label = QLabel(title)
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
title_label.setStyleSheet("padding-bottom: 7px;")

layout.addWidget(title_label)
layout.addWidget(widget)
return panel


def create_group_layout(widgets: list[QWidget] | None = None) -> QVBoxLayout:
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
for w in widgets or []:
layout.addWidget(w)
return layout


def create_group_box(title: str, layout: QVBoxLayout) -> QGroupBox:
group_box = QGroupBox(title)
group_box.setStyleSheet("QGroupBox { font-style: italic; }")
group_box.setLayout(layout)
return group_box
7 changes: 7 additions & 0 deletions src/ert/gui/plotting/widgets/plot_controls/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .everest_controls_plot_options import EverestControlsPlotOptions
from .misfits_options import MisfitsOptions

__all__ = [
"EverestControlsPlotOptions",
"MisfitsOptions",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from collections.abc import Callable

from PyQt6.QtWidgets import (
QButtonGroup,
QGroupBox,
QRadioButton,
)

from ert.gui.plotting.utils.qt_creator import create_group_box, create_group_layout


class EverestControlsPlotOptions:
def __init__(self, connection_point: Callable[..., object]) -> None:

self._display_over_batches_radio = QRadioButton("batches")
self._display_over_batches_radio.setObjectName("display_over_batches_radio")
self._display_over_batches_radio.setChecked(True)
self._display_over_controls_radio = QRadioButton("controls")
self._display_over_controls_radio.setObjectName("display_over_controls_radio")
self._display_over_button_group = QButtonGroup()
self._display_over_button_group.addButton(self._display_over_batches_radio)
self._display_over_button_group.addButton(self._display_over_controls_radio)
self._display_over_button_group.buttonClicked.connect(connection_point)

self._display_over_group = create_group_box(
"X-axis:",
create_group_layout(
[
self._display_over_batches_radio,
self._display_over_controls_radio,
]
),
)

def get_widget(self) -> QGroupBox:
return self._display_over_group

def is_batches_selected(self) -> bool:
return self._display_over_batches_radio.isChecked()
72 changes: 72 additions & 0 deletions src/ert/gui/plotting/widgets/plot_controls/misfits_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from collections.abc import Callable

from PyQt6.QtWidgets import (
QCheckBox,
QGroupBox,
)

from ert.gui.plotting.utils.qt_creator import create_group_box, create_group_layout


class MisfitsOptions:
def __init__(self, connection_point: Callable[..., object]) -> None:

self._toggle_mean = QCheckBox("Show mean")
self._toggle_mean.setChecked(True)
self._toggle_mean.stateChanged.connect(connection_point)
self._toggle_outliers = QCheckBox("Show outliers")
self._toggle_outliers.setChecked(True)
self._toggle_outliers.stateChanged.connect(connection_point)
self._toggle_scatter_plot = QCheckBox("Show scatter")
self._toggle_scatter_plot.setChecked(False)
self._toggle_scatter_plot.stateChanged.connect(connection_point)
self._toggle_box = QCheckBox("Show box plot")
self._toggle_box.setChecked(True)
self._toggle_box.stateChanged.connect(connection_point)

self._misfit_options = create_group_box(
"Misfit options",
create_group_layout(
[
self._toggle_scatter_plot,
self._toggle_box,
self._toggle_mean,
self._toggle_outliers,
]
),
)

@property
def mean_checkbox_state(self) -> bool:
return self._toggle_mean.isChecked()

@mean_checkbox_state.setter
def mean_checkbox_state(self, value: bool) -> None:
self._toggle_mean.setChecked(value)

@property
def outliers_checkbox_state(self) -> bool:
return self._toggle_outliers.isChecked()

@outliers_checkbox_state.setter
def outliers_checkbox_state(self, value: bool) -> None:
self._toggle_outliers.setChecked(value)

@property
def scatter_checkbox_state(self) -> bool:
return self._toggle_scatter_plot.isChecked()

@scatter_checkbox_state.setter
def scatter_checkbox_state(self, value: bool) -> None:
self._toggle_scatter_plot.setChecked(value)

@property
def box_checkbox_state(self) -> bool:
return self._toggle_box.isChecked()

@box_checkbox_state.setter
def box_checkbox_state(self, value: bool) -> None:
self._toggle_box.setChecked(value)

def get_widget(self) -> QGroupBox:
return self._misfit_options
Empty file.
Empty file.
Loading
Loading