diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 67fa883..16d701d 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -1,13 +1,36 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
@@ -33,10 +56,59 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -59,8 +131,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -75,7 +172,12 @@
1658317648754
-
+
+
+
+
+
+
1658494877565
@@ -91,7 +193,14 @@
1658744594836
-
+
+ 1658822265357
+
+
+
+ 1658822265357
+
+
@@ -107,11 +216,13 @@
+
-
+
+
@@ -127,13 +238,14 @@
- file://$USER_HOME$/labscript-suite/userlib/analysislib/example_apparatus/analysis_plot_panel/src/analysis_plot_panel/user_data_extractors.py
+ file://$USER_HOME$/labscript-suite/userlib/analysislib/example_apparatus/analysis_plot_panel/src/analysis_plot_panel/data_extractors.py
7
-
+
- file://$PROJECT_DIR$/test_absorption_imaging.py
-
+ file://$USER_HOME$/labscript-suite/userlib/analysislib/example_apparatus/analysis_plot_panel/src/analysis_plot_panel/user_plots.py
+ 7
+
@@ -146,6 +258,9 @@
+
+
+
\ No newline at end of file
diff --git a/dylab/AbsorptionImaging.py b/HelperClasses/AbsorptionImaging.py
similarity index 95%
rename from dylab/AbsorptionImaging.py
rename to HelperClasses/AbsorptionImaging.py
index 9a5b313..ff6e45d 100644
--- a/dylab/AbsorptionImaging.py
+++ b/HelperClasses/AbsorptionImaging.py
@@ -2,7 +2,7 @@ from lyse import *
from pylab import *
import numpy as np
import scipy.constants as constant
-from dylab import DyTransition, Camera
+from HelperClasses import DyTransition, Camera
import matplotlib.pyplot as plt
from matplotlib import colors as mcolors
import colorsys
@@ -54,6 +54,7 @@ class absorption_imaging:
# select effective region
self.image_absorption_cut = None
+ self.image_absorption_remove_background = None
self.x_start = None
self.y_start = None
self.x_end = None
@@ -134,6 +135,10 @@ class absorption_imaging:
self.image_absorption_cut = self.image_absorption[self.y_start:self.y_end, self.x_start:self.x_end]
+ back_ground = self.corner_subtract(self.image_absorption_cut, 0.1, 0.1)
+ self.image_absorption_cut = self.image_absorption_cut - back_ground
+ self.image_absorption_remove_background = self.image_absorption - back_ground
+
return self.image_absorption_cut
# The function do the analyzation for absorption imaging
@@ -184,19 +189,11 @@ class absorption_imaging:
if self.atom_number is not None and not force_to_run:
return self.atom_number
- if self.image_absorption is None or not force_to_run:
+ if self.image_absorption is None:
self.image_absorption = self.get_image_absorption()
if self.image_absorption_cut is None:
- self.image_absorption_cut = self.image_absorption
- self.x_start = 0
- self.x_end = self.image_absorption.shape[1]
- self.y_start = 0
- self.y_end = self.image_absorption.shape[0]
-
- back_ground = self.corner_subtract(self.image_absorption_cut, 0.1, 0.1)
- self.image_absorption_cut = self.image_absorption_cut - back_ground
- self.image_absorption = self.image_absorption - back_ground
+ self.select_effective_data((0, 0), np.shape(self.image_absorption))
OD_act = self.image_absorption_cut
diff --git a/dylab/Camera.py b/HelperClasses/Camera.py
similarity index 100%
rename from dylab/Camera.py
rename to HelperClasses/Camera.py
diff --git a/dylab/DyTransition.py b/HelperClasses/DyTransition.py
similarity index 91%
rename from dylab/DyTransition.py
rename to HelperClasses/DyTransition.py
index d203361..aabef80 100644
--- a/dylab/DyTransition.py
+++ b/HelperClasses/DyTransition.py
@@ -1,7 +1,7 @@
# Functions create constant dictionary storing transition information of Dy.
import scipy.constants as constant
-from dylab import TransitionClass
+from HelperClasses import TransitionClass
def creat_Dy421():
diff --git a/dylab/Fitting.py b/HelperClasses/Fitting.py
similarity index 99%
rename from dylab/Fitting.py
rename to HelperClasses/Fitting.py
index 5dc8f5e..e909591 100644
--- a/dylab/Fitting.py
+++ b/HelperClasses/Fitting.py
@@ -1,5 +1,5 @@
import numpy as np
-from dylab import FittingFunction
+from HelperClasses import FittingFunction
import laserbeamsize as lbs
import lmfit
from lmfit.minimizer import MinimizerResult
diff --git a/dylab/FittingFunction.py b/HelperClasses/FittingFunction.py
similarity index 100%
rename from dylab/FittingFunction.py
rename to HelperClasses/FittingFunction.py
diff --git a/HelperClasses/Plotting/DataExtractorLyse.py b/HelperClasses/Plotting/DataExtractorLyse.py
new file mode 100644
index 0000000..58228b7
--- /dev/null
+++ b/HelperClasses/Plotting/DataExtractorLyse.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Mar 15 15:08:46 2021
+
+@author: Nick Sauerwein
+"""
+
+import lyse
+import numpy as np
+
+import os.path
+import time
+
+import h5py
+
+
+def get_mtime(filename):
+ return time.ctime(os.path.getmtime(filename))
+
+
+class DataExtractorManager:
+
+ def __init__(self, data_path=None):
+
+ self.data_extractors = {}
+ self.h5_file = None
+ self.data_path = data_path
+ self.local_data_changed = False
+
+ def update_local_data(self, h5_path):
+ with h5py.File(h5_path, 'r') as h5_file:
+ self.local_data_changed = False
+ for key in self.data_extractors:
+ self.data_extractors[key].update_local_data(h5_path, h5_file=h5_file)
+ if self.data_extractors[key].local_data_changed:
+ self.local_data_changed = True
+
+ def clean_memory(self, h5_paths):
+ for key in self.data_extractors:
+ self.data_extractors[key].clean_memory(h5_paths)
+
+ def __getitem__(self, key):
+ return self.data_extractors[key]
+
+ def __setitem__(self, key, de):
+ self.data_extractors[key] = de
+
+ def add_data_extractor(self, label, type, data_path):
+
+ result = DataExtractor(label, type, data_path)
+ self.data_extractors[label] = result
+
+ return result
+
+ def get_data(self, h5_path, h5_file=None):
+ result = {}
+ for key in self.data_extractors:
+ result[key] = self.data_extractors[key].get_data(h5_path, h5_file=h5_file)
+
+ return result
+
+
+class DataExtractor:
+ def __init__(self, label, type, data_path, load_to_ram=True):
+
+ self.load_to_ram = load_to_ram
+
+ self.local_datas = {}
+ self.local_mtimes = {}
+
+ if self.load_to_ram:
+ self.local_data_changed = True
+
+ # The path of data in hdf5 file
+ self.data_path = data_path
+ self.data_type = type
+ self.data_label = label
+
+ def extract_data(self, h5_path, h5_file=None):
+
+ result = None
+
+ data_handle = lyse.Run(h5_path, no_write=True)
+
+ if self.data_type == 'array':
+ try:
+ result = data_handle.get_result_array(self.data_path[0], self.data_path[1],
+ h5_file=h5_file)
+ result = np.array(result)
+ except:
+ result = None
+
+ elif self.data_type == 'single':
+ try:
+ if len(self.data_path) == 1:
+ result = data_handle.get_globals(self.data_path[0], h5_file=h5_file)
+ else:
+ result = data_handle.get_result(self.data_path[0], self.data_path[1],
+ h5_file=h5_file)
+ except:
+ result = None
+
+ return result
+
+ def update_local_data(self, h5_path, h5_file=None):
+ if h5_path in self.local_datas and self.local_mtimes[h5_path] == get_mtime(h5_path):
+ self.local_data_changed = False
+ elif self.load_to_ram:
+ self.local_datas[h5_path] = self.extract_data(h5_path, h5_file=h5_file)
+ self.local_mtimes[h5_path] = get_mtime(h5_path)
+ self.local_data_changed = True
+
+ def update_local_datas(self):
+ for key in self.local_datas:
+ self.update_local_data(key)
+
+ def get_data(self, h5_path, h5_file=None):
+
+ if self.load_to_ram:
+
+ self.update_local_data(h5_path)
+
+ return self.local_datas[h5_path]
+ else:
+ return self.extract_data(h5_path, h5_file=h5_file)
+
+ def clean_memory(self, h5_paths):
+ for key in list(self.local_datas):
+ if key not in h5_paths.to_list():
+ del self.local_datas[key]
+ del self.local_mtimes[key]
+
+ self.local_data_changed = True
+ self.update_local_datas()
diff --git a/HelperClasses/Plotting/MainPlotPanel.py b/HelperClasses/Plotting/MainPlotPanel.py
new file mode 100644
index 0000000..874e5f3
--- /dev/null
+++ b/HelperClasses/Plotting/MainPlotPanel.py
@@ -0,0 +1,104 @@
+import pyqtgraph as pg
+from pyqtgraph.Qt import QtCore, QtGui
+import numpy as np
+
+from pyqtgraph.dockarea import *
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import *
+import lyse
+from .WidgetDataSelector import ShotSelector
+
+
+class MainPlotPanel(QtGui.QMainWindow):
+
+ def __init__(self, h5_paths, n_rows=3, **kwargs):
+
+ self.h5_paths = h5_paths
+
+ super().__init__(**kwargs)
+
+ pg.mkQApp()
+
+ self.n_rows = n_rows
+
+ self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False)
+ self.area = DockArea()
+
+ self.setCentralWidget(self.area)
+ self.resize(1000, 500)
+
+ self.dshotselector = Dock("Shot selector")
+ self.shotselector = ShotSelector()
+
+ self.dshotselector.addWidget(self.shotselector)
+ self.area.addDock(self.dshotselector, 'bottom')
+
+ # self.qpg_dock = Dock("Quick Plot Generator")
+ # self.qpg_dock.addWidget(QuickPlotGenerator(self))
+ # self.qpg_dock.setMinimumSize(self.qpg_dock.minimumSizeHint())
+ # self.area.addDock(self.qpg_dock)
+
+ self.show()
+
+ self.plots = {}
+
+ self.shotselector.valueChanged.connect(self.refresh)
+ self.shotselector.selectionChanged.connect(self.refresh)
+
+ self.df = lyse.data()
+
+ def add_plot_dock(self, plot_name, plot_widget, **kwargs):
+ if plot_name not in self.plots:
+ plot_widget.plot_name = plot_name
+
+ dock = Dock(plot_name, **kwargs)
+
+ dock.sigClosed.connect(self.remove_plot)
+ dock.addWidget(plot_widget)
+ if len(self.plots):
+ if len(self.plots) % self.n_rows == 2:
+ position = 'right'
+ self.area.addDock(dock, position)
+ if len(self.plots) % (self.n_rows * 2) in [0, 1]:
+ position = 'bottom'
+ self.area.addDock(dock, position, relativeTo=self.area.docks[list(self.plots.keys())[-1]])
+ if len(self.plots) % (self.n_rows * 2) in [3, 4]:
+ position = 'top'
+ self.area.addDock(dock, position, relativeTo=self.area.docks[list(self.plots.keys())[-1]])
+ print(len(self.plots) % (self.n_rows * 2), len(self.plots) % self.n_rows)
+ else:
+ self.area.addDock(dock, 'right')
+
+ self.plots[plot_name] = plot_widget
+ else:
+ print(f'Plot {plot_name} already exists. Please choose different name.')
+
+ def remove_plot(self, dock):
+ del self.plots[dock.title()]
+
+ def update_h5_paths(self, h5_paths):
+ self.h5_paths = h5_paths
+
+ self.shotselector.update_nshots(len(h5_paths))
+
+ for plot_name in self.plots:
+ self.plots[plot_name].data_extractor_manager.clean_memory(h5_paths)
+
+ def refresh(self, h5_path=None):
+ if len(self.h5_paths):
+
+ self.h5_paths_selected = self.h5_paths.iloc[self.shotselector.get_selected_indices()]
+
+ if h5_path == None:
+ i = self.shotselector.get_current_index()
+ h5_path = self.h5_paths.iloc[i]
+
+ for plot_name in self.plots:
+ self.plots[plot_name].data_extractor_manager.update_local_data(h5_path)
+
+ # self.data_extractor_manager.update_local_data(h5_path)
+
+ for plot_name, plot in self.plots.items():
+ plot.update_from_h5(h5_path)
+ else:
+ pass
\ No newline at end of file
diff --git a/HelperClasses/Plotting/PlottingData.py b/HelperClasses/Plotting/PlottingData.py
new file mode 100644
index 0000000..3fbe40c
--- /dev/null
+++ b/HelperClasses/Plotting/PlottingData.py
@@ -0,0 +1,172 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Mar 15 15:08:46 2021
+
+@author: Nick Sauerwein
+"""
+
+import lyse
+import numpy as np
+
+import os.path
+import time
+
+import h5py
+
+
+def get_mtime(filename):
+ return time.ctime(os.path.getmtime(filename))
+
+
+class DataExtractorManager:
+
+ def __init__(self):
+
+ self.data_extractors = {}
+
+ def update_local_data(self, h5_path):
+ with h5py.File(h5_path, 'r') as h5_file:
+ for key in self.data_extractors:
+ self.data_extractors[key].update_local_data(h5_path, h5_file=h5_file)
+
+ def clean_memory(self, h5_paths):
+ for key in self.data_extractors:
+ self.data_extractors[key].clean_memory(h5_paths)
+
+ def __getitem__(self, key):
+ return self.data_extractors[key]
+
+ def __setitem__(self, key, de):
+ self.data_extractors[key] = de
+
+
+class DataExtractor:
+ def __init__(self, load_to_ram=True):
+
+ self.load_to_ram = load_to_ram
+
+ self.local_datas = {}
+ self.local_mtimes = {}
+
+ if self.load_to_ram:
+ self.local_data_changed = True
+
+ def update_local_data(self, h5_path, h5_file=None):
+ if h5_path in self.local_datas and self.local_mtimes[h5_path] == get_mtime(h5_path):
+ self.local_data_changed = False
+ elif self.load_to_ram:
+ self.local_datas[h5_path] = self.extract_data(h5_path, h5_file=h5_file)
+ self.local_mtimes[h5_path] = get_mtime(h5_path)
+ self.local_data_changed = True
+
+ def update_local_datas(self):
+ for key in self.local_datas:
+ self.update_local_data(key)
+
+ def get_data(self, h5_path, h5_file=None):
+
+ if self.load_to_ram:
+
+ self.update_local_data(h5_path)
+
+ return self.local_datas[h5_path]
+ else:
+ return self.extract_data(h5_path, h5_file=h5_file)
+
+ def clean_memory(self, h5_paths):
+ for key in list(self.local_datas):
+ if key not in h5_paths.to_list():
+ del self.local_datas[key]
+ del self.local_mtimes[key]
+
+ self.local_data_changed = True
+ self.update_local_datas()
+
+
+class MultiDataExtractor(DataExtractor):
+ def __init__(self, **kwargs):
+
+ super().__init__(load_to_ram=False, **kwargs)
+
+ self.data_extractors = {}
+ self.children_changed = False
+
+ def extract_data(self, h5_path, h5_file=None):
+
+ data = {}
+
+ for key in self.data_extractors:
+ data[key] = self.data_extractors[key].get_data(h5_path, h5_file=h5_file)
+
+ self.children_changed = False
+
+ return [data]
+
+ def clean_children(self, keys):
+ self.children_changed = False
+ for key in list(self.data_extractors):
+ if key not in keys:
+ del self.data_extractors[key]
+ self.children_changed = True
+
+ def clean_memory(self, h5_paths):
+ for key in self.data_extractors:
+ self.data_extractors[key].clean_memory(h5_paths)
+
+ def __getitem__(self, key):
+ return self.data_extractors[key]
+
+ def __setitem__(self, key, de):
+ self.children_changed = True
+ self.data_extractors[key] = de
+
+ @property
+ def local_data_changed(self):
+ return any(
+ [self.data_extractors[key].local_data_changed for key in self.data_extractors]) or self.children_changed
+
+
+class EmptyDataExtractor(DataExtractor):
+
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+
+ def extract_data(self, h5_path, h5_file=None):
+ return []
+
+
+class ArrayDataExtractor(DataExtractor):
+
+ def __init__(self, idx, **kwargs):
+ super().__init__(**kwargs)
+ self.idx = idx
+
+ def extract_data(self, h5_path, h5_file=None):
+ run = lyse.Run(h5_path, no_write=True)
+ try:
+ res = run.get_result_array(self.idx[0], self.idx[1], h5_file=h5_file)
+ except:
+ res = None
+
+ return res
+
+
+class SingleDataExtractor(DataExtractor):
+
+ def __init__(self, idx, **kwargs):
+ super().__init__(**kwargs)
+ self.idx = idx
+ print(len(idx))
+
+ def extract_data(self, h5_path, h5_file=None):
+ run = lyse.Run(h5_path, no_write=True)
+ try:
+ if len(self.idx) == 1:
+ res = run.get_global(self.idx[0], h5_file=h5_file)
+ else:
+ res = run.get_result(self.idx[0], self.idx[1], h5_file=h5_file)
+ except:
+ raise
+ res = None
+
+ return res
diff --git a/HelperClasses/Plotting/PlottingPanel.py b/HelperClasses/Plotting/PlottingPanel.py
new file mode 100644
index 0000000..631ac19
--- /dev/null
+++ b/HelperClasses/Plotting/PlottingPanel.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Wed Mar 10 15:35:59 2021
+
+@author: nicks
+"""
+
+import pyqtgraph as pg
+from pyqtgraph.Qt import QtCore, QtGui
+import numpy as np
+
+from pyqtgraph.dockarea import *
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import *
+
+from PIL import ImageColor
+
+import lyse
+
+import collections
+
+import h5py
+from pandas.api.types import is_numeric_dtype
+from sortedcontainers import SortedSet
+from scipy.interpolate import griddata
+
+# from data_extractors import MultiDataExtractor, SingleDataExtractor, ArrayDataExtractor, EmptyDataExtractor, \
+# DataExtractorManager
+
+color_palette_html = ['#1f77b4',
+ '#ff7f0e',
+ '#2ca02c',
+ '#d62728',
+ '#9467bd',
+ '#8c564b',
+ '#e377c2',
+ '#7f7f7f',
+ '#bcbd22',
+ '#17becf']
+
+color_palette = [ImageColor.getcolor(color, "RGB") for color in color_palette_html]
+
diff --git a/HelperClasses/Plotting/Quick1DPlot.py b/HelperClasses/Plotting/Quick1DPlot.py
new file mode 100644
index 0000000..96b4a72
--- /dev/null
+++ b/HelperClasses/Plotting/Quick1DPlot.py
@@ -0,0 +1,128 @@
+class Quick1DPlot(QuickDataPlot):
+
+ def __init__(self, ap, **kwargs):
+
+ super().__init__(ap, **kwargs)
+ self.nplots = 0
+
+ self.table.setColumnCount(5)
+ self.table.setRowCount(1)
+ self.table.setColumnWidth(0, 200)
+ self.table.setColumnWidth(1, 200)
+ self.table.setColumnWidth(2, 30)
+ self.table.setColumnWidth(3, 30)
+ self.table.setColumnWidth(4, 40)
+ self.table.setHorizontalHeaderLabels(['xvalue', 'yvalue', 'color', 'show', 'scatter'])
+
+ self.combos = []
+ self.curves = []
+ self.show_cbs = []
+ self.scatter_cbs = []
+
+ self.mk_buttons()
+
+ def mk_buttons(self):
+ self.bt_add_plot = QPushButton('Add plot', self)
+ self.bt_add_plot.clicked.connect(self.add_plot)
+ self.table.setCellWidget(self.nplots, 0, self.bt_add_plot)
+
+ self.bt_update = QPushButton('Update', self)
+ self.bt_update.clicked.connect(self.update_from_h5)
+ self.table.setCellWidget(self.nplots, 1, self.bt_update)
+
+ def add_plot(self):
+ self.nplots += 1
+ self.table.setRowCount(self.nplots + 1)
+ combox = NumericDataCombo(self.ap.df)
+ comboy = NumericDataCombo(self.ap.df)
+
+ self.combos += [[combox, comboy]]
+ self.table.setCellWidget(self.nplots - 1, 0, combox)
+ self.table.setCellWidget(self.nplots - 1, 1, comboy)
+
+ self.table.setItem(self.nplots - 1, 2, QtGui.QTableWidgetItem())
+ self.table.item(self.nplots - 1, 2).setBackground(QtGui.QColor(*color_palette[self.nplots - 1]))
+
+ # self.table.setFixedSize(self.table.sizeHint())
+
+ self.curves += [
+ self.plot.plot(pen=pg.mkPen(color=color_palette[self.nplots - 1], width=1.5), symbol='x', symbolPen=None,
+ symbolBrush=None)]
+
+ self.show_cbs += [QCheckBox()]
+ self.show_cbs[self.nplots - 1].setChecked(True)
+ self.table.setCellWidget(self.nplots - 1, 3, self.show_cbs[self.nplots - 1])
+ self.show_cbs[self.nplots - 1].stateChanged.connect(self.update_shows)
+
+ self.scatter_cbs += [QCheckBox()]
+ self.scatter_cbs[self.nplots - 1].setChecked(False)
+ self.table.setCellWidget(self.nplots - 1, 4, self.scatter_cbs[self.nplots - 1])
+ self.scatter_cbs[self.nplots - 1].stateChanged.connect(self.update_scatters)
+
+ self.mk_buttons()
+
+ def update_shows(self):
+ for k, cb in enumerate(self.show_cbs):
+ if cb.isChecked():
+ self.curves[k].show()
+ else:
+ self.curves[k].hide()
+
+ def update_scatters(self):
+ for k, cb in enumerate(self.scatter_cbs):
+ pen = pg.mkPen(color=color_palette[k], width=1.5)
+ brush = pg.mkBrush(color=color_palette[k])
+ if cb.isChecked():
+ self.curves[k].setSymbolBrush(brush)
+ self.curves[k].setPen(None)
+ else:
+ self.curves[k].setSymbolBrush(None)
+ self.curves[k].setPen(pen)
+
+ def update_data_extractor(self):
+
+ idxxs = [combo[0].get_idx() for combo in self.combos]
+ idxys = [combo[1].get_idx() for combo in self.combos]
+
+ for idxx in idxxs:
+ if idxx not in self.data_extractor.data_extractors:
+ if idxx[0] == 'shot number':
+ self.data_extractor[idxx] = EmptyDataExtractor()
+ else:
+ self.data_extractor[idxx] = SingleDataExtractor(idxx)
+
+ for idxy in idxys:
+ if idxy not in self.data_extractor.data_extractors:
+ if idxy[0] == 'shot number':
+ self.data_extractor[idxy] = EmptyDataExtractor()
+ else:
+ self.data_extractor[idxy] = SingleDataExtractor(idxy)
+
+ self.data_extractor.clean_children(idxxs + idxys)
+ self.data_extractor.clean_memory(self.ap.h5_paths)
+
+ def update(self, data=None):
+
+ Xs = np.zeros((self.nplots, len(self.ap.h5_paths_selected)))
+ Ys = np.zeros((self.nplots, len(self.ap.h5_paths_selected)))
+
+ for i, h5_path in enumerate(self.ap.h5_paths_selected):
+
+ data = self.data_extractor.get_data(h5_path)[0]
+
+ for k in range(self.nplots):
+ idxx = self.combos[k][0].get_idx()
+ idxy = self.combos[k][1].get_idx()
+
+ if idxx[0] == 'shot number':
+ Xs[k, i] = self.ap.shotselector.get_selected_indices()[0][i]
+ else:
+ Xs[k, i] = data[idxx]
+
+ if idxy[0] == 'shot number':
+ Ys[k, i] = self.ap.shotselector.get_selected_indices()[0][i]
+ else:
+ Ys[k, i] = data[idxy]
+
+ for k in range(self.nplots):
+ self.curves[k].setData(Xs[k], Ys[k])
\ No newline at end of file
diff --git a/HelperClasses/Plotting/Unknown.py b/HelperClasses/Plotting/Unknown.py
new file mode 100644
index 0000000..4adb2e2
--- /dev/null
+++ b/HelperClasses/Plotting/Unknown.py
@@ -0,0 +1,188 @@
+class DataPlot(QSplitter):
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+
+ self.setOrientation(Qt.Vertical)
+
+ self.plots = pg.GraphicsLayoutWidget()
+
+ self.addWidget(self.plots)
+
+ self.bottom = QSplitter()
+ self.bottom.setOrientation(Qt.Horizontal)
+
+ self.addWidget(self.bottom)
+
+ self.h5_path_shown = None
+
+ def update_from_h5(self, h5_path):
+ if self.h5_path_shown != h5_path or self.data_extractor.local_data_changed:
+ self.h5_path_shown = h5_path
+ self.update(*self.data_extractor.extract_data(h5_path))
+
+
+class QuickDataPlot(DataPlot):
+ def __init__(self, ap, **kwargs):
+
+ super().__init__(**kwargs)
+
+ self.plot = self.plots.addPlot()
+
+ for key in self.plot.axes:
+ ax = self.plot.getAxis(key)
+ # Fix Z value making the grid on top of the image
+ ax.setZValue(1)
+ self.ap = ap
+ self.h5_paths_shown = []
+
+ self.table = QTableWidget()
+ self.table.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
+
+ self.bottom.addWidget(self.table)
+
+ self.plot_setting = PlotSettings(self.plot)
+
+ self.bottom.addWidget(self.plot_setting)
+
+ def update_from_h5(self, h5_path=None):
+ self.update_data_extractor()
+ if self.data_extractor.local_data_changed or collections.Counter(self.h5_paths_shown) != collections.Counter(
+ self.ap.h5_paths_selected):
+ self.h5_paths_shown = self.ap.h5_paths_selected
+ self.update()
+
+
+class ExtendedCombo(QComboBox):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.setFocusPolicy(Qt.StrongFocus)
+ self.setEditable(True)
+ self.completer = QCompleter(self)
+
+ # always show all completions
+ self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
+ self.pFilterModel = QSortFilterProxyModel(self)
+ self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
+
+ self.completer.setPopup(self.view())
+
+ self.setCompleter(self.completer)
+
+ self.lineEdit().textEdited[str].connect(self.pFilterModel.setFilterFixedString)
+ self.completer.activated.connect(self.setTextIfCompleterIsClicked)
+
+ def setModel(self, model):
+ super().setModel(model)
+ self.pFilterModel.setSourceModel(model)
+ self.completer.setModel(self.pFilterModel)
+
+ def setModelColumn(self, column):
+ self.completer.setCompletionColumn(column)
+ self.pFilterModel.setFilterKeyColumn(column)
+ super().setModelColumn(column)
+
+ def view(self):
+ return self.completer.popup()
+
+ def index(self):
+ return self.currentIndex()
+
+ def setTextIfCompleterIsClicked(self, text):
+ if text:
+ index = self.findText(text)
+ self.setCurrentIndex(index)
+
+
+class PlotSettings(QTableWidget):
+ def __init__(self, plot, **kwargs):
+ super().__init__(**kwargs)
+
+ self.plot = plot
+ self.setColumnCount(3)
+ self.setColumnWidth(0, 100)
+ self.setColumnWidth(1, 100)
+
+ self.setHorizontalHeaderLabels(['parameter', 'setting', 'units'])
+
+ self.setRowCount(4)
+
+ # title
+ i = 0
+ self.le_title = QLineEdit()
+
+ self.setCellWidget(i, 0, QLabel('title'))
+ self.setCellWidget(i, 1, self.le_title)
+
+ self.le_title.textChanged[str].connect(self.set_title)
+
+ # xlabel
+ i += 1
+ self.le_xlabel = QLineEdit()
+ self.le_xlabel_unit = QLineEdit()
+
+ self.setCellWidget(i, 0, QLabel('xlabel'))
+ self.setCellWidget(i, 1, self.le_xlabel)
+ self.setCellWidget(i, 2, self.le_xlabel_unit)
+
+ self.le_xlabel.textChanged[str].connect(self.set_xlabel)
+ self.le_xlabel_unit.textChanged[str].connect(self.set_xlabel)
+
+ # ylabel
+ i += 1
+ self.le_ylabel = QLineEdit()
+ self.le_ylabel_unit = QLineEdit()
+
+ self.setCellWidget(i, 0, QLabel('ylabel'))
+ self.setCellWidget(i, 1, self.le_ylabel)
+ self.setCellWidget(i, 2, self.le_ylabel_unit)
+
+ self.le_ylabel.textChanged[str].connect(self.set_ylabel)
+ self.le_ylabel_unit.textChanged[str].connect(self.set_ylabel)
+
+ # grid
+ i += 1
+ self.cb_grid = QCheckBox()
+ self.setCellWidget(i, 0, QLabel('grid'))
+ self.setCellWidget(i, 1, self.cb_grid)
+
+ self.cb_grid.stateChanged.connect(self.set_grid)
+
+ def set_title(self):
+ self.plot.setTitle(self.le_title.text())
+
+ def set_xlabel(self):
+ self.plot.setLabel('bottom', self.le_xlabel.text(), units=self.le_xlabel_unit.text())
+
+ def set_ylabel(self):
+ self.plot.setLabel('left', self.le_ylabel.text(), units=self.le_ylabel_unit.text())
+
+ def set_grid(self):
+ self.plot.showGrid(x=self.cb_grid.isChecked(), y=self.cb_grid.isChecked(), alpha=0.3)
+
+
+class NumericDataCombo(ExtendedCombo):
+
+ def __init__(self, df, **kwargs):
+
+ super().__init__(**kwargs)
+ self.update_model(df)
+
+ def update_model(self, df):
+ model = QtGui.QStandardItemModel()
+
+ item = QtGui.QStandardItem('shot number')
+ model.setItem(0, 0, item)
+
+ i = 1
+ for midx in df.columns:
+ if is_numeric_dtype(df.dtypes[midx]):
+ column_name = ','.join([x for x in midx if x])
+ item = QtGui.QStandardItem(column_name)
+ model.setItem(i, 0, item)
+ i += 1
+
+ self.setModel(model)
+
+ def get_idx(self):
+ return tuple(str(self.currentText()).split(','))
\ No newline at end of file
diff --git a/HelperClasses/Plotting/WidgetDataSelector.py b/HelperClasses/Plotting/WidgetDataSelector.py
new file mode 100644
index 0000000..2b65830
--- /dev/null
+++ b/HelperClasses/Plotting/WidgetDataSelector.py
@@ -0,0 +1,131 @@
+import pyqtgraph as pg
+from pyqtgraph.Qt import QtGui
+import numpy as np
+
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import *
+
+from sortedcontainers import SortedSet
+
+
+# ShotSelector class for choosing which the hdf5 files are import for plotting
+class ShotSelector(pg.LayoutWidget):
+ valueChanged = pyqtSignal()
+ selectionChanged = pyqtSignal()
+
+ def __init__(self, **kwargs):
+
+ super().__init__(**kwargs)
+
+ self.nshots = 1
+
+ self.setFixedHeight(100)
+
+ self.current_idx_le = QLineEdit(self)
+
+ self.current_idx_le.setMaximumWidth(30)
+ self.current_idx_le.setValidator(QtGui.QIntValidator())
+
+ self.current_idx_le.setText(str(-1))
+
+ self.addWidget(self.current_idx_le)
+
+ self.slider = QSlider(self)
+
+ self.slider.setPageStep(1)
+ self.slider.setOrientation(Qt.Horizontal)
+
+ self.addWidget(self.slider, colspan=2)
+
+ self.nextRow()
+
+ self.addWidget(QLabel('index selector'))
+
+ self.idx_select_le = QLineEdit(self)
+ self.idx_select_le.setText(':')
+ self.addWidget(self.idx_select_le)
+
+ self.warning = QLabel()
+ self.warning.setMargin(5)
+ self.update_warning()
+ self.addWidget(self.warning)
+
+ self.update_nshots(self.nshots)
+
+ self.idx_select_le.editingFinished.connect(self.update_selection)
+ self.current_idx_le.editingFinished.connect(self.setSliderValue)
+ self.slider.valueChanged.connect(self.setLabelValue)
+
+ def update_nshots(self, nshots):
+ self.nshots = nshots
+ self.idx = np.arange(self.nshots)
+ self.slider.setRange(0, self.nshots - 1)
+
+ self.update_selection()
+ self.setSliderValue()
+
+ def update_warning(self, warning=''):
+ if warning == '':
+ self.warning.setStyleSheet("background-color: lightgreen")
+ warning = 'all good'
+ else:
+ self.warning.setStyleSheet("background-color: red")
+
+ self.warning.setText(warning)
+
+ def update_selection(self):
+ self.update_warning()
+
+ slice_text = self.idx_select_le.text()
+ slices = slice_text.split(',')
+
+ self.idx_selected = SortedSet([])
+ for s in slices:
+ try:
+ scope = locals()
+
+ select = eval('self.idx[' + s + ']', scope)
+
+ if isinstance(select, np.ndarray):
+ for idx in select:
+ self.idx_selected.add(idx)
+ else:
+ self.idx_selected.add(select)
+
+ except:
+ self.update_warning('problem in selected indeces')
+ return 0
+
+ self.slider.setRange(0, len(self.idx_selected) - 1)
+
+ if int(self.current_idx_le.text()) % self.nshots not in self.idx_selected:
+ self.current_idx_le.setText(self.idx_selected[-1])
+
+ self.update_warning('last index not in selection
-> setting last selected')
+
+ self.selectionChanged.emit()
+
+ def setLabelValue(self, value):
+ newval = self.idx_selected[value]
+
+ if newval != self.get_current_index():
+ self.current_idx_le.setText(str(newval))
+
+ self.valueChanged.emit()
+
+ def setSliderValue(self):
+ self.update_warning()
+
+ value = int(self.current_idx_le.text())
+
+ try:
+ value_sl = self.idx_selected.index(value % len(self.idx))
+ self.slider.setValue(value_sl)
+ except ValueError:
+ self.update_warning('set index not in selection
ignore')
+
+ def get_current_index(self):
+ return int(self.current_idx_le.text()) % self.nshots
+
+ def get_selected_indices(self):
+ return (np.array(self.idx_selected),)
\ No newline at end of file
diff --git a/HelperClasses/Plotting/WidgetFakeColorPlot.py b/HelperClasses/Plotting/WidgetFakeColorPlot.py
new file mode 100644
index 0000000..cc7b4a7
--- /dev/null
+++ b/HelperClasses/Plotting/WidgetFakeColorPlot.py
@@ -0,0 +1,139 @@
+import pyqtgraph as pg
+from pyqtgraph import Qt
+from pyqtgraph.Qt import QtCore, QtGui
+import numpy as np
+
+from pyqtgraph.dockarea import *
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import *
+
+from .DataExtractorLyse import DataExtractorManager
+# import DataExtractorLyse
+from .WidgetPlot import DataPlot
+# import WidgetPlot
+
+from PIL import ImageColor
+
+color_palette_html = ['#1f77b4',
+ '#ff7f0e',
+ '#2ca02c',
+ '#d62728',
+ '#9467bd',
+ '#8c564b',
+ '#e377c2',
+ '#7f7f7f',
+ '#bcbd22',
+ '#17becf']
+
+color_palette = [ImageColor.getcolor(color, "RGB") for color in color_palette_html]
+
+
+class FakeColorPlot(DataPlot):
+ def __init__(self, title, data_path=None, **kwargs):
+
+ super().__init__(title, **kwargs)
+
+ # set data extractor
+ self.data_extractor_manager = DataExtractorManager(data_path)
+ self.data_extractor_manager.add_data_extractor('data_img', 'array', data_path)
+ self.data_extractor_manager.add_data_extractor('x', 'array', (data_path[0], 'x'))
+ self.data_extractor_manager.add_data_extractor('y', 'array', (data_path[0], 'y'))
+ self.data_extractor_manager.add_data_extractor('warning', 'single', (data_path[0], 'warning'))
+
+ self.setMinimumHeight(550)
+ self.setMinimumWidth(550)
+
+ # Add imaging
+ self.img = pg.ImageItem()
+ self.aximg = self.plots.addPlot(title="")
+ self.aximg.addItem(self.img)
+
+ # Isocurve draplotsg
+ self.iso = pg.IsocurveItem(level=1000, pen=color_palette[2])
+ self.iso.setParentItem(self.img)
+ self.iso.setZValue(5)
+
+ # Contrast/color control
+ self.hist = pg.HistogramLUTItem()
+ self.hist.setImageItem(self.img)
+ self.plots.addItem(self.hist)
+
+ # Draggable line for setting isocurve level
+ self.isoLine = pg.InfiniteLine(angle=0, movable=True, pen=color_palette[2])
+ self.hist.vb.addItem(self.isoLine)
+ self.hist.vb.setMouseEnabled(y=False) # makes user interaction a little easier
+ self.isoLine.setValue(1000)
+ self.isoLine.setZValue(1000) # bring iso line above contrast controls
+ self.isoLine.sigDragged.connect(self.updateIsocurve)
+
+ # Monkey-patch the image to use our custom hover function.
+ # This is generally discouraged (you should subclass ImageItem instead),
+ # but it works for a very simple use like this.
+ self.img.hoverEvent = self.imageHoverEvent
+
+ self.plots.nextRow()
+ self.plots.nextColumn()
+ self.img.translate(-0.5, -0.5)
+
+ self.scalex = 1
+ self.scaley = 1
+
+ self.cx = 0
+ self.cy = 0
+
+ self.data_image = None
+
+ def update(self, h5_path):
+
+ data = self.data_extractor_manager.get_data(h5_path)
+ data_img = data['data_img']
+ x = data['x']
+ y = data['y']
+ if x is None:
+ x = np.linspace(0, data_img.shape[0], data_img.shape[0])
+ if y is None:
+ y = np.linspace(0, data_img.shape[1], data_img.shape[1])
+ warning = data['warning']
+
+ # update plots
+ self.img.setImage(data_img.T)
+ self.iso.setData(data_img.T)
+
+ self.data_img = data_img
+
+ # set position and scale of image
+ newscalex = x[1] - x[0]
+ newscaley = y[1] - y[0]
+
+ transx = (x[0] - (self.cx - 0.5 * self.scalex)) / newscalex - 0.5
+ transy = (y[0] - (self.cy - 0.5 * self.scaley)) / newscaley - 0.5
+
+ self.img.scale(newscalex / self.scalex, newscaley / self.scaley)
+ self.img.translate(transx, transy)
+
+ self.scalex = newscalex
+ self.scaley = newscaley
+
+ self.cx = x[0]
+ self.cy = y[0]
+
+ # update table and warning
+ self.update_warning(warning)
+
+ def updateIsocurve(self):
+ self.iso.setLevel(self.isoLine.value())
+
+ def imageHoverEvent(self, event):
+ """Show the position, pixel, and value under the mouse cursor.
+ """
+ if event.isExit():
+ self.aximg.setTitle("")
+ return
+ pos = event.pos()
+ i, j = pos.y(), pos.x()
+ i = int(np.clip(i, 0, self.data_img.shape[0] - 1))
+ j = int(np.clip(j, 0, self.data_img.shape[1] - 1))
+ val = self.data_img[i, j]
+ ppos = self.img.mapToParent(pos)
+ x, y = ppos.x(), ppos.y()
+ self.aximg.setTitle("pos: (%0.1f, %0.1f) value: %g" % (x, y, val))
\ No newline at end of file
diff --git a/HelperClasses/Plotting/WidgetPlot.py b/HelperClasses/Plotting/WidgetPlot.py
new file mode 100644
index 0000000..49a073e
--- /dev/null
+++ b/HelperClasses/Plotting/WidgetPlot.py
@@ -0,0 +1,63 @@
+import pyqtgraph as pg
+from pyqtgraph import Qt
+from pyqtgraph.Qt import QtCore, QtGui
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import *
+
+from .DataExtractorLyse import DataExtractorManager
+
+
+class DataPlot(QSplitter):
+ def __init__(self, title, **kwargs):
+ super().__init__(**kwargs)
+
+ # Orientation of plot and title (and other information)
+ self.setOrientation(Qt.Vertical)
+
+ # Add plot
+ self.plots = pg.GraphicsLayoutWidget()
+ self.addWidget(self.plots)
+
+ # Claim bottom part (title)
+ self.bottom = QSplitter()
+ self.bottom.setOrientation(Qt.Horizontal)
+ self.addWidget(self.bottom)
+
+ # Add title
+ self.title = QtGui.QLabel()
+ self.title.setAlignment(QtCore.Qt.AlignCenter)
+ self.title.setText('
' + title + ' <\h2>')
+ self.desciption = pg.LayoutWidget()
+ self.desciption.addWidget(self.title)
+
+ # Add warning information
+ self.warning = QtGui.QLabel()
+ self.warning.setAlignment(QtCore.Qt.AlignCenter)
+ self.desciption.nextRow()
+ self.desciption.addWidget(self.warning)
+ self.bottom.addWidget(self.desciption)
+
+ # Add a table for fitting information
+ self.table = pg.TableWidget()
+ self.bottom.addWidget(self.table)
+
+ # Current hdf5 file path
+ self.h5_path_shown = None
+
+ # data extractor
+ self.data_extractor_manager = DataExtractorManager()
+
+ def update_from_h5(self, h5_path):
+ if self.h5_path_shown != h5_path or self.data_extractor_manager.local_data_changed:
+ self.h5_path_shown = h5_path
+ self.update(h5_path)
+
+ def update_warning(self, warning):
+ if warning == '':
+ self.warning.setStyleSheet("background-color: lightgreen")
+ warning = 'all good'
+ else:
+ self.warning.setStyleSheet("background-color: red")
+
+ self.warning.setText(warning)
+
diff --git a/HelperClasses/Plotting/WidgetQuick2DPlot.py b/HelperClasses/Plotting/WidgetQuick2DPlot.py
new file mode 100644
index 0000000..174146f
--- /dev/null
+++ b/HelperClasses/Plotting/WidgetQuick2DPlot.py
@@ -0,0 +1,166 @@
+class Quick2DPlot(QuickDataPlot):
+
+ def __init__(self, *args, **kwargs):
+
+ super().__init__(*args, **kwargs)
+
+ self.img = pg.ImageItem()
+ self.plot.addItem(self.img)
+
+ # Isocurve draplotsg
+ self.iso = pg.IsocurveItem(level=1000, pen=color_palette[2])
+ self.iso.setParentItem(self.img)
+ self.iso.setZValue(5)
+
+ # Contrast/color control
+ self.hist = pg.HistogramLUTItem()
+ self.hist.setImageItem(self.img)
+ self.plots.addItem(self.hist)
+
+ # Draggable line for setting isocurve level
+ self.isoLine = pg.InfiniteLine(angle=0, movable=True, pen=color_palette[2])
+ self.hist.vb.addItem(self.isoLine)
+ self.hist.vb.setMouseEnabled(y=False) # makes user interaction a little easier
+ self.isoLine.setValue(1000)
+ self.isoLine.setZValue(1000) # bring iso line above contrast controls
+ self.isoLine.sigDragged.connect(self.updateIsocurve)
+
+ # Monkey-patch the image to use our custom hover function.
+ # This is generally discouraged (you should subclass ImageItem instead),
+ # but it works for a very simple use like this.
+ self.img.hoverEvent = self.imageHoverEvent
+
+ self.img.translate(-0.5, -0.5)
+
+ self.scalex = 1
+ self.scaley = 1
+
+ self.cx = 0
+ self.cy = 0
+
+ self.nplots = 0
+
+ self.table.setColumnCount(3)
+ self.table.setRowCount(2)
+ self.table.setColumnWidth(0, 200)
+ self.table.setColumnWidth(1, 150)
+ self.table.setColumnWidth(2, 150)
+ self.table.setHorizontalHeaderLabels(['xvalue', 'yarray', 'zarray'])
+
+ self.combox = NumericDataCombo(self.ap.df)
+ self.comboy = NumericDataCombo(self.ap.df)
+ self.comboz = NumericDataCombo(self.ap.df)
+
+ self.table.setCellWidget(0, 0, self.combox)
+ self.table.setCellWidget(0, 1, self.comboy)
+ self.table.setCellWidget(0, 2, self.comboz)
+
+ self.mk_buttons()
+
+ def mk_buttons(self):
+ self.bt_update = QPushButton('Update', self)
+ self.bt_update.clicked.connect(self.update_from_h5)
+ self.table.setCellWidget(1, 1, self.bt_update)
+
+ def update_data_extractor(self):
+
+ idxx = self.combox.get_idx()
+ idxy = self.comboy.get_idx()
+ idxz = self.comboz.get_idx()
+
+ if idxx not in self.data_extractor.data_extractors:
+ if idxx[0] == 'shot number':
+ self.data_extractor[idxx] = EmptyDataExtractor()
+ else:
+ self.data_extractor[idxx] = SingleDataExtractor(idxx)
+
+ if idxy not in self.data_extractor.data_extractors:
+ if idxy[0] == 'shot number':
+ self.data_extractor[idxy] = EmptyDataExtractor()
+ else:
+ self.data_extractor[idxy] = SingleDataExtractor(idxy)
+
+ if idxz not in self.data_extractor.data_extractors:
+ if idxz[0] == 'shot number':
+ self.data_extractor[idxz] = EmptyDataExtractor()
+ else:
+ self.data_extractor[idxz] = SingleDataExtractor(idxz)
+
+ self.data_extractor.clean_children([idxx, idxy, idxz])
+ self.data_extractor.clean_memory(self.ap.h5_paths)
+
+ def update(self, murks=None):
+
+ xs = np.array([])
+ ys = np.array([])
+ zs = np.array([])
+
+ idxx = self.combox.get_idx()
+ idxy = self.comboy.get_idx()
+ idxz = self.comboz.get_idx()
+
+ for i, h5_path in enumerate(self.ap.h5_paths_selected):
+
+ data = self.data_extractor.get_data(h5_path)[0]
+ if idxx[0] == 'shot number':
+ xs = np.append(xs, self.ap.shotselector.get_selected_indices()[0][i])
+ else:
+ xs = np.append(xs, data[idxx])
+
+ if idxy[0] == 'shot number':
+ ys = np.append(ys, self.ap.shotselector.get_selected_indices()[0][i])
+ else:
+ ys = np.append(ys, data[idxy])
+
+ if idxz[0] == 'shot number':
+ zs = np.append(zs, self.ap.shotselector.get_selected_indices()[0][i])
+ else:
+ zs = np.append(zs, data[idxz])
+
+ # here we don't want to assume that the data is on a grid
+ # this can happen if the parameters where changed between two sweeps
+
+ xi = np.linspace(xs.min(), xs.max(), 200)
+ yi = np.linspace(ys.min(), ys.max(), 200)
+
+ Xi, Yi = np.meshgrid(xi, yi)
+
+ Zi = griddata((xs, ys), zs, (Xi, Yi), 'nearest')
+
+ self.img.setImage(Zi.T)
+ self.iso.setData(Zi.T)
+ self.data_img = Zi
+
+ # set position and scale of image
+ newscalex = xi[1] - xi[0]
+ newscaley = yi[1] - yi[0]
+
+ transx = (xi[0] - (self.cx - 0.5 * self.scalex)) / newscalex - 0.5
+ transy = (yi[0] - (self.cy - 0.5 * self.scaley)) / newscaley - 0.5
+
+ self.img.scale(newscalex / self.scalex, newscaley / self.scaley)
+ self.img.translate(transx, transy)
+
+ self.scalex = newscalex
+ self.scaley = newscaley
+
+ self.cx = xi[0]
+ self.cy = yi[0]
+
+ def updateIsocurve(self):
+ self.iso.setLevel(self.isoLine.value())
+
+ def imageHoverEvent(self, event):
+ """Show the position, pixel, and value under the mouse cursor.
+ """
+ if event.isExit():
+ self.plot.setTitle("")
+ return
+ pos = event.pos()
+ i, j = pos.y(), pos.x()
+ i = int(np.clip(i, 0, self.data_img.shape[0] - 1))
+ j = int(np.clip(j, 0, self.data_img.shape[1] - 1))
+ val = self.data_img[i, j]
+ ppos = self.img.mapToParent(pos)
+ x, y = ppos.x(), ppos.y()
+ self.plot.setTitle("pos: (%0.1f, %0.1f) value: %g" % (x, y, val))
\ No newline at end of file
diff --git a/HelperClasses/Plotting/WidgetQuickPlotGenerator.py b/HelperClasses/Plotting/WidgetQuickPlotGenerator.py
new file mode 100644
index 0000000..9d661e7
--- /dev/null
+++ b/HelperClasses/Plotting/WidgetQuickPlotGenerator.py
@@ -0,0 +1,55 @@
+import pyqtgraph as pg
+from PyQt5.QtWidgets import *
+
+
+class QuickPlotGenerator(pg.LayoutWidget):
+
+ def __init__(self, ap, **kwargs):
+ super().__init__(**kwargs)
+
+ self.ap = ap
+
+ self.title = QLabel(' Quick Plot Generator <\h2>')
+
+ self.addWidget(self.title, colspan=2)
+
+ self.nextRow()
+
+ self.newlayout = pg.LayoutWidget()
+
+ self.newlayout.addWidget(QLabel('Make new plot'))
+ self.newlayout.nextRow()
+ self.newlayout.addWidget(QLabel('Title: '))
+
+ self.title_le = QLineEdit('murks')
+ self.newlayout.addWidget(self.title_le)
+
+ self.newlayout.nextRow()
+ self.bt1d = QPushButton('make 1D plot', self)
+ self.newlayout.addWidget(self.bt1d)
+
+ self.bt1d.clicked.connect(self.mk1dplot)
+
+ self.btwaterfall = QPushButton('make waterfall plot', self)
+ self.newlayout.addWidget(self.btwaterfall)
+
+ self.btwaterfall.clicked.connect(self.mkwaterfallplot)
+
+ self.bt2d = QPushButton('make 2d plot', self)
+ self.newlayout.addWidget(self.bt2d)
+
+ self.bt2d.clicked.connect(self.mk2dplot)
+
+ self.addWidget(self.newlayout)
+
+ def mk1dplot(self):
+ title = self.title_le.text()
+ self.ap.add_plot_dock(title, Quick1DPlot(self.ap), MultiDataExtractor(), closable=True)
+
+ def mkwaterfallplot(self):
+ title = self.title_le.text()
+ self.ap.add_plot_dock(title, QuickWaterfallPlot(self.ap), MultiDataExtractor(), closable=True)
+
+ def mk2dplot(self):
+ title = self.title_le.text()
+ self.ap.add_plot_dock(title, Quick2DPlot(self.ap), MultiDataExtractor(), closable=True)
\ No newline at end of file
diff --git a/HelperClasses/Plotting/WidgetQuickWaterFlowPlot.py b/HelperClasses/Plotting/WidgetQuickWaterFlowPlot.py
new file mode 100644
index 0000000..2b5f03b
--- /dev/null
+++ b/HelperClasses/Plotting/WidgetQuickWaterFlowPlot.py
@@ -0,0 +1,197 @@
+class ArrayDataCombo(ExtendedCombo):
+
+ def __init__(self, h5_paths, **kwargs):
+
+ super().__init__(**kwargs)
+ self.update_model(h5_paths)
+
+ def update_model(self, h5_paths):
+ results_array_labels = set([])
+ for h5_path in h5_paths:
+ with h5py.File(h5_path, 'r') as h5_file:
+ analysis_names = h5_file['results'].keys()
+
+ for analysis_name in analysis_names:
+ for key in h5_file['results'][analysis_name].keys():
+ results_array_labels.add(analysis_name + ',' + key)
+
+ model = QtGui.QStandardItemModel()
+
+ item = QtGui.QStandardItem('shot number')
+ model.setItem(0, 0, item)
+
+ for i, idx in enumerate(results_array_labels):
+ item = QtGui.QStandardItem(idx)
+ model.setItem(i, 0, item)
+
+ self.setModel(model)
+
+ def get_idx(self):
+ return tuple(str(self.currentText()).split(','))
+
+
+class QuickWaterfallPlot(QuickDataPlot):
+
+ def __init__(self, *args, **kwargs):
+
+ super().__init__(*args, **kwargs)
+
+ self.img = pg.ImageItem()
+ self.plot.addItem(self.img)
+
+ # Isocurve draplotsg
+ self.iso = pg.IsocurveItem(level=1000, pen=color_palette[2])
+ self.iso.setParentItem(self.img)
+ self.iso.setZValue(5)
+
+ # Contrast/color control
+ self.hist = pg.HistogramLUTItem()
+ self.hist.setImageItem(self.img)
+ self.plots.addItem(self.hist)
+
+ # Draggable line for setting isocurve level
+ self.isoLine = pg.InfiniteLine(angle=0, movable=True, pen=color_palette[2])
+ self.hist.vb.addItem(self.isoLine)
+ self.hist.vb.setMouseEnabled(y=False) # makes user interaction a little easier
+ self.isoLine.setValue(1000)
+ self.isoLine.setZValue(1000) # bring iso line above contrast controls
+ self.isoLine.sigDragged.connect(self.updateIsocurve)
+
+ # Monkey-patch the image to use our custom hover function.
+ # This is generally discouraged (you should subclass ImageItem instead),
+ # but it works for a very simple use like this.
+ self.img.hoverEvent = self.imageHoverEvent
+
+ self.img.translate(-0.5, -0.5)
+
+ self.scalex = 1
+ self.scaley = 1
+
+ self.cx = 0
+ self.cy = 0
+
+ self.nplots = 0
+
+ self.table.setColumnCount(3)
+ self.table.setRowCount(2)
+ self.table.setColumnWidth(0, 200)
+ self.table.setColumnWidth(1, 150)
+ self.table.setColumnWidth(2, 150)
+ self.table.setHorizontalHeaderLabels(['xvalue', 'yarray', 'zarray'])
+
+ self.combox = NumericDataCombo(self.ap.df)
+ self.comboy = ArrayDataCombo(self.ap.h5_paths)
+ self.comboz = ArrayDataCombo(self.ap.h5_paths)
+
+ self.table.setCellWidget(0, 0, self.combox)
+ self.table.setCellWidget(0, 1, self.comboy)
+ self.table.setCellWidget(0, 2, self.comboz)
+
+ self.mk_buttons()
+
+ def mk_buttons(self):
+ self.bt_update = QPushButton('Update', self)
+ self.bt_update.clicked.connect(self.update_from_h5)
+ self.table.setCellWidget(1, 1, self.bt_update)
+
+ def update_data_extractor(self):
+
+ idxx = self.combox.get_idx()
+ idxy = self.comboy.get_idx()
+ idxz = self.comboz.get_idx()
+
+ if idxx not in self.data_extractor.data_extractors:
+ if idxx[0] == 'shot number':
+ self.data_extractor[idxx] = EmptyDataExtractor()
+ else:
+ self.data_extractor[idxx] = SingleDataExtractor(idxx)
+
+ if idxy not in self.data_extractor.data_extractors:
+ self.data_extractor[idxy] = ArrayDataExtractor(idxy)
+
+ if idxz not in self.data_extractor.data_extractors:
+ self.data_extractor[idxz] = ArrayDataExtractor(idxz)
+
+ self.data_extractor.clean_children([idxx, idxy, idxz])
+ self.data_extractor.clean_memory(self.ap.h5_paths)
+
+ def update(self, data=None):
+ xs = np.array([])
+ ys = np.array([])
+ zs = np.array([])
+
+ idxx = self.combox.get_idx()
+ idxy = self.comboy.get_idx()
+ idxz = self.comboz.get_idx()
+
+ for i, h5_path in enumerate(self.ap.h5_paths_selected):
+
+ data = self.data_extractor.get_data(h5_path)[0]
+
+ if idxx[0] == 'shot number':
+ x = self.ap.shotselector.get_selected_indices()[0][i]
+ else:
+ x = data[idxx]
+
+ y = data[idxy]
+ z = data[idxz]
+
+ if y is not None and z is not None:
+ xs = np.append(xs, x * np.ones_like(y))
+ ys = np.append(ys, y)
+ zs = np.append(zs, z)
+ elif y is not None:
+ xs = np.append(xs, x * np.ones_like(y))
+ ys = np.append(ys, y)
+ zs = np.append(zs, y * np.nan)
+ elif z is not None:
+ xs = np.append(xs, x * np.ones_like(z))
+ ys = np.append(ys, z * np.nan)
+ zs = np.append(zs, z)
+
+ # here we don't want to assume that the data is on a grid
+ # this can happen if the parameters where changed between two sweeps
+ xi = np.linspace(xs.min(), xs.max(), 200)
+ yi = np.linspace(ys.min(), ys.max(), 200)
+
+ Xi, Yi = np.meshgrid(xi, yi)
+
+ Zi = griddata((xs, ys), zs, (Xi, Yi), 'nearest')
+
+ self.img.setImage(Zi.T)
+ self.iso.setData(Zi.T)
+ self.data_img = Zi
+
+ # set position and scale of image
+ newscalex = xi[1] - xi[0]
+ newscaley = yi[1] - yi[0]
+
+ transx = (xi[0] - (self.cx - 0.5 * self.scalex)) / newscalex - 0.5
+ transy = (yi[0] - (self.cy - 0.5 * self.scaley)) / newscaley - 0.5
+
+ self.img.scale(newscalex / self.scalex, newscaley / self.scaley)
+ self.img.translate(transx, transy)
+
+ self.scalex = newscalex
+ self.scaley = newscaley
+
+ self.cx = xi[0]
+ self.cy = yi[0]
+
+ def updateIsocurve(self):
+ self.iso.setLevel(self.isoLine.value())
+
+ def imageHoverEvent(self, event):
+ """Show the position, pixel, and value under the mouse cursor.
+ """
+ if event.isExit():
+ self.plot.setTitle("")
+ return
+ pos = event.pos()
+ i, j = pos.y(), pos.x()
+ i = int(np.clip(i, 0, self.data_img.shape[0] - 1))
+ j = int(np.clip(j, 0, self.data_img.shape[1] - 1))
+ val = self.data_img[i, j]
+ ppos = self.img.mapToParent(pos)
+ x, y = ppos.x(), ppos.y()
+ self.plot.setTitle("pos: (%0.1f, %0.1f) value: %g" % (x, y, val))
\ No newline at end of file
diff --git a/HelperClasses/Plotting/__init__.py b/HelperClasses/Plotting/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/dylab/TransitionClass.py b/HelperClasses/TransitionClass.py
similarity index 100%
rename from dylab/TransitionClass.py
rename to HelperClasses/TransitionClass.py
diff --git a/dylab/__init__.py b/HelperClasses/__init__.py
similarity index 100%
rename from dylab/__init__.py
rename to HelperClasses/__init__.py
diff --git a/__init__.py b/__init__.py
index 1a630b9..e119571 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1 +1 @@
-name = "dylab"
\ No newline at end of file
+name = "HelperClasses"
\ No newline at end of file
diff --git a/build/lib/dylab/AbsorptionImaging.py b/build/lib/dylab/AbsorptionImaging.py
index 2c4513d..bd4b188 100644
--- a/build/lib/dylab/AbsorptionImaging.py
+++ b/build/lib/dylab/AbsorptionImaging.py
@@ -2,7 +2,7 @@ from lyse import *
from pylab import *
import numpy as np
import scipy.constants as constant
-from dylab import DyTransition, Camera
+from HelperClasses import DyTransition, Camera
import matplotlib.pyplot as plt
from matplotlib import colors as mcolors
import colorsys
diff --git a/build/lib/dylab/DyTransition.py b/build/lib/dylab/DyTransition.py
index d203361..aabef80 100644
--- a/build/lib/dylab/DyTransition.py
+++ b/build/lib/dylab/DyTransition.py
@@ -1,7 +1,7 @@
# Functions create constant dictionary storing transition information of Dy.
import scipy.constants as constant
-from dylab import TransitionClass
+from HelperClasses import TransitionClass
def creat_Dy421():
diff --git a/build/lib/dylab/Fitting.py b/build/lib/dylab/Fitting.py
index 5dc8f5e..e909591 100644
--- a/build/lib/dylab/Fitting.py
+++ b/build/lib/dylab/Fitting.py
@@ -1,5 +1,5 @@
import numpy as np
-from dylab import FittingFunction
+from HelperClasses import FittingFunction
import laserbeamsize as lbs
import lmfit
from lmfit.minimizer import MinimizerResult
diff --git a/setup.py b/setup.py
index 2ab153f..24e6323 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
- name="dylab",
+ name="HelperClasses",
version="0.0.2",
author="QF-group (AG Chomaz), Heidelberg university",
author_email="gao@physi.uni-heidelberg.de",
diff --git a/test_absorption_imaging.py b/test_absorption_imaging.py
index e2dc29d..e262bc4 100644
--- a/test_absorption_imaging.py
+++ b/test_absorption_imaging.py
@@ -1,6 +1,12 @@
from lyse import *
-from dylab import DyTransition, Camera, AbsorptionImaging
+from HelperClasses import DyTransition, Camera, AbsorptionImaging, Plotting
+import matplotlib.pyplot as plt
+import matplotlib.style as mplstyle
+import matplotlib
+matplotlib.use("qt5agg")
+
+mplstyle.use('fast')
absorption_imaging_transition = DyTransition.creat_Dy421()
mot_3D_camera = Camera.c11440_36u(absorption_imaging_transition['wavelength'])
@@ -10,6 +16,11 @@ with AbsorptionImaging.absorption_imaging(path, 'MOT_3D_Camera', 'in_situ_absorp
absorption_image.select_effective_data((800, 500), (1000, 700))
absorption_image.get_atom_number()
- absorption_image.plot_result(-0.05, 0.05)
print(absorption_image.atom_number)
+
+
+plotting = Plotting.plotting_absorption_imaging(absorption_image)
+plotting.four_plots()
+# plotting.absorption_plots()
+
diff --git a/test_fitting.py b/test_fitting.py
index 41c8d27..a894f63 100644
--- a/test_fitting.py
+++ b/test_fitting.py
@@ -1,10 +1,10 @@
import laserbeamsize as lbs
-from dylab import Fitting
+from HelperClasses import Fitting
import numpy as np
import lmfit
import matplotlib.pyplot as plt
-from dylab.FittingFunction import twoD_Gaussian
+from HelperClasses.FittingFunction import twoD_Gaussian
xc = 300
yc = 200
diff --git a/test_living_plot.py b/test_living_plot.py
new file mode 100644
index 0000000..471464a
--- /dev/null
+++ b/test_living_plot.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Thu Mar 11 14:58:18 2021
+
+@author: Nick Sauerwein
+"""
+import lyse
+import numpy as np
+
+from HelperClasses.Plotting import WidgetFakeColorPlot, MainPlotPanel
+
+import cProfile
+import pstats
+
+profile = cProfile.Profile()
+profile.enable()
+
+fm = lyse.figure_manager
+
+h5_paths = lyse.h5_paths()
+
+if lyse.spinning_top:
+ # If so, use the filepath of the current shot
+ h5_path = lyse.path
+else:
+ # If not, get the filepath of the last shot of the lyse DataFrame
+ h5_path = lyse.h5_paths().iloc[-1]
+
+print(h5_path)
+
+if len(h5_paths):
+ last_globals = run = lyse.Run(h5_paths.iloc[-1]).get_globals()
+
+if not hasattr(fm, 'ap'):
+ fm.ap = MainPlotPanel.MainPlotPanel(h5_paths)
+
+# Imaging Methods
+imagings = ['absorption_imaging']
+
+cams = {'absorption_imaging': ['Cam_absorption']}
+
+imaging = 'absorption_imaging'
+for cam in cams[imaging]:
+ plot_name = imaging + ' ' + cam
+
+ if not plot_name in fm.ap.plots:
+ ip = WidgetFakeColorPlot.FakeColorPlot(plot_name, ('absorption_imaging', 'absorption_imaging'))
+
+ fm.ap.add_plot_dock(plot_name, ip)
+
+fm.ap.update_h5_paths(h5_paths)
+fm.ap.refresh(h5_path)
+
+profile.disable()
+ps = pstats.Stats(profile)
+ps.sort_stats('cumtime')
+ps.print_stats(10)