Skip to content

Commit

Permalink
Merge pull request #94 from neutrons/tab_main_ui
Browse files Browse the repository at this point in the history
Main Tab UI - Changes
  • Loading branch information
AndreiSavici authored Aug 17, 2023
2 parents e489eae + f4dab6e commit 21c7b88
Show file tree
Hide file tree
Showing 13 changed files with 525 additions and 94 deletions.
38 changes: 32 additions & 6 deletions src/shiver/models/histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
import numpy as np

# pylint: disable=no-name-in-module
from mantid.api import AlgorithmManager, AlgorithmObserver, AnalysisDataServiceObserver
from mantid.api import (
AlgorithmManager,
AlgorithmObserver,
AnalysisDataServiceObserver,
Progress,
)
from mantid.simpleapi import mtd, DeleteWorkspace, RenameWorkspace, SaveMD
from mantid.kernel import Logger
from mantid.geometry import (
Expand All @@ -30,22 +35,28 @@ def __init__(self):

def load(self, filename, ws_type):
"""Method to take filename and workspace type and load with correct algorithm"""
info_step = ""
ws_name, _ = os.path.splitext(os.path.basename(filename))

additional_parameters = {}
if ws_type == "mde":
logger.information(f"Loading {filename} as MDE")
info_step = f"Loading {filename} as MDE"
logger.information(info_step)
load = AlgorithmManager.create("LoadMD")
elif ws_type == "mdh":
logger.information(f"Loading {filename} as MDH")
info_step = f"Loading {filename} as MDH"
logger.information(info_step)
load = AlgorithmManager.create("LoadMD")
elif ws_type == "norm":
logger.information(f"Loading {filename} as normalization")
info_step = f"Loading {filename} as normalization"
logger.information(info_step)
load = AlgorithmManager.create("LoadNexusProcessed")
additional_parameters = {"LoadHistory": False}
else:
logger.error(f"Unsupported workspace type {ws_type} for {filename}")

endrange = 100
progress = Progress(load, start=0.0, end=1.0, nreports=endrange)
progress.report(info_step)
alg_obs = FileLoadingObserver(self, filename, ws_type, ws_name)
self.algorithms_observers.add(alg_obs)

Expand Down Expand Up @@ -335,6 +346,22 @@ def get_all_valid_workspaces(self):
if filter_ws(name)
)

def get_plot_display_name(self, ws_name, ndims):
"""Method retrieve all dimension names, minimum and maximum for displaying in the plot"""
workspace = mtd[ws_name]
display_name = f"{ws_name}"
if ndims <= 2:
# collect dimensions min and max for 1D and 2D plots
display_name += ":"
for dim in range(ndims, workspace.getNumDims()):
dimension = workspace.getDimension(dim)
dim_min = round(dimension.getMinimum(), 2)
dim_max = round(dimension.getMaximum(), 2)
dim_name = dimension.name
display_name += f" {dim_min}<{dim_name}<{dim_max}"
display_name += "," if dim < (workspace.getNumDims() - 1) else ""
return display_name

def do_make_slice(self, config: dict):
"""Method to take filename and workspace type and load with correct algorithm"""

Expand All @@ -345,7 +372,6 @@ def do_make_slice(self, config: dict):
alg = AlgorithmManager.create("MakeSlice")
alg_obs = MakeSliceObserver(parent=self, ws_name=config.get("OutputWorkspace"))
self.algorithms_observers.add(alg_obs)

alg_obs.observeFinish(alg)
alg_obs.observeError(alg)
alg.initialize()
Expand Down
1 change: 1 addition & 0 deletions src/shiver/presenters/histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self, view, model):

self.view.histogram_parameters.connect_histogram_submit(self.handle_button)
self.view.histogram_parameters.histogram_btn.clicked.connect(self.submit_histogram_to_make_slice)
self.view.connect_plot_display_name_callback(self.model.get_plot_display_name)

self.view.buttons.connect_load_dataset(self.load_dataset)
self.view.buttons.connect_load_file(self.load_file)
Expand Down
2 changes: 1 addition & 1 deletion src/shiver/views/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
QHBoxLayout,
QAbstractItemView,
)
from .histogram_parameters import INVALID_QLISTWIDGET
from .invalid_styles import INVALID_QLISTWIDGET


class RawData(QGroupBox):
Expand Down
14 changes: 13 additions & 1 deletion src/shiver/views/histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, parent=None):
# check the state of the required fields
# based on the fields states
self.field_errors = []
self.plot_display_name_callback = None

self.buttons = LoadingButtons(self)
self.input_workspaces = InputWorkspaces(self)
Expand All @@ -47,6 +48,10 @@ def __init__(self, parent=None):
self.histogram_parameters.initialize_default()
self.input_workspaces.initialize_default()

def connect_plot_display_name_callback(self, callback):
"""callback for the display name-description for the plot"""
self.plot_display_name_callback = callback

def set_field_invalid_state(self, item):
"""include the item in the field_error list and disable the corresponding button"""
if item not in self.field_errors:
Expand All @@ -73,7 +78,14 @@ def make_slice_finish(self, ws_name, ndims):
self.makeslice_finish_signal.emit(ws_name, ndims)

def _make_slice_finish(self, ws_name, ndims):
do_default_plot(ws_name, ndims)
display_name = self.plot_display_name_callback(ws_name, ndims)
min_intensity = self.histogram_parameters.dimensions.intensity_min.text()
max_intensity = self.histogram_parameters.dimensions.intensity_max.text()
intensity_limits = {
"min": float(min_intensity) if min_intensity != "" else None,
"max": float(max_intensity) if max_intensity != "" else None,
}
do_default_plot(ws_name, ndims, display_name, intensity_limits)
self.histogram_workspaces.histogram_workspaces.set_selected(ws_name)

def show_error_message(self, msg, accumulate=False):
Expand Down
129 changes: 68 additions & 61 deletions src/shiver/views/histogram_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
QCheckBox,
QDoubleSpinBox,
)
from .invalid_styles import INVALID_QLINEEDIT

try:
from qtpy.QtCore import QString
Expand All @@ -41,55 +42,6 @@ def translation(number, character):
return str(number) + character


INVALID_QLINEEDIT = """
QLineEdit {
border-color: red;
border-style: outset;
border-width: 2px;
border-radius: 4px;
padding-left: -1px;
padding-right: -1px;
padding-top: 1px;
padding-bottom: 1px;
}
"""
INVALID_QTABLEWIDGET = """
QTableWidget {
border-color: red;
border-style: outset;
border-width: 2px;
border-radius: 4px;
padding-left: -1px;
padding-right: -1px;
padding-top: 1px;
padding-bottom: 1px;
}
"""
INVALID_QLISTWIDGET = """
QListWidget {
border-color: red;
border-style: outset;
border-width: 2px;
border-radius: 4px;
padding-left: -1px;
padding-right: -1px;
padding-top: 1px;
padding-bottom: 1px;
}
"""
INVALID_QCHECKBOX = """
QCheckBox::indicator{
border-color: red;
border-style: outset;
border-width: 2px;
padding-left: -1px;
padding-right: -1px;
padding-top: 1px;
padding-bottom: 1px;
}
"""


# validator for projections 3-digit array format: [1,0,0] from mantid --> DimensionSelectorWidget.py
class V3DValidator(QtGui.QValidator):
"""Validates the projection values"""
Expand Down Expand Up @@ -735,6 +687,14 @@ def __init__(self, parent=None):
layout.addWidget(self.combo_max4, 4, 2)
layout.addWidget(self.combo_step4, 4, 3)

layout.addWidget(QLabel("Intensity"), 5, 0)
self.intensity_min = QLineEdit()
self.intensity_min.setValidator(self.double_validator)
layout.addWidget(self.intensity_min, 5, 1)
self.intensity_max = QLineEdit()
self.intensity_max.setValidator(self.double_validator)
layout.addWidget(self.intensity_max, 5, 2)

self.setLayout(layout)

# required steps background color
Expand All @@ -759,6 +719,10 @@ def __init__(self, parent=None):
self.combo_min4.textEdited.connect(lambda: self.min_max_checked(self.combo_min4, self.combo_max4))
self.combo_max4.textEdited.connect(lambda: self.min_max_checked(self.combo_min4, self.combo_max4))

# both min-max should be added; each min<max
self.intensity_min.textEdited.connect(lambda: self.min_max_compare(self.intensity_min, self.intensity_max))
self.intensity_max.textEdited.connect(lambda: self.min_max_compare(self.intensity_min, self.intensity_max))

self.inhibit_signals = False

# for min max valid states
Expand Down Expand Up @@ -863,6 +827,46 @@ def combo_changed(self, index):
if receiver_combo_step in self.required_steps:
self.validate_step(receiver_combo_step)

def min_max_compare(self, cmin, cmax):
"""Ensure Minimum and Maximum value pairs are:
float numbers, Minimum < Maximum if both exist at the same time"""

if cmin in self.min_max_invalid_states:
self.min_max_invalid_states.remove(cmin)
self.set_field_valid_state(cmin)
if cmax in self.min_max_invalid_states:
self.min_max_invalid_states.remove(cmax)
self.set_field_valid_state(cmax)

min_value = cmin.text()
max_value = cmax.text()

# if both values exist check: min<max
if len(min_value) > 0 and len(max_value) > 0:
valid = self.compare_numbers(min_value, max_value)
if not valid:
self.set_field_invalid_state(cmin)
self.set_field_invalid_state(cmax)
self.min_max_invalid_states.append(cmin)
self.min_max_invalid_states.append(cmax)
elif min_value:
try:
min_value = float(min_value)
except ValueError:
self.min_max_invalid_states.append(cmin)
self.set_field_invalid_state(cmin)

elif max_value:
try:
max_value = float(max_value)
except ValueError:
self.set_field_invalid_state(cmax)
self.min_max_invalid_states.append(cmax)

else:
# it should never be here
return

def min_max_checked(self, cmin, cmax):
"""Ensure Minimum and Maximum value pairs are:
float numbers, Minimum < Maximum and both exist at the same time"""
Expand Down Expand Up @@ -891,25 +895,15 @@ def validate_min_max_checked(self, cmin, cmax, sender_flag):
else:
# needs to be number and less than max
if len(min_value) != 0:
try:
tempvalue = float(min_value)
if tempvalue > float(max_value):
valid = False
except ValueError:
valid = False
valid = self.compare_numbers(min_value, max_value)
else:
# both min and max values need to filled in
if (len(max_value) == 0 and len(min_value) != 0) or (len(max_value) != 0 and len(min_value) == 0):
valid = False
else:
# needs to be number and greater than min
if len(max_value) != 0:
try:
tempvalue = float(max_value)
if tempvalue < float(min_value):
valid = False
except ValueError:
valid = False
valid = self.compare_numbers(min_value, max_value)

if not valid:
self.min_max_invalid_states.append(cmin)
Expand All @@ -920,6 +914,19 @@ def validate_min_max_checked(self, cmin, cmax, sender_flag):
self.set_field_valid_state(cmin)
self.set_field_valid_state(cmax)

def compare_numbers(self, minnum, maxnum):
"""Check Minimum and Maximum value pairs are:
float numbers, Minimum < Maximum"""
valid = True
try:
minvalue = float(minnum)
maxvalue = float(maxnum)
if minvalue > maxvalue:
valid = False
except ValueError:
valid = False
return valid

# projection value-dimension value update functionality from mantid --> DimensionSelectorWidget.py
def update_combo(self):
"""Update combo box dimension values"""
Expand Down
12 changes: 6 additions & 6 deletions src/shiver/views/loading_buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ class LoadingButtons(QWidget):
def __init__(self, parent=None):
super().__init__(parent)

self.load_dataset = QPushButton("Load Python Description")
self.load_dataset.setToolTip("Load multi-dimensional data from a description in a python file.")
self.gen_dataset = QPushButton("Generate dataset")
self.gen_dataset.setToolTip("Opens a new tab to generate multi-dimensional data from raw files.")
self.load_mde = QPushButton("Load Data Nexus")
self.load_mde.setToolTip("Opens a previously generated multi-dimensional data.")
self.load_norm = QPushButton("Load Normalization Nexus")
self.load_norm.setToolTip("Opens a processed normalization nexus file, containing incoherent scattering.")
self.load_dataset = QPushButton("Load Python Description")
self.load_dataset.setToolTip("Load multi-dimensional data from a description in a python file.")
self.gen_dataset = QPushButton("Generate dataset")
self.gen_dataset.setToolTip("Opens a new tab to generate multi-dimensional data from raw files.")

layout = QVBoxLayout()
layout.addWidget(self.load_dataset)
layout.addWidget(self.gen_dataset)
layout.addWidget(self.load_mde)
layout.addWidget(self.load_norm)
layout.addWidget(self.load_dataset)
layout.addWidget(self.gen_dataset)
layout.addStretch()
self.setLayout(layout)

Expand Down
20 changes: 12 additions & 8 deletions src/shiver/views/oncat.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,14 +524,18 @@ def get_dataset_names(
projection = ["metadata.entry.daslogs.sequencename", "metadata.entry.notes"]
datasets = get_data_from_oncat(login, projection, ipts_number, instrument, facility)

if use_notes:
dsn = [datasets[i]["metadata"]["entry"].get("notes", None) for i in range(len(datasets))]
else:
dsn = [
datasets[i]["metadata"]["entry"].get("daslogs", {}).get("sequencename", {}).get("value", None)
for i in range(len(datasets))
]

try:
if use_notes:
dsn = [datasets[i]["metadata"]["entry"].get("notes", None) for i in range(len(datasets))]
else:
dsn = [
datasets[i]["metadata"]["entry"].get("daslogs", {}).get("sequencename", {}).get("value", None)
for i in range(len(datasets))
]
except (KeyError, TypeError):
# if keys do not exist following the expected schema return an empty list dsn = []
dsn = []
return dsn
# remove None values
dsn = [ds for ds in dsn if ds is not None]

Expand Down
Loading

0 comments on commit 21c7b88

Please sign in to comment.