Skip to content
Snippets Groups Projects
Unverified Commit 7dfb2214 authored by Antti Soininen's avatar Antti Soininen Committed by GitHub
Browse files

Merge pull request #23492 from mantidproject/23426_elemental_analysis_linking

Elemental Analysis - Linking
parents 18acb4dc 9350244a
No related branches found
No related tags found
No related merge requests found
Showing
with 2783 additions and 2537 deletions
...@@ -3,8 +3,10 @@ from __future__ import absolute_import, print_function ...@@ -3,8 +3,10 @@ from __future__ import absolute_import, print_function
from PyQt4 import QtGui from PyQt4 import QtGui
import sys import sys
import random
from time import time from itertools import cycle
from six import iteritems
from Muon.GUI.ElementalAnalysis.PeriodicTable.periodic_table_presenter import PeriodicTablePresenter from Muon.GUI.ElementalAnalysis.PeriodicTable.periodic_table_presenter import PeriodicTablePresenter
from Muon.GUI.ElementalAnalysis.PeriodicTable.periodic_table_view import PeriodicTableView from Muon.GUI.ElementalAnalysis.PeriodicTable.periodic_table_view import PeriodicTableView
...@@ -39,46 +41,167 @@ class ElementalAnalysisGui(QtGui.QMainWindow): ...@@ -39,46 +41,167 @@ class ElementalAnalysisGui(QtGui.QMainWindow):
self.ptable = PeriodicTablePresenter( self.ptable = PeriodicTablePresenter(
PeriodicTableView(), PeriodicTableModel()) PeriodicTableView(), PeriodicTableModel())
self.ptable.register_table_changed(self.table_changed)
self.ptable.register_table_lclicked(self.table_left_clicked) self.ptable.register_table_lclicked(self.table_left_clicked)
self.ptable.register_table_rclicked(self.table_right_clicked) self.ptable.register_table_rclicked(self.table_right_clicked)
self.load_widget = LoadPresenter( self.load_widget = LoadPresenter(
LoadView(), LoadModel(), CoLoadModel()) LoadView(), LoadModel(), CoLoadModel())
self.load_widget.on_loading_finished(self.loading_finished)
self.widget_list = QtGui.QVBoxLayout() self.widget_list = QtGui.QVBoxLayout()
self.detectors = DetectorsPresenter(DetectorsView()) self.detectors = DetectorsPresenter(DetectorsView())
for detector in self.detectors.detectors:
detector.on_checkbox_checked(self.add_plot)
detector.on_checkbox_unchecked(self.del_plot)
self.peaks = PeaksPresenter(PeaksView()) self.peaks = PeaksPresenter(PeaksView())
self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
self.peaks.electron.on_checkbox_checked(self.electrons_checked)
self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)
self.widget_list.addWidget(self.peaks.view) self.widget_list.addWidget(self.peaks.view)
self.widget_list.addWidget(self.detectors.view) self.widget_list.addWidget(self.detectors.view)
self.widget_list.addWidget(self.load_widget.view) self.widget_list.addWidget(self.load_widget.view)
self.plotting = PlotPresenter(PlotView()) self.plotting = PlotPresenter(PlotView())
self.plotting.view.setFixedSize(self.plotting.view.sizeHint()) self.plotting.view.setMinimumSize(self.plotting.view.sizeHint())
self.add = QtGui.QPushButton("Add")
self.add.clicked.connect(self.add_plot)
self.rem = QtGui.QPushButton("Del")
self.rem.clicked.connect(self.del_plot)
self.box = QtGui.QHBoxLayout() self.box = QtGui.QHBoxLayout()
self.box.addWidget(self.ptable.view) self.box.addWidget(self.ptable.view)
self.box.addLayout(self.widget_list) self.box.addLayout(self.widget_list)
self.box.addWidget(self.load_widget.view)
self.box.addWidget(self.add)
self.box.addWidget(self.rem)
# layout.addWidget(self.plot_view)
self.setCentralWidget(QtGui.QWidget(self)) self.setCentralWidget(QtGui.QWidget(self))
self.centralWidget().setLayout(self.box) self.centralWidget().setLayout(self.box)
self.setWindowTitle("Elemental Analysis") self.setWindowTitle("Elemental Analysis")
self.plotting.view.show()
self.element_widgets = {} self.element_widgets = {}
self.element_data = {} self.element_data = {}
self.element_lines = {}
self.gamma_lines = []
self.gamma_peaks = self.ptable.peak_data["Gammas"]
self.electron_peaks = self._get_electron_peaks()
self.electron_lines = []
self._generate_element_widgets() self._generate_element_widgets()
self._generate_element_data() self._generate_element_data()
self.line_colours = cycle(["r", "g", "b", "c", "m", "y"])
def iterate_over_selectors(self, check_state, primary_checkboxes=True):
"""
Iterates over element popups.
:param check_state: True or False - i.e. check boxes or not
:param primary_checkboxes: True if Primary, False if Secondary
"""
for element, selector in iteritems(self.element_widgets):
for checkbox in selector.primary_checkboxes if primary_checkboxes else selector.secondary_checkboxes:
checkbox.setChecked(check_state)
selector.finish_selection()
def major_peaks_checked(self):
self.iterate_over_selectors(True, primary_checkboxes=True)
def major_peaks_unchecked(self):
self.iterate_over_selectors(False, primary_checkboxes=True)
self.plotting.update_canvas()
def minor_peaks_checked(self):
self.iterate_over_selectors(True, primary_checkboxes=False)
def minor_peaks_unchecked(self):
self.iterate_over_selectors(False, primary_checkboxes=False)
self.plotting.update_canvas()
def _plot_gammas(self, subplot, colour=None):
if colour is None:
colour = self.line_colours.next()
for element, peaks in iteritems(self.gamma_peaks):
for peak_type, peak in iteritems(peaks):
if peak is None:
continue
self.gamma_lines.append(
subplot.axvline(
peak, 0, 1, color=colour))
self.plotting.update_canvas()
def _iterate_over_gamma_selectors(self, check_state):
for element, selector in iteritems(self.element_widgets):
for checkbox in selector.gamma_checkboxes:
checkbox.setChecked(check_state)
selector.finish_selection()
def gammas_checked(self):
self._iterate_over_gamma_selectors(True)
def gammas_unchecked(self):
self._iterate_over_gamma_selectors(False)
for line in self.gamma_lines:
line.remove()
del line
self.gamma_lines = []
self.plotting.update_canvas()
def _get_electron_peaks(self):
return self.ptable.peak_data["Electrons"].copy()
def _plot_electrons(self, subplot, colour=None):
if colour is None:
colour = self.line_colours.next()
for peak, intensity in iteritems(self.electron_peaks):
# intensity will be used in the future for labelling lines
self.electron_lines.append(
subplot.axvline(
float(peak), 0, 1, color=colour))
self.plotting.update_canvas()
def electrons_checked(self):
colour = self.line_colours.next()
for subplot_name, subplot in iteritems(self.plotting.get_subplots()):
self._plot_electrons(subplot, colour=colour)
def electrons_unchecked(self):
for line in self.electron_lines:
line.remove()
del line
self.electron_lines = []
self.plotting.update_canvas()
def load_run(self, detector, run):
name = "{}; Detector {}".format(run, detector[-1])
subplot = self.plotting.add_subplot(detector)
subplot.set_title(detector)
for plot in mantid.mtd[name]:
self.plotting.plot(detector, plot)
if self.plotting.view.isHidden():
self.plotting.view.show()
if self.peaks.gamma.isChecked():
self._plot_gammas(subplot)
if self.peaks.electron.isChecked():
self._plot_electrons(subplot)
self.plotting.update_canvas()
def load_last_run(self, detector):
self.load_run(detector, self.load_widget.last_loaded_run())
def loading_finished(self):
last_run = self.load_widget.last_loaded_run()
if last_run is None:
return
self.plotting.view.setWindowTitle(str(last_run))
for plot in self.plotting.get_subplots():
self.plotting.remove_subplot(plot)
for detector in self.detectors.detectors:
if detector.isChecked():
self.load_run(detector.name, last_run)
for item in self.ptable.selection:
self._add_element_lines(
item.symbol, self.element_data[item.symbol])
def _generate_element_data(self): def _generate_element_data(self):
for element in self.ptable.peak_data: for element in self.ptable.peak_data:
if element in ["Gammas", "Electrons"]: if element in ["Gammas", "Electrons"]:
...@@ -89,8 +212,62 @@ class ElementalAnalysisGui(QtGui.QMainWindow): ...@@ -89,8 +212,62 @@ class ElementalAnalysisGui(QtGui.QMainWindow):
except KeyError: except KeyError:
continue continue
def _add_element_line(self, x_value, element, colour="b"):
if x_value is None:
return
for plot_name in self.plotting.get_subplots():
line = self.plotting.get_subplot(
plot_name).axvline(x_value, 0, 1, color=colour)
try:
self.element_lines[element][x_value].append(line)
except KeyError:
self.element_lines[element][x_value] = [line]
self.plotting.update_canvas()
def _add_element_lines(self, element, data):
self.element_lines[element] = {}
colour = self.line_colours.next()
for label, x_value in iteritems(data):
# label will be used in the future for labelling lines
self._add_element_line(x_value, element, colour=colour)
def _remove_element_lines(self, element):
for x_value, lines in iteritems(self.element_lines[element]):
for line in lines:
line.remove()
del line
self.plotting.update_canvas()
self.element_lines[element] = {}
def _update_element_lines(self, element, current_dict, new_dict):
# can be split up: this section removes lines
if len(current_dict) > len(new_dict): # i.e. item removed
dict_difference = {k: current_dict[k]
for k in set(current_dict) - set(new_dict)}
for label, x_value in iteritems(dict_difference):
# label will be used in the future for labelling lines
for line in self.element_lines[element][x_value]:
line.remove()
del line
self.element_lines[element][x_value] = []
del current_dict[label]
self.plotting.update_canvas()
# can be split up: this section adds lines
elif current_dict != new_dict: # i.e. item added or not closed without changes
colour = self.line_colours.next()
dict_difference = {k: new_dict[k]
for k in set(new_dict) - set(current_dict)}
for label, x_value in iteritems(dict_difference):
# label will be used in the future for labelling lines
self._add_element_line(x_value, element, colour)
current_dict.update(dict_difference)
def _update_peak_data(self, element, data): def _update_peak_data(self, element, data):
self.element_data[element] = data if self.ptable.is_selected(element):
self._update_element_lines(
element, self.element_data[element], data)
else:
self.element_data[element] = data.copy()
def _generate_element_widgets(self): def _generate_element_widgets(self):
self.element_widgets = {} self.element_widgets = {}
...@@ -98,42 +275,69 @@ class ElementalAnalysisGui(QtGui.QMainWindow): ...@@ -98,42 +275,69 @@ class ElementalAnalysisGui(QtGui.QMainWindow):
if element in ["Gammas", "Electrons"]: if element in ["Gammas", "Electrons"]:
continue continue
data = self.ptable.element_data(element) data = self.ptable.element_data(element)
try:
data["Gammas"] = self.ptable.peak_data["Gammas"][element]
except KeyError:
pass
widget = PeakSelectorPresenter(PeakSelectorView(data, element)) widget = PeakSelectorPresenter(PeakSelectorView(data, element))
widget.on_finished(self._update_peak_data) widget.on_finished(self._update_peak_data)
self.element_widgets[element] = widget self.element_widgets[element] = widget
def table_left_clicked(self, item): def table_left_clicked(self, item):
print("Element Left Clicked: {}".format( if self.ptable.is_selected(item.symbol):
self.element_data[item.symbol])) self._add_element_lines(
item.symbol, self.element_data[item.symbol])
else:
self._remove_element_lines(item.symbol)
def table_right_clicked(self, item): def table_right_clicked(self, item):
self.element_widgets[item.symbol].view.show() self.element_widgets[item.symbol].view.show()
print("Element Right Clicked: {}".format(item.symbol))
def table_changed(self, items): def _clear_lines(self, lines):
print("Table Changed: {}".format([i.symbol for i in items])) for line in lines:
line.remove()
del line
return []
def _clear_lines_after_data_file_selected(self):
for element in self.element_lines.keys():
self._remove_element_lines(element)
self.electron_lines = self._clear_lines(self.electron_lines)
self.gamma_lines = self._clear_lines(self.gamma_lines)
for checkbox in self.peaks.peak_checkboxes:
checkbox.setChecked(False)
def select_data_file(self): def select_data_file(self):
filename = str(QtGui.QFileDialog.getOpenFileName()) filename = str(QtGui.QFileDialog.getOpenFileName())
if filename: if filename:
self.ptable.set_peak_datafile(filename) self.ptable.set_peak_datafile(filename)
self._clear_lines_after_data_file_selected()
self._generate_element_widgets() self._generate_element_widgets()
self._generate_element_data() self._generate_element_data()
def add_plot(self): def add_plot(self, checkbox):
name = "Plot {}".format(time()) detector = checkbox.name
subplot = self.plotting.add_subplot(name) last_run = self.load_widget.last_loaded_run()
self.plotting.call_plot_method(name, subplot.set_title, name) # not using load_last_run prevents two calls to last_loaded_run()
plot1 = mantid.CreateSampleWorkspace(OutputWorkspace=str(time())) if last_run is not None:
self.plotting.plot(name, plot1) self.load_run(detector, last_run)
plot2 = mantid.Plus(plot1, plot1, OutputWorkspace=str(time())) colour = self.line_colours.next()
self.plotting.plot(name, plot2) for element in self.ptable.selection:
self.plotting.add_hline(name, 0.06, 0, 1) for label, x_value in iteritems(self.element_data[element.symbol]):
self.plotting.add_vline(name, 10100, 0, 1) # label will be used in the future for labelling lines
line = self.plotting.get_subplot(detector).axvline(
def del_plot(self): x_value, 1, 0, color=colour)
to_del = random.choice(self.plotting.get_subplots().keys()) try:
self.plotting.remove_subplot(to_del) self.element_lines[element.symbol][x_value].append(line)
except KeyError:
self.element_lines[element.symbol][x_value] = [line]
self.plotting.update_canvas()
def del_plot(self, checkbox):
if self.load_widget.last_loaded_run() is not None:
self.plotting.remove_subplot(checkbox.name)
if not self.plotting.get_subplots():
self.plotting.view.close()
def qapp(): def qapp():
......
...@@ -12,9 +12,11 @@ class LoadPresenter(object): ...@@ -12,9 +12,11 @@ class LoadPresenter(object):
self.co_thread = None self.co_thread = None
self.view.on_load_clicked(self.equalise_loaded_runs) self.view.on_load_clicked(self.equalise_loaded_runs)
self.view.on_load_clicked(self.equalise_last_loaded_run)
self.view.on_load_clicked(self.load_run) self.view.on_load_clicked(self.load_run)
self.view.on_load_clicked(self.co_model.wipe_co_runs) self.view.on_load_clicked(self.co_model.wipe_co_runs)
self.view.on_co_add_clicked(self.equalise_loaded_runs) self.view.on_co_add_clicked(self.equalise_loaded_runs)
self.view.on_co_add_clicked(self.equalise_last_loaded_run)
self.view.on_co_add_clicked(self.co_add_run) self.view.on_co_add_clicked(self.co_add_run)
self.view.on_spinbox_changed(self.update_models) self.view.on_spinbox_changed(self.update_models)
...@@ -25,6 +27,13 @@ class LoadPresenter(object): ...@@ -25,6 +27,13 @@ class LoadPresenter(object):
self.co_model.loaded_runs = loaded_runs self.co_model.loaded_runs = loaded_runs
self.load_model.loaded_runs = loaded_runs self.load_model.loaded_runs = loaded_runs
def equalise_last_loaded_run(self):
last_run = max(
self.co_model.last_loaded_runs,
self.load_model.last_loaded_runs)
self.co_model.last_loaded_runs = last_run
self.load_model.last_loaded_runs = last_run
def update_models(self, run): def update_models(self, run):
self.load_model.set_run(run) self.load_model.set_run(run)
self.co_model.set_run(run) self.co_model.set_run(run)
...@@ -59,3 +68,15 @@ class LoadPresenter(object): ...@@ -59,3 +68,15 @@ class LoadPresenter(object):
def new_thread(self, model): def new_thread(self, model):
return thread_model.ThreadModel(model) return thread_model.ThreadModel(model)
def last_loaded_run(self):
try:
return self.load_model.last_loaded_runs[-1]
except IndexError:
return None
def on_loading_finished(self, slot):
self.view.on_loading_finished(slot)
def unreg_on_loading_finished(self, slot):
self.view.unreg_on_loading_finished(slot)
from __future__ import absolute_import from __future__ import absolute_import
from PyQt4 import QtGui from PyQt4 import QtGui, QtCore
from mantidqtpython import MantidQt from mantidqtpython import MantidQt
class LoadView(QtGui.QWidget): class LoadView(QtGui.QWidget):
sig_loading_finished = QtCore.pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
super(LoadView, self).__init__(parent) super(LoadView, self).__init__(parent)
...@@ -39,6 +41,7 @@ class LoadView(QtGui.QWidget): ...@@ -39,6 +41,7 @@ class LoadView(QtGui.QWidget):
self.spinbox.setEnabled(True) self.spinbox.setEnabled(True)
self.load_button.setEnabled(True) self.load_button.setEnabled(True)
self.co_button.setEnabled(True) self.co_button.setEnabled(True)
self.sig_loading_finished.emit()
def on_load_clicked(self, slot): def on_load_clicked(self, slot):
self.load_button.clicked.connect(slot) self.load_button.clicked.connect(slot)
...@@ -66,3 +69,12 @@ class LoadView(QtGui.QWidget): ...@@ -66,3 +69,12 @@ class LoadView(QtGui.QWidget):
self.spinbox.valueChanged.disconnect(slot) self.spinbox.valueChanged.disconnect(slot)
except TypeError: except TypeError:
return return
def on_loading_finished(self, slot):
self.sig_loading_finished.connect(slot)
def unreg_on_loading_finished(self, slot):
try:
self.sig_loading_finished.disconnect(slot)
except TypeError:
return
class DetectorsPresenter(object): class DetectorsPresenter(object):
def __init__(self, view): def __init__(self, view):
self.view = view self.view = view
self.detectors = [
self.view.GE1,
self.view.GE2,
self.view.GE3,
self.view.GE4]
...@@ -12,6 +12,8 @@ class LoadModel(lutils.LModel): ...@@ -12,6 +12,8 @@ class LoadModel(lutils.LModel):
def execute(self): def execute(self):
if self.run not in self.loaded_runs: if self.run not in self.loaded_runs:
self.load_run() self.load_run()
else:
self.last_loaded_runs.append(self.run)
class CoLoadModel(lutils.LModel): class CoLoadModel(lutils.LModel):
...@@ -30,6 +32,8 @@ class CoLoadModel(lutils.LModel): ...@@ -30,6 +32,8 @@ class CoLoadModel(lutils.LModel):
if current_ws is None: if current_ws is None:
return return
self.loaded_runs[self.run] = current_ws self.loaded_runs[self.run] = current_ws
else:
self.last_loaded_runs.append(self.run)
if self.run not in self.co_runs: if self.run not in self.co_runs:
self.co_runs.append(self.run) self.co_runs.append(self.run)
if self.workspace: if self.workspace:
...@@ -45,6 +49,7 @@ class CoLoadModel(lutils.LModel): ...@@ -45,6 +49,7 @@ class CoLoadModel(lutils.LModel):
def co_load_run(self, workspace): def co_load_run(self, workspace):
run = lutils.hyphenise(self.co_runs) run = lutils.hyphenise(self.co_runs)
self.last_loaded_runs.append(run)
to_add = [self.add_runs(l, r, run) for l, r in zip(*lutils.flatten_run_data( to_add = [self.add_runs(l, r, run) for l, r in zip(*lutils.flatten_run_data(
self.workspace, workspace))] self.workspace, workspace))]
self.workspace = lutils.group_by_detector(run, to_add) self.workspace = lutils.group_by_detector(run, to_add)
...@@ -13,11 +13,13 @@ class LModel(object): ...@@ -13,11 +13,13 @@ class LModel(object):
def __init__(self): def __init__(self):
self.run = 0 self.run = 0
self.loaded_runs = {} self.loaded_runs = {}
self.last_loaded_runs = []
def _load(self, inputs): def _load(self, inputs):
""" inputs is a dict mapping filepaths to output names """ """ inputs is a dict mapping filepaths to output names """
for path, output in iteritems(inputs): for path, output in iteritems(inputs):
mantid.LoadAscii(path, OutputWorkspace=output) workspace = mantid.LoadAscii(path, OutputWorkspace=output)
workspace.getAxis(0).setUnit("Label").setLabel("Energy", "keV")
def load_run(self): def load_run(self):
to_load = search_user_dirs(self.run) to_load = search_user_dirs(self.run)
...@@ -28,6 +30,7 @@ class LModel(object): ...@@ -28,6 +30,7 @@ class LModel(object):
self._load(workspaces) self._load(workspaces)
self.loaded_runs[self.run] = group_by_detector( self.loaded_runs[self.run] = group_by_detector(
self.run, workspaces.values()) self.run, workspaces.values())
self.last_loaded_runs.append(self.run)
return self.loaded_runs[self.run] return self.loaded_runs[self.run]
def output(self): def output(self):
......
class PeaksPresenter(object): class PeaksPresenter(object):
def __init__(self, view): def __init__(self, view):
self.view = view self.view = view
self.major = self.view.major
self.minor = self.view.minor
self.gamma = self.view.gamma
self.electron = self.view.electron
self.peak_checkboxes = self.view.peak_checkboxes
...@@ -13,6 +13,12 @@ class PeaksView(QtGui.QWidget): ...@@ -13,6 +13,12 @@ class PeaksView(QtGui.QWidget):
self.minor = Checkbox("Minor Peaks") self.minor = Checkbox("Minor Peaks")
self.gamma = Checkbox("Gamma Peaks") self.gamma = Checkbox("Gamma Peaks")
self.electron = Checkbox("Electron Peaks") self.electron = Checkbox("Electron Peaks")
for peak_type in [self.major, self.minor, self.gamma, self.electron]:
self.peak_checkboxes = [
self.major,
self.minor,
self.gamma,
self.electron]
for peak_type in self.peak_checkboxes:
self.list.addWidget(peak_type) self.list.addWidget(peak_type)
self.setLayout(self.list) self.setLayout(self.list)
class PeakSelectorPresenter(object): class PeakSelectorPresenter(object):
def __init__(self, view): def __init__(self, view):
self.view = view self.view = view
self.primary_checkboxes = self.view.primary_checkboxes
self.secondary_checkboxes = self.view.secondary_checkboxes
self.gamma_checkboxes = self.view.gamma_checkboxes
def finish_selection(self):
self.view.finish_selection()
def update_peak_data(self, data): def update_peak_data(self, data):
self.view.update_peak_data(data) self.view.update_peak_data(data)
......
...@@ -19,9 +19,17 @@ class PeakSelectorView(QtGui.QListWidget): ...@@ -19,9 +19,17 @@ class PeakSelectorView(QtGui.QListWidget):
self.list = QtGui.QVBoxLayout(self) self.list = QtGui.QVBoxLayout(self)
primary = peak_data["Primary"] primary = peak_data["Primary"]
self._create_checkbox_list("Primary", primary) self.primary_checkboxes = self._create_checkbox_list(
"Primary", primary)
secondary = peak_data["Secondary"] secondary = peak_data["Secondary"]
self._create_checkbox_list("Secondary", secondary, checked=False) self.secondary_checkboxes = self._create_checkbox_list(
"Secondary", secondary, checked=False)
try:
gammas = peak_data["Gammas"]
self.gamma_checkboxes = self._create_checkbox_list(
"Gammas", gammas, checked=False)
except KeyError:
self.gamma_checkboxes = []
widget.setLayout(self.list) widget.setLayout(self.list)
scroll = QtGui.QScrollArea() scroll = QtGui.QScrollArea()
...@@ -34,8 +42,11 @@ class PeakSelectorView(QtGui.QListWidget): ...@@ -34,8 +42,11 @@ class PeakSelectorView(QtGui.QListWidget):
self.setLayout(scroll_layout) self.setLayout(scroll_layout)
def closeEvent(self, event): def finish_selection(self):
self.sig_finished_selection.emit(self.element, self.new_data) self.sig_finished_selection.emit(self.element, self.new_data)
def closeEvent(self, event):
self.finish_selection()
event.accept() event.accept()
def update_new_data(self, data): def update_new_data(self, data):
...@@ -45,15 +56,23 @@ class PeakSelectorView(QtGui.QListWidget): ...@@ -45,15 +56,23 @@ class PeakSelectorView(QtGui.QListWidget):
new_data = data["Primary"].copy() new_data = data["Primary"].copy()
self.new_data = new_data self.new_data = new_data
def _setup_checkbox(self, name, checked):
checkbox = Checkbox(name)
checkbox.setChecked(checked)
checkbox.on_checkbox_unchecked(self._remove_value_from_new_data)
checkbox.on_checkbox_checked(self._add_value_to_new_data)
self.list.addWidget(checkbox)
return checkbox
def _create_checkbox_list(self, heading, checkbox_data, checked=True): def _create_checkbox_list(self, heading, checkbox_data, checked=True):
_heading = QtGui.QLabel(heading) _heading = QtGui.QLabel(heading)
self.list.addWidget(_heading) self.list.addWidget(_heading)
checkboxes = []
for peak_type, value in iteritems(checkbox_data): for peak_type, value in iteritems(checkbox_data):
checkbox = Checkbox("{}: {}".format(peak_type, value)) checkboxes.append(
checkbox.setChecked(checked) self._setup_checkbox(
checkbox.on_checkbox_unchecked(self._remove_value_from_new_data) "{}: {}".format(peak_type, value), checked))
checkbox.on_checkbox_checked(self._add_value_to_new_data) return checkboxes
self.list.addWidget(checkbox)
def _parse_checkbox_name(self, name): def _parse_checkbox_name(self, name):
peak_type, value = name.replace(" ", "").split(":") peak_type, value = name.replace(" ", "").split(":")
......
from PyQt4 import QtGui, QtCore from qtpy import QtGui, QtCore, QtWidgets
class AxisChangerView(QtGui.QWidget): class AxisChangerView(QtWidgets.QWidget):
sig_lower_bound_changed = QtCore.pyqtSignal(object) sig_lower_bound_changed = QtCore.Signal(object)
sig_upper_bound_changed = QtCore.pyqtSignal(object) sig_upper_bound_changed = QtCore.Signal(object)
def __init__(self, label): def __init__(self, label):
super(AxisChangerView, self).__init__() super(AxisChangerView, self).__init__()
layout = QtGui.QHBoxLayout() layout = QtWidgets.QHBoxLayout()
layout.addWidget(QtGui.QLabel(label)) layout.addWidget(QtWidgets.QLabel(label))
self.lower_bound = QtGui.QLineEdit() self.lower_bound = QtWidgets.QLineEdit()
self.lower_bound.setValidator(QtGui.QDoubleValidator()) self.lower_bound.setValidator(QtGui.QDoubleValidator())
self.lower_bound.returnPressed.connect(self._lower_bound_changed) self.lower_bound.returnPressed.connect(self._lower_bound_changed)
self.upper_bound = QtGui.QLineEdit() self.upper_bound = QtWidgets.QLineEdit()
self.upper_bound.setValidator(QtGui.QDoubleValidator()) self.upper_bound.setValidator(QtGui.QDoubleValidator())
self.upper_bound.returnPressed.connect(self._upper_bound_changed) self.upper_bound.returnPressed.connect(self._upper_bound_changed)
layout.addWidget(self.lower_bound) layout.addWidget(self.lower_bound)
layout.addWidget(QtGui.QLabel("to")) layout.addWidget(QtWidgets.QLabel("to"))
layout.addWidget(self.upper_bound) layout.addWidget(self.upper_bound)
self.setLayout(layout) self.setLayout(layout)
......
...@@ -2,6 +2,10 @@ class PlotPresenter(object): ...@@ -2,6 +2,10 @@ class PlotPresenter(object):
def __init__(self, view): def __init__(self, view):
self.view = view self.view = view
def update_canvas(self):
""" Redraws the canvas. """
self.view.canvas.draw()
def get_subplot(self, name): def get_subplot(self, name):
""" """
Returns the subplot with the given name. Returns the subplot with the given name.
...@@ -39,45 +43,6 @@ class PlotPresenter(object): ...@@ -39,45 +43,6 @@ class PlotPresenter(object):
""" Removes the subplot corresponding to 'name' from the plotting window """ """ Removes the subplot corresponding to 'name' from the plotting window """
self.view.remove_subplot(name) self.view.remove_subplot(name)
def call_plot_method(self, name, func, *args, **kwargs):
"""
Calls the function with the specified arguments and returns the result:
the call will be 'saved' and 'replayed' when the plots are redrawn (errors checkbox is changed).
"""
return self.view.call_plot_method(name, func, *args, **kwargs)
def add_vline(self, plot_name, x_value, y_min, y_max, **kwargs):
"""
Adds a vertical line to a plot.
This line will be re-added when the plots are redrawn (errors checkbox is changed).
:param plot_name: the plot on which to add the line
:param x_value: the x value for the axvline
:param y_min: 0 <= y_min <= 1. The minimum y-value of the line (multiple of the y-axis)
:param y_min: 0 <= y_max <= 1. The maximum y-value of the line (multiple of the y-axis)
:param **kwargs: any keyword arguments for the matplotlib line object
:returns: a matplotlib line object
:raise KeyError: if the subplot plot_name does not exist
"""
return self.view.add_vline(plot_name, x_value, y_min, y_max, **kwargs)
def add_hline(self, plot_name, y_value, x_min, x_max, **kwargs):
"""
Adds a horizontal line to a plot.
This line will be re-added when the plots are redrawn (errors checkbox is changed).
:param plot_name: the plot on which to add the line
:param y_value: the y value for the axvline
:param x_min: 0 <= x_min <= 1. The minimum x-value of the line (multiple of the x-axis)
:param x_min: 0 <= x_max <= 1. The maximum x-value of the line (multiple of the x-axis)
:param **kwargs: any keyword arguments for the matplotlib line object
:returns: a matplotlib line object
:raise KeyError: if the subplot plot_name does not exist
"""
return self.view.add_hline(plot_name, y_value, x_min, x_max, **kwargs)
def add_moveable_vline(self, plot_name, x_value, y_minx, y_max, **kwargs): def add_moveable_vline(self, plot_name, x_value, y_minx, y_max, **kwargs):
pass pass
......
...@@ -3,7 +3,7 @@ from six import iteritems ...@@ -3,7 +3,7 @@ from six import iteritems
from mantid import plots from mantid import plots
from collections import OrderedDict from collections import OrderedDict
from PyQt4 import QtGui from qtpy import QtWidgets
from matplotlib.figure import Figure from matplotlib.figure import Figure
from matplotlib import gridspec from matplotlib import gridspec
...@@ -16,13 +16,13 @@ from Muon.GUI.ElementalAnalysis.Plotting.AxisChanger.axis_changer_presenter impo ...@@ -16,13 +16,13 @@ from Muon.GUI.ElementalAnalysis.Plotting.AxisChanger.axis_changer_presenter impo
from Muon.GUI.ElementalAnalysis.Plotting.AxisChanger.axis_changer_view import AxisChangerView from Muon.GUI.ElementalAnalysis.Plotting.AxisChanger.axis_changer_view import AxisChangerView
class PlotView(QtGui.QWidget): class PlotView(QtWidgets.QWidget):
def __init__(self): def __init__(self):
super(PlotView, self).__init__() super(PlotView, self).__init__()
self.plots = OrderedDict({}) self.plots = OrderedDict({})
self.errors_list = set() self.errors_list = set()
self.workspaces = {} self.workspaces = {}
self.plot_additions = {} self.workspace_plots = {} # stores the plotted 'graphs' for deletion
self.current_grid = None self.current_grid = None
self.gridspecs = { self.gridspecs = {
1: gridspec.GridSpec(1, 1), 1: gridspec.GridSpec(1, 1),
...@@ -34,11 +34,11 @@ class PlotView(QtGui.QWidget): ...@@ -34,11 +34,11 @@ class PlotView(QtGui.QWidget):
self.figure.set_facecolor("none") self.figure.set_facecolor("none")
self.canvas = FigureCanvas(self.figure) self.canvas = FigureCanvas(self.figure)
self.plot_selector = QtGui.QComboBox() self.plot_selector = QtWidgets.QComboBox()
self._update_plot_selector() self._update_plot_selector()
self.plot_selector.currentIndexChanged[str].connect(self._set_bounds) self.plot_selector.currentIndexChanged[str].connect(self._set_bounds)
button_layout = QtGui.QHBoxLayout() button_layout = QtWidgets.QHBoxLayout()
self.x_axis_changer = AxisChangerPresenter(AxisChangerView("X")) self.x_axis_changer = AxisChangerPresenter(AxisChangerView("X"))
self.x_axis_changer.on_upper_bound_changed(self._update_x_axis_upper) self.x_axis_changer.on_upper_bound_changed(self._update_x_axis_upper)
self.x_axis_changer.on_lower_bound_changed(self._update_x_axis_lower) self.x_axis_changer.on_lower_bound_changed(self._update_x_axis_lower)
...@@ -47,7 +47,7 @@ class PlotView(QtGui.QWidget): ...@@ -47,7 +47,7 @@ class PlotView(QtGui.QWidget):
self.y_axis_changer.on_upper_bound_changed(self._update_y_axis_upper) self.y_axis_changer.on_upper_bound_changed(self._update_y_axis_upper)
self.y_axis_changer.on_lower_bound_changed(self._update_y_axis_lower) self.y_axis_changer.on_lower_bound_changed(self._update_y_axis_lower)
self.errors = QtGui.QCheckBox("Errors") self.errors = QtWidgets.QCheckBox("Errors")
self.errors.stateChanged.connect(self._errors_changed) self.errors.stateChanged.connect(self._errors_changed)
button_layout.addWidget(self.plot_selector) button_layout.addWidget(self.plot_selector)
...@@ -55,7 +55,7 @@ class PlotView(QtGui.QWidget): ...@@ -55,7 +55,7 @@ class PlotView(QtGui.QWidget):
button_layout.addWidget(self.y_axis_changer.view) button_layout.addWidget(self.y_axis_changer.view)
button_layout.addWidget(self.errors) button_layout.addWidget(self.errors)
grid = QtGui.QGridLayout() grid = QtWidgets.QGridLayout()
grid.addWidget(self.canvas, 0, 0) grid.addWidget(self.canvas, 0, 0)
grid.addLayout(button_layout, 1, 0) grid.addLayout(button_layout, 1, 0)
self.setLayout(grid) self.setLayout(grid)
...@@ -68,38 +68,33 @@ class PlotView(QtGui.QWidget): ...@@ -68,38 +68,33 @@ class PlotView(QtGui.QWidget):
""" """
def wraps(self, *args, **kwargs): def wraps(self, *args, **kwargs):
func(self, *args, **kwargs) output = func(self, *args, **kwargs)
if len(self.plots): if len(self.plots):
self.figure.tight_layout() self.figure.tight_layout()
self.canvas.draw() self.canvas.draw()
return wraps return output
def _save_addition(func):
"""
Simple decorator (@_save_addition) to 'Save' the function call to be
replayed later when the plots are cleared.
(https://www.python.org/dev/peps/pep-0318/)
"""
def wraps(self, name, *args, **kwargs):
try:
self.plot_additions[name].append((func, name, args, kwargs))
except KeyError:
self.plot_additions[name] = [(func, name, args, kwargs)]
func(self, name, *args, **kwargs)
return wraps return wraps
def _silent_checkbox_check(self, state): def _silent_checkbox_check(self, state):
""" Checks a checkbox without emitting a checked event. """
self.errors.blockSignals(True) self.errors.blockSignals(True)
self.errors.setChecked(state) self.errors.setChecked(state)
self.errors.blockSignals(False) self.errors.blockSignals(False)
def _set_plot_bounds(self, name, plot): def _set_plot_bounds(self, name, plot):
"""
Sets AxisChanger bounds to the given plot bounds and updates
the plot-specific error checkbox.
"""
self.x_axis_changer.set_bounds(plot.get_xlim()) self.x_axis_changer.set_bounds(plot.get_xlim())
self.y_axis_changer.set_bounds(plot.get_ylim()) self.y_axis_changer.set_bounds(plot.get_ylim())
self._silent_checkbox_check(name in self.errors_list) self._silent_checkbox_check(name in self.errors_list)
def _set_bounds(self, new_plot): def _set_bounds(self, new_plot):
"""
Sets AxisChanger bounds if a new plot is added, or removes the AxisChanger
fields if a plot is removed.
"""
new_plot = str(new_plot) new_plot = str(new_plot)
if new_plot and new_plot != "All": if new_plot and new_plot != "All":
plot = self.get_subplot(new_plot) plot = self.get_subplot(new_plot)
...@@ -109,15 +104,20 @@ class PlotView(QtGui.QWidget): ...@@ -109,15 +104,20 @@ class PlotView(QtGui.QWidget):
self.y_axis_changer.clear_bounds() self.y_axis_changer.clear_bounds()
def _get_current_plot_name(self): def _get_current_plot_name(self):
""" Returns the 'current' plot name based on the dropdown selector. """
return str(self.plot_selector.currentText()) return str(self.plot_selector.currentText())
def _get_current_plots(self): def _get_current_plots(self):
"""
Returns a list of the current plot, or all plots if 'All' is selected.
"""
name = self._get_current_plot_name() name = self._get_current_plot_name()
return self.plots.values() if name == "All" else [ return self.plots.values() if name == "All" else [
self.get_subplot(name)] self.get_subplot(name)]
@_redo_layout @_redo_layout
def _update_x_axis(self, bound): def _update_x_axis(self, bound):
""" Updates the plot's x limits with the specified bound. """
try: try:
for plot in self._get_current_plots(): for plot in self._get_current_plots():
plot.set_xlim(**bound) plot.set_xlim(**bound)
...@@ -125,13 +125,16 @@ class PlotView(QtGui.QWidget): ...@@ -125,13 +125,16 @@ class PlotView(QtGui.QWidget):
return return
def _update_x_axis_lower(self, bound): def _update_x_axis_lower(self, bound):
""" Updates the lower x axis limit. """
self._update_x_axis({"left": bound}) self._update_x_axis({"left": bound})
def _update_x_axis_upper(self, bound): def _update_x_axis_upper(self, bound):
""" Updates the upper x axis limit. """
self._update_x_axis({"right": bound}) self._update_x_axis({"right": bound})
@_redo_layout @_redo_layout
def _update_y_axis(self, bound): def _update_y_axis(self, bound):
""" Updates the plot's y limits with the specified bound. """
try: try:
for plot in self._get_current_plots(): for plot in self._get_current_plots():
plot.set_ylim(**bound) plot.set_ylim(**bound)
...@@ -139,12 +142,17 @@ class PlotView(QtGui.QWidget): ...@@ -139,12 +142,17 @@ class PlotView(QtGui.QWidget):
return return
def _update_y_axis_lower(self, bound): def _update_y_axis_lower(self, bound):
""" Updates the lower y axis limit. """
self._update_y_axis({"bottom": bound}) self._update_y_axis({"bottom": bound})
def _update_y_axis_upper(self, bound): def _update_y_axis_upper(self, bound):
""" Updates the upper y axis limit. """
self._update_y_axis({"top": bound}) self._update_y_axis({"top": bound})
def _modify_errors_list(self, name, state): def _modify_errors_list(self, name, state):
"""
Adds/Removes a plot name to the errors set depending on the 'state' bool.
"""
if state: if state:
self.errors_list.add(name) self.errors_list.add(name)
else: else:
...@@ -154,20 +162,27 @@ class PlotView(QtGui.QWidget): ...@@ -154,20 +162,27 @@ class PlotView(QtGui.QWidget):
return return
def _change_plot_errors(self, name, plot, state): def _change_plot_errors(self, name, plot, state):
"""
Removes the previous plot and redraws with/without errors depending on the state.
"""
self._modify_errors_list(name, state) self._modify_errors_list(name, state)
workspaces = self.workspaces[name] workspaces = self.workspaces[name]
self.workspaces[name] = [] self.workspaces[name] = []
# get the limits before replotting, so they appear unchanged.
x, y = plot.get_xlim(), plot.get_ylim() x, y = plot.get_xlim(), plot.get_ylim()
plot.clear() for old_plot in self.workspace_plots[name]:
old_plot.remove()
del old_plot
self.workspace_plots[name] = []
for workspace in workspaces: for workspace in workspaces:
self.plot(name, workspace) self.plot(name, workspace)
plot.set_xlim(x) plot.set_xlim(x)
plot.set_ylim(y) plot.set_ylim(y)
self._set_bounds(name) self._set_bounds(name) # set AxisChanger bounds again.
self._replay_additions(name)
@_redo_layout @_redo_layout
def _errors_changed(self, state): def _errors_changed(self, state):
""" Replots subplots with errors depending on the current selection. """
current_name = self._get_current_plot_name() current_name = self._get_current_plot_name()
if current_name == "All": if current_name == "All":
for name, plot in iteritems(self.plots): for name, plot in iteritems(self.plots):
...@@ -176,18 +191,19 @@ class PlotView(QtGui.QWidget): ...@@ -176,18 +191,19 @@ class PlotView(QtGui.QWidget):
self._change_plot_errors( self._change_plot_errors(
current_name, self.get_subplot(current_name), state) current_name, self.get_subplot(current_name), state)
def _replay_additions(self, name):
for func, name, args, kwargs in self.plot_additions[name]:
func(self, name, *args, **kwargs)
def _set_positions(self, positions): def _set_positions(self, positions):
""" Moves all subplots based on a gridspec change. """
for plot, pos in zip(self.plots.values(), positions): for plot, pos in zip(self.plots.values(), positions):
grid_pos = self.current_grid[pos[0], pos[1]] grid_pos = self.current_grid[pos[0], pos[1]]
plot.set_position(grid_pos.get_position(self.figure)) plot.set_position(
grid_pos.get_position(
self.figure)) # sets plot position, magic?
# required because tight_layout() is used.
plot.set_subplotspec(grid_pos) plot.set_subplotspec(grid_pos)
@_redo_layout @_redo_layout
def _update_gridspec(self, new_plots, last=None): def _update_gridspec(self, new_plots, last=None):
""" Updates the gridspec; adds a 'last' subplot if one is supplied. """
if new_plots: if new_plots:
self.current_grid = self.gridspecs[new_plots] self.current_grid = self.gridspecs[new_plots]
positions = putils.get_layout(new_plots) positions = putils.get_layout(new_plots)
...@@ -201,11 +217,13 @@ class PlotView(QtGui.QWidget): ...@@ -201,11 +217,13 @@ class PlotView(QtGui.QWidget):
self._update_plot_selector() self._update_plot_selector()
def _update_plot_selector(self): def _update_plot_selector(self):
""" Updates plot selector (dropdown). """
self.plot_selector.clear() self.plot_selector.clear()
self.plot_selector.addItem("All") self.plot_selector.addItem("All")
self.plot_selector.addItems(list(self.plots.keys())) self.plot_selector.addItems(list(self.plots.keys()))
def _add_workspace_name(self, name, workspace): def _add_workspace_name(self, name, workspace):
""" Adds a workspace to a plot's list of workspaces. """
try: try:
if workspace not in self.workspaces[name]: if workspace not in self.workspaces[name]:
self.workspaces[name].append(workspace) self.workspaces[name].append(workspace)
...@@ -214,6 +232,7 @@ class PlotView(QtGui.QWidget): ...@@ -214,6 +232,7 @@ class PlotView(QtGui.QWidget):
@_redo_layout @_redo_layout
def plot(self, name, workspace): def plot(self, name, workspace):
""" Plots a workspace to a subplot (with errors, if necessary). """
self._add_workspace_name(name, workspace) self._add_workspace_name(name, workspace)
if name in self.errors_list: if name in self.errors_list:
self.plot_workspace_errors(name, workspace) self.plot_workspace_errors(name, workspace)
...@@ -221,18 +240,35 @@ class PlotView(QtGui.QWidget): ...@@ -221,18 +240,35 @@ class PlotView(QtGui.QWidget):
self.plot_workspace(name, workspace) self.plot_workspace(name, workspace)
self._set_bounds(name) self._set_bounds(name)
def _add_plotted_line(self, name, lines):
""" Appends plotted lines to the related subplot list. """
try:
self.workspace_plots[name].extend(lines)
except KeyError:
self.workspace_plots[name] = lines
def plot_workspace_errors(self, name, workspace): def plot_workspace_errors(self, name, workspace):
""" Plots a workspace with errrors, and appends caps/bars to the subplot list. """
subplot = self.get_subplot(name) subplot = self.get_subplot(name)
plots.plotfunctions.errorbar(subplot, workspace, specNum=1) line, cap_lines, bar_lines = plots.plotfunctions.errorbar(
subplot, workspace, specNum=1)
all_lines = [line]
all_lines.extend(cap_lines)
all_lines.extend(bar_lines)
self._add_plotted_line(name, all_lines)
def plot_workspace(self, name, workspace): def plot_workspace(self, name, workspace):
""" Plots a workspace normally. """
subplot = self.get_subplot(name) subplot = self.get_subplot(name)
plots.plotfunctions.plot(subplot, workspace, specNum=1) line, = plots.plotfunctions.plot(subplot, workspace, specNum=1)
self._add_plotted_line(name, [line])
def get_subplot(self, name): def get_subplot(self, name):
""" Returns the subplot corresponding to a given name """
return self.plots[name] return self.plots[name]
def get_subplots(self): def get_subplots(self):
""" Returns all subplots. """
return self.plots return self.plots
def add_subplot(self, name): def add_subplot(self, name):
...@@ -245,35 +281,12 @@ class PlotView(QtGui.QWidget): ...@@ -245,35 +281,12 @@ class PlotView(QtGui.QWidget):
self.figure.delaxes(self.get_subplot(name)) self.figure.delaxes(self.get_subplot(name))
del self.plots[name] del self.plots[name]
del self.workspaces[name] del self.workspaces[name]
del self.plot_additions[name]
self._update_gridspec(len(self.plots)) self._update_gridspec(len(self.plots))
@_redo_layout @_redo_layout
@_save_addition
def call_plot_method(self, name, func, *args, **kwargs):
"""
Allows an arbitrary function call to be replayed
"""
return func(*args, **kwargs)
@_redo_layout
@_save_addition
def add_vline(self, plot_name, x_value, y_min, y_max, **kwargs):
return self.get_subplot(plot_name).axvline(
x_value, y_min, y_max, **kwargs)
@_redo_layout
@_save_addition
def add_hline(self, plot_name, y_value, x_min, x_max, **kwargs):
return self.get_subplot(plot_name).axhline(
y_value, x_min, x_max, **kwargs)
@_redo_layout
@_save_addition
def add_moveable_vline(self, plot_name, x_value, y_minx, y_max, **kwargs): def add_moveable_vline(self, plot_name, x_value, y_minx, y_max, **kwargs):
pass pass
@_redo_layout @_redo_layout
@_save_addition
def add_moveable_hline(self, plot_name, y_value, x_min, x_max, **kwargs): def add_moveable_hline(self, plot_name, y_value, x_min, x_max, **kwargs):
pass pass
This diff is collapsed.
import unittest import unittest
from Muon.GUI.ElementalAnalysis.Plotting.AxisChanger.axis_changer_view import AxisChangerView import os
os.environ["QT_API"] = "pyqt" # noqa E402
from Muon.GUI.ElementalAnalysis.Plotting.AxisChanger.axis_changer_view import AxisChangerView
from Muon.GUI.Common import mock_widget from Muon.GUI.Common import mock_widget
try: try:
from unittest import mock from unittest import mock
except ImportError: except ImportError:
......
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