Skip to content
Snippets Groups Projects
Commit cc1bf4d1 authored by WHITFIELDRE email's avatar WHITFIELDRE email
Browse files

Make SampleLogs figure double clickable for new plot

Also add menu to print or plot selected rows. Update docstrings for methods.
parent 66a3df73
No related branches found
No related tags found
No related merge requests found
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source
# & Institut Laue - Langevin
# SPDX - License - Identifier: GPL - 3.0 +
# This file is part of the mantid workbench.
#
#
"""
You can run this widget independently by for example:
......
......@@ -20,6 +20,7 @@ TimeSeriesProperties = (BoolTimeSeriesProperty,
def get_type(log):
"""Convert type to something readable"""
dtype_map = {'i': 'int', 'f': 'float', 's': 'string', 'b': 'bool'}
if isinstance(log, TimeSeriesProperties):
return "{} series".format(dtype_map[log.dtype()[0].lower()])
......@@ -28,6 +29,9 @@ def get_type(log):
def get_value(log):
"""Returns the first value if length==1 otherwise returns number of
entries
"""
if isinstance(log, TimeSeriesProperties):
if log.size() > 1:
return "({} entries)".format(log.size())
......@@ -38,57 +42,80 @@ def get_value(log):
class SampleLogsModel(object):
"""This class stores the workspace object and return log values when
requested
"""
def __init__(self, ws):
"""Stores three thing:, the workspace, which experiment info number
to use, and the run object.
"""
self._ws = ws
self._exp = 0
self._set_run()
def _set_run(self):
"""Set run depending on workspace type and experiment info number"""
if self.isMD():
self.run = self._ws.getExperimentInfo(self._exp).run()
else:
self.run = self._ws.run()
def set_exp(self, exp):
"""Change the experiment info number"""
self._exp = exp
self._set_run()
def get_exp(self):
"""Return the experiment info number"""
return self._exp
def get_ws(self):
"""Return the workspace"""
return self._ws
def get_name(self):
"""Return the workspace name"""
return self._ws.name()
def getNumExperimentInfo(self):
"""Return number of experiment info's in workspace"""
return self._ws.getNumExperimentInfo() if self.isMD() else 0
def get_log(self, LogName):
"""Return log of given LogName"""
return self.run.getLogData(LogName)
def get_log_names(self):
"""Returns a list of logs in workspace"""
return self.run.keys()
def get_log_display_values(self, LogName):
"""Return a row to display for a log (name, type, value, units)"""
log = self.get_log(LogName)
return log.name, get_type(log), get_value(log), log.units
def is_log_plottable(self, LogName):
"""Checks if logs is plottable. Only Float, Int32 and Int64
TimeSeriesProperties are plottable at this point.
"""
return isinstance(self.get_log(LogName), (FloatTimeSeriesProperty,
Int32TimeSeriesProperty,
Int64TimeSeriesProperty))
def get_statistics(self, LogName):
"""Return the statistics of a particular log"""
log = self.get_log(LogName)
if isinstance(log, TimeSeriesProperties):
return log.getStatistics()
def isMD(self):
"""Checks if workspace is a MD Workspace"""
return isinstance(self._ws, MultipleExperimentInfos)
def getItemModel(self):
"""Return a QModel made from the current workspace. This should be set
onto a QTableView
"""
model = QStandardItemModel()
model.setHorizontalHeaderLabels(["Name", "Type", "Value", "Units"])
model.setColumnCount(4)
......
......@@ -13,6 +13,8 @@ from .view import SampleLogsView
class SampleLogs(object):
"""
"""
def __init__(self, ws, parent=None, model=None, view=None):
# Create model and view, or accept mocked versions
self.model = model if model else SampleLogsModel(ws)
......@@ -24,27 +26,52 @@ class SampleLogs(object):
self.setup_table()
def clicked(self):
"""When a log is clicked update stats with selected log and replot the
logs
"""
self.update_stats()
self.plot_logs()
def doubleClicked(self, i):
# print the log, later this should display the data in a table workspace or something
log_text = self.view.get_row_log_name(i.row())
log = self.model.get_log(log_text)
print('# {}'.format(log.name))
print(log.valueAsPrettyStr())
if self.model.is_log_plottable(log_text):
self.view.new_plot_log(self.model.get_ws(), self.model.get_exp(), log_text)
"""When a log is doubleCliked, print the log, later this should
display the data in a table workspace or something
"""
self.print_selected_logs()
def print_selected_logs(self):
"""Print all selected logs"""
for row in self.view.get_selected_row_indexes():
log = self.model.get_log(self.view.get_row_log_name(row))
print('# {}'.format(log.name))
print(log.valueAsPrettyStr())
def plot_clicked(self, event):
"""Check if figure is doubleClicked, then create new plot"""
if event.dblclick:
self.new_plot_logs()
def changeExpInfo(self):
"""When a different experiment info is selected this updates
everything
"""
# get currently selected rows
selected_rows = self.view.get_selected_row_indexes()
# set experiment info of model to one from view
self.model.set_exp(self.view.get_exp())
# create a new table as eveything has changed
self.setup_table()
# reselect previously selected rows
self.view.set_selected_rows(selected_rows)
# update stats with different experiment info
self.update_stats()
# replot logs with different experiment info
self.plot_logs()
def update_stats(self):
"""Updates the stats for currently select row.
If more then one row is selected then that stats are just cleared.
"""
selected_rows = self.view.get_selected_row_indexes()
if len(selected_rows) == 1:
stats = self.model.get_statistics(self.view.get_row_log_name(selected_rows[0]))
......@@ -56,9 +83,17 @@ class SampleLogs(object):
self.view.clear_statistics()
def plot_logs(self):
selected_rows = self.view.get_selected_row_indexes()
to_plot = [row for row in selected_rows if self.model.is_log_plottable(self.view.get_row_log_name(row))]
"""Get all selected rows, check if plottable, then plot the logs"""
to_plot = [row for row in self.view.get_selected_row_indexes()
if self.model.is_log_plottable(self.view.get_row_log_name(row))]
self.view.plot_selected_logs(self.model.get_ws(), self.model.get_exp(), to_plot)
def new_plot_logs(self):
"""Get all selected rows, check if plottable, then plot the logs in new figure"""
to_plot = [row for row in self.view.get_selected_row_indexes()
if self.model.is_log_plottable(self.view.get_row_log_name(row))]
self.view.new_plot_selected_logs(self.model.get_ws(), self.model.get_exp(), to_plot)
def setup_table(self):
"""Set the model in the view to the one create from the model"""
self.view.set_model(self.model.getItemModel())
......@@ -81,6 +81,25 @@ class SampleLogsTest(unittest.TestCase):
presenter.clicked()
self.assertEqual(self.view.get_selected_row_indexes.call_count, 2)
# plot clicked
self.model.reset_mock()
self.view.reset_mock()
event = mock.Mock()
type(event).dblclick = mock.PropertyMock(return_value=False)
presenter.plot_clicked(event)
self.assertEqual(self.view.get_selected_row_indexes.call_count, 0)
self.assertEqual(self.view.get_row_log_name.call_count, 0)
self.assertEqual(self.model.is_log_plottable.call_count, 0)
self.assertEqual(self.view.new_plot_selected_logs.call_count, 0)
type(event).dblclick = mock.PropertyMock(return_value=True)
presenter.plot_clicked(event)
self.assertEqual(self.view.get_selected_row_indexes.call_count, 1)
self.assertEqual(self.view.get_row_log_name.call_count, 2)
self.assertEqual(self.model.is_log_plottable.call_count, 2)
self.assertEqual(self.view.new_plot_selected_logs.call_count, 1)
# double clicked
self.model.reset_mock()
self.view.reset_mock()
......@@ -89,8 +108,9 @@ class SampleLogsTest(unittest.TestCase):
index.row = mock.Mock(return_value=7)
presenter.doubleClicked(index)
self.view.get_row_log_name.assert_called_once_with(7)
self.model.get_log.assert_called_once_with("Speed5")
self.assertEqual(self.view.get_row_log_name.call_count, 2)
self.view.get_row_log_name.assert_called_with(5)
self.model.get_log.assert_called_with("Speed5")
if __name__ == '__main__':
......
......@@ -10,7 +10,7 @@
from __future__ import (absolute_import, division, print_function)
from qtpy.QtWidgets import (QTableView, QHBoxLayout, QVBoxLayout,
QAbstractItemView, QFormLayout, QLineEdit,
QHeaderView, QLabel, QCheckBox,
QHeaderView, QLabel, QCheckBox, QMenu,
QSizePolicy, QSpinBox, QSplitter, QFrame)
from qtpy.QtCore import QItemSelectionModel, Qt
from matplotlib.backends.backend_qt5agg import FigureCanvas
......@@ -19,6 +19,11 @@ import matplotlib.pyplot as plt
class SampleLogsView(QSplitter):
"""Sample Logs View
This contains a table of the logs, a plot of the currently
selected logs, and the statistics of the selected log.
"""
def __init__(self, presenter, parent = None, name = '', isMD=False, noExp = 0):
super(SampleLogsView, self).__init__(parent)
......@@ -32,6 +37,7 @@ class SampleLogsView(QSplitter):
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.clicked.connect(self.presenter.clicked)
self.table.doubleClicked.connect(self.presenter.doubleClicked)
self.table.contextMenuEvent = self.tableMenu
self.addWidget(self.table)
frame_right = QFrame()
......@@ -57,6 +63,7 @@ class SampleLogsView(QSplitter):
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
self.canvas.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked)
self.ax = self.fig.add_subplot(111, projection='mantid')
layout_right.addWidget(self.canvas)
......@@ -81,52 +88,69 @@ class SampleLogsView(QSplitter):
self.resize(1200,800)
self.show()
def tableMenu(self, event):
"""Right click menu for table, can plot or print selected logs"""
menu = QMenu(self)
plotAction = menu.addAction("Plot selected")
plotAction.triggered.connect(self.presenter.new_plot_logs)
plotAction = menu.addAction("Print selected")
plotAction.triggered.connect(self.presenter.print_selected_logs)
menu.exec_(event.globalPos())
def set_model(self, model):
"""Set the model onto the table"""
self.model = model
self.table.setModel(self.model)
self.table.resizeColumnsToContents()
self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
def plot_selected_logs(self, ws, exp, rows):
"""Update the plot with the selected rows"""
self.ax.clear()
for row in rows:
log_text = self.get_row_log_name(row)
self.ax.plot(ws,
LogName=log_text,
label=log_text,
marker='.',
FullTime=not self.full_time.isChecked(),
ExperimentInfo=exp)
self.ax.set_ylabel('')
if self.ax.get_legend_handles_labels()[0]:
self.ax.legend()
self.create_ax_by_rows(self.ax, ws, exp, rows)
self.fig.canvas.draw()
def new_plot_log(self, ws, exp, log_text):
def new_plot_selected_logs(self, ws, exp, rows):
"""Create a new plot, in a separate window for selected rows"""
fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'})
ax.plot(ws,
LogName=log_text,
FullTime=not self.full_time.isChecked(),
ExperimentInfo=exp)
self.create_ax_by_rows(ax, ws, exp, rows)
fig.show()
def create_ax_by_rows(self, ax, ws, exp, rows):
"""Creates the plots for given rows onto axis ax"""
for row in rows:
log_text = self.get_row_log_name(row)
ax.plot(ws,
LogName=log_text,
label=log_text,
marker='.',
FullTime=not self.full_time.isChecked(),
ExperimentInfo=exp)
ax.set_ylabel('')
if ax.get_legend_handles_labels()[0]:
ax.legend()
def get_row_log_name(self, i):
"""Returns the log name of particular row"""
return str(self.model.item(i, 0).text())
def get_exp(self):
"""Get set experiment info number"""
return self.experimentInfo.value()
def get_selected_row_indexes(self):
"""Return a list of selected row from table"""
return [row.row() for row in self.table.selectionModel().selectedRows()]
def set_selected_rows(self, rows):
"""Set seleceted rows in table"""
mode = QItemSelectionModel.Select | QItemSelectionModel.Rows
for row in rows:
self.table.selectionModel().select(self.model.index(row, 0), mode)
def create_stats_widgets(self):
"""Creates the statistics widgets"""
self.stats_widgets = {"minimum": QLineEdit(),
"maximum": QLineEdit(),
"mean": QLineEdit(),
......@@ -139,9 +163,11 @@ class SampleLogsView(QSplitter):
widget.setReadOnly(True)
def set_statistics(self, stats):
"""Updates the statistics widgets from stats dictionary"""
for param in self.stats_widgets.keys():
self.stats_widgets[param].setText('{:.6}'.format(getattr(stats, param)))
def clear_statistics(self):
"""Clears the values in statistics widgets"""
for widget in self.stats_widgets.values():
widget.clear()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment