# -*- coding: utf-8 -*-
# pylint: bare-except
import logging
import sys
from typing import List
import numpy as np
from quicknxs.interfaces.data_handling.data_set import CrossSectionData
from quicknxs.ui.mplwidget import MPLWidget
[docs]
class PlotManager(object):
"""PlotManager is responsible for plotting the data in the main window."""
_refl_color_list = ["blue", "red", "green", "purple", "#aaaa00", "cyan"]
def __init__(self, main_window):
self.main_window = main_window
self.overview_lines = None
self._x_projection = None
self._y_projection = None
self.proj_lines = None
self.y_bg = 0.0
self.color = None
# Selection lines
self.xy_x1 = None
self.xy_x2 = None
self.xy_y1 = None
self.xy_y2 = None
self.xtof_x1 = None
self.xtof_x2 = None
self.xtof_bck1 = None
self.xtof_bck2 = None
[docs]
def plot_overview(self):
"""X vs. Y and X vs. Tof for main xs."""
self.xy_x1 = None
self.xy_x2 = None
self.xy_y1 = None
self.xy_y2 = None
self.xtof_x1 = None
self.xtof_x2 = None
self.xtof_bck1 = None
self.xtof_bck2 = None
main_window = self.main_window
main_window.ui.xy_overview.clear()
main_window.ui.xtof_overview.clear()
data: CrossSectionData = main_window.data_manager.active_cross_section
# Initialize data as needed
data.prepare_plot_data()
if data.total_counts == 0:
main_window.ui.xy_overview.draw()
main_window.ui.xtof_overview.draw()
return
xy = data.xydata
if data.proton_charge > 0.0:
xtof = data.xtofdata / data.proton_charge
else:
logging.error("Empty reflectivity curve has no proton charge")
xtof = data.xtofdata
if len(xtof[xtof > 0]) == 0:
logging.error("No positive data found")
return
ref_norm = main_window.get_direct_beam()
if main_window.ui.normalizeXTof.isChecked() and ref_norm is not None:
ref_norm = ref_norm.get_counts_vs_TOF()
# normalize ToF dataset for wavelength distribution
ref_norm = np.where(ref_norm > 0.0, ref_norm, 1.0)
xtof = xtof.astype(float) / ref_norm[np.newaxis, :]
xy_imin = xy[xy > 0].min()
xy_imax = xy.max()
tof_imin = xtof[xtof > 0].min()
tof_imax = xtof.max()
# for lines of the current extraction area
x_peak = main_window.ui.refXPos.value()
x_width = main_window.ui.refXWidth.value()
y_pos = main_window.ui.refYPos.value()
y_width = main_window.ui.refYWidth.value()
bg_pos = main_window.ui.bgCenter.value()
bg_width = main_window.ui.bgWidth.value()
# XY plot
if main_window.ui.tthPhi.isChecked():
rad_per_pixel = data.det_size_x / data.dist_sam_det / data.xydata.shape[1]
phi_range = xy.shape[0] * rad_per_pixel * 180.0 / np.pi
tth_range = xy.shape[1] * rad_per_pixel * 180.0 / np.pi
phi0 = main_window.ui.refYPos.value() * rad_per_pixel * 180.0 / np.pi
tth0 = (data.dangle - data.angle_offset) - (xy.shape[1] - data.dpix) * rad_per_pixel * 180.0 / np.pi
main_window.ui.xy_overview.imshow(
xy,
log=main_window.ui.logarithmic_colorscale.isChecked(),
aspect="auto",
cmap=self.color,
origin="lower",
extent=[tth_range + tth0, tth0, phi0, phi0 - phi_range],
)
main_window.ui.xy_overview.set_xlabel("2$\\Theta{}$ [°]")
main_window.ui.xy_overview.set_ylabel("$\\phi{}$ [°]")
main_window.ui.xy_overview.cplot.set_clim([xy_imin, xy_imax])
else:
main_window.ui.xy_overview.imshow(
xy,
log=main_window.ui.logarithmic_colorscale.isChecked(),
aspect="auto",
cmap=self.color,
origin="lower",
)
main_window.ui.xy_overview.set_xlabel("x [pix]")
main_window.ui.xy_overview.set_ylabel("y [pix]")
main_window.ui.xy_overview.cplot.set_clim([xy_imin, xy_imax])
if self.xy_x1 is None:
self.xy_x1 = main_window.ui.xy_overview.canvas.ax.axvline(x_peak - (x_width / 2.0), color="#aa0000")
self.xy_x2 = main_window.ui.xy_overview.canvas.ax.axvline(x_peak + (x_width / 2.0), color="#aa0000")
self.xy_y1 = main_window.ui.xy_overview.canvas.ax.axhline(y_pos - (y_width / 2.0), color="#00aa00")
self.xy_y2 = main_window.ui.xy_overview.canvas.ax.axhline(y_pos + (y_width / 2.0), color="#00aa00")
else:
self.xy_x1.set_xdata([x_peak - (x_width / 2.0), x_peak - (x_width / 2.0)])
self.xy_x2.set_xdata([x_peak + (x_width / 2.0), x_peak + (x_width / 2.0)])
self.xy_y1.set_ydata([y_pos - (y_width / 2.0), y_pos - (y_width / 2.0)])
self.xy_y2.set_ydata([y_pos + (y_width / 2.0), y_pos + (y_width / 2.0)])
# XToF plot
if main_window.ui.xLamda.isChecked():
main_window.ui.xtof_overview.imshow(
xtof[::-1],
log=main_window.ui.logarithmic_colorscale.isChecked(),
aspect="auto",
cmap=self.color,
extent=[data.wavelength[0], data.wavelength[-1], 0, data.x.shape[0] - 1],
)
main_window.ui.xtof_overview.set_xlabel("$\\lambda{}$ [Å]")
else:
main_window.ui.xtof_overview.imshow(
xtof[::-1],
log=main_window.ui.logarithmic_colorscale.isChecked(),
aspect="auto",
cmap=self.color,
extent=[data.tof[0] * 1e-3, data.tof[-1] * 1e-3, 0, data.x.shape[0] - 1],
)
main_window.ui.xtof_overview.set_xlabel("ToF [ms]")
main_window.ui.xtof_overview.set_ylabel("x [pix]")
if self.xtof_x1 is None:
self.xtof_x1 = main_window.ui.xtof_overview.canvas.ax.axhline(x_peak - (x_width / 2.0), color="#aa0000")
self.xtof_x2 = main_window.ui.xtof_overview.canvas.ax.axhline(x_peak + (x_width / 2.0), color="#aa0000")
self.xtof_bck1 = main_window.ui.xtof_overview.canvas.ax.axhline(bg_pos - (bg_width / 2.0), color="black")
self.xtof_bck2 = main_window.ui.xtof_overview.canvas.ax.axhline(bg_pos + (bg_width / 2.0), color="black")
else:
self.xtof_x1.set_ydata([x_peak - (x_width / 2.0), x_peak - (x_width / 2.0)])
self.xtof_x2.set_ydata([x_peak + (x_width / 2.0), x_peak + (x_width / 2.0)])
self.xtof_bck1.set_ydata([bg_pos - (bg_width / 2.0), bg_pos - (bg_width / 2.0)])
self.xtof_bck2.set_ydata([bg_pos + (bg_width / 2.0), bg_pos + (bg_width / 2.0)])
main_window.ui.xtof_overview.cplot.set_clim([tof_imin, tof_imax])
if main_window.ui.show_colorbars.isChecked() and main_window.ui.xy_overview.cbar is None:
main_window.ui.xy_overview.cbar = main_window.ui.xy_overview.canvas.fig.colorbar(
main_window.ui.xy_overview.cplot
)
main_window.ui.xtof_overview.cbar = main_window.ui.xtof_overview.canvas.fig.colorbar(
main_window.ui.xtof_overview.cplot
)
main_window.ui.xy_overview.draw()
main_window.ui.xtof_overview.draw()
[docs]
def plot_xy(self):
"""X vs. Y plots for all xss."""
main_window = self.main_window
data_set_keys = list(main_window.data_manager.data_sets.keys())
plots = [main_window.ui.xy_pp, main_window.ui.xy_mm, main_window.ui.xy_pm, main_window.ui.xy_mp]
for i in range(len(data_set_keys), 4):
if plots[i].cplot is not None:
plots[i].clear()
plots[i].draw()
imin = 1e20
imax = 1e-20
xynormed = []
progress = self.main_window.file_handler.new_progress_reporter()
n_total = len(data_set_keys)
progress(0.1, message="Compiling plots", out_of=n_total + 1)
for i, key in enumerate(data_set_keys[:4]):
dataset = main_window.data_manager.data_sets[key]
dataset.prepare_plot_data()
d = dataset.xydata / dataset.proton_charge
xynormed.append(d)
if dataset.total_counts == 0:
continue
imin = min(imin, d[d > 0].min())
imax = max(imax, d.max())
progress(i, message="Prepared %s plot" % key, out_of=n_total + 1)
if len(xynormed) > 1:
main_window.ui.frame_xy_mm.show()
if len(xynormed) == 4:
main_window.ui.frame_xy_sf.show()
else:
main_window.ui.frame_xy_sf.hide()
else:
main_window.ui.frame_xy_mm.hide()
main_window.ui.frame_xy_sf.hide()
progress(n_total, message="Plotting...", out_of=n_total + 1)
for i, datai in enumerate(xynormed):
if main_window.data_manager.data_sets[data_set_keys[i]].total_counts == 0:
continue
if main_window.ui.tthPhi.isChecked():
plots[i].clear()
rad_per_pixel = dataset.det_size_x / dataset.dist_sam_det / dataset.xydata.shape[1]
phi_range = datai.shape[0] * rad_per_pixel * 180.0 / np.pi
tth_range = datai.shape[1] * rad_per_pixel * 180.0 / np.pi
phi0 = main_window.ui.refYPos.value() * rad_per_pixel * 180.0 / np.pi
tth0 = (dataset.dangle - dataset.angle_offset) - (
datai.shape[1] - dataset.dpix
) * rad_per_pixel * 180.0 / np.pi
plots[i].imshow(
datai,
log=main_window.ui.logarithmic_colorscale.isChecked(),
imin=imin,
imax=imax,
aspect="auto",
cmap=self.color,
origin="lower",
extent=[tth_range + tth0, tth0, phi0, phi0 - phi_range],
)
plots[i].set_xlabel("2$\\Theta{}$ [°]")
plots[i].set_ylabel("$\\phi{}$ [°]")
else:
plots[i].imshow(
datai,
log=main_window.ui.logarithmic_colorscale.isChecked(),
imin=imin,
imax=imax,
aspect="auto",
cmap=self.color,
origin="lower",
)
plots[i].set_xlabel("x [pix]")
plots[i].set_ylabel("y [pix]")
plots[i].set_title(data_set_keys[i])
if plots[i].cplot is not None:
plots[i].cplot.set_clim([imin, imax])
if plots[i].cplot is not None and main_window.ui.show_colorbars.isChecked() and plots[i].cbar is None:
plots[i].cbar = plots[i].canvas.fig.colorbar(plots[i].cplot)
plots[i].draw()
progress(100, message="Ready", out_of=100)
[docs]
def plot_xtof(self):
"""X vs. ToF plots for all xss."""
main_window = self.main_window
data_set_keys = list(main_window.data_manager.data_sets.keys())
imin = 1e20
imax = 1e-20
xtofnormed = []
ref_norm = main_window.get_direct_beam()
if ref_norm is not None:
ref_norm = ref_norm.get_counts_vs_TOF()
ref_norm = np.where(ref_norm > 0, ref_norm, 1.0)
progress = self.main_window.file_handler.new_progress_reporter()
n_total = len(data_set_keys)
progress(0.1, message="Compiling plots", out_of=n_total + 1)
for i, key in enumerate(data_set_keys[:4]):
dataset = main_window.data_manager.data_sets[key]
dataset.prepare_plot_data()
d = dataset.xtofdata / dataset.proton_charge
if main_window.ui.normalizeXTof.isChecked() and ref_norm is not None:
# normalize all datasets for wavelength distribution
d = d / ref_norm[np.newaxis, :]
xtofnormed.append(d)
if dataset.total_counts == 0:
continue
imin = min(imin, d[d > 0].min())
imax = max(imax, d.max())
progress(i, message="Prepared %s plot" % key, out_of=n_total + 1)
progress(n_total, message="Plotting...", out_of=n_total + 1)
wavelength = main_window.data_manager.active_cross_section.wavelength
tof = main_window.data_manager.active_cross_section.tof
plots = [main_window.ui.xtof_pp, main_window.ui.xtof_mm, main_window.ui.xtof_pm, main_window.ui.xtof_mp]
for i in range(len(data_set_keys), 4):
if plots[i].cplot is not None:
plots[i].clear()
plots[i].draw()
if len(xtofnormed) > 1:
main_window.ui.frame_xtof_mm.show()
if len(xtofnormed) == 4:
main_window.ui.frame_xtof_sf.show()
else:
main_window.ui.frame_xtof_sf.hide()
else:
main_window.ui.frame_xtof_mm.hide()
main_window.ui.frame_xtof_sf.hide()
for i, datai in enumerate(xtofnormed):
if main_window.data_manager.data_sets[data_set_keys[i]].total_counts == 0:
continue
if main_window.ui.xLamda.isChecked():
plots[i].imshow(
datai[::-1],
log=main_window.ui.logarithmic_colorscale.isChecked(),
imin=imin,
imax=imax,
aspect="auto",
cmap=self.color,
extent=[wavelength[0], wavelength[-1], 0, datai.shape[0] - 1],
)
plots[i].set_xlabel("$\\lambda{}$ [Å]")
else:
plots[i].imshow(
datai[::-1],
log=main_window.ui.logarithmic_colorscale.isChecked(),
imin=imin,
imax=imax,
aspect="auto",
cmap=self.color,
extent=[tof[0] * 1e-3, tof[-1] * 1e-3, 0, datai.shape[0] - 1],
)
plots[i].set_xlabel("ToF [ms]")
plots[i].set_title(data_set_keys[i])
plots[i].set_ylabel("x [pix]")
if plots[i].cplot is not None:
plots[i].cplot.set_clim([imin, imax])
if plots[i].cplot is not None and main_window.ui.show_colorbars.isChecked() and plots[i].cbar is None:
plots[i].cbar = plots[i].canvas.fig.colorbar(plots[i].cplot)
plots[i].draw()
progress(100, message="Ready", out_of=100)
[docs]
def plot_projections(self, preserve_lim=False):
"""Create projections of the data on the x and y axes.
The x-projection can also be done be means of quantile calculation,
which means that the ToF intensities are calculation which are
exceeded by a certain number of points. This can be helpful to better
separate the specular reflection from bragg-sheets
"""
main_window = self.main_window
if main_window.data_manager.active_cross_section is None:
return
data = main_window.data_manager.active_cross_section
data.prepare_plot_data()
if data.total_counts == 0:
main_window.ui.x_project.clear()
main_window.ui.x_project.draw()
main_window.ui.y_project.clear()
main_window.ui.y_project.draw()
return
xproj = data.xdata
yproj = data.ydata
x_peak = main_window.ui.refXPos.value()
x_width = main_window.ui.refXWidth.value()
y_pos = main_window.ui.refYPos.value()
y_width = main_window.ui.refYWidth.value()
bg_pos = main_window.ui.bgCenter.value()
bg_width = main_window.ui.bgWidth.value()
if preserve_lim:
xview = main_window.ui.x_project.canvas.ax.axis()
yview = main_window.ui.y_project.canvas.ax.axis()
xxlim = (0, len(xproj) - 1)
xylim = (xproj[xproj > 0].min(), xproj.max() * 2)
yxlim = (0, len(yproj) - 1)
yylim = (yproj[yproj > 0].min(), yproj.max() * 2)
if self._x_projection is None or len(self._x_projection.get_xdata()) != xproj.shape[0]:
main_window.ui.x_project.clear_fig()
main_window.ui.y_project.clear_fig()
x_projection = main_window.ui.x_project.plot(xproj, color="blue")[0]
# main_window.ui.x_project.set_xlabel(u'x [pix]')
# main_window.ui.x_project.set_ylabel(u'I$_{max}$')
xpos = main_window.ui.x_project.canvas.ax.axvline(x_peak, color="black")
xleft = main_window.ui.x_project.canvas.ax.axvline(x_peak - x_width / 2.0, color="red")
xright = main_window.ui.x_project.canvas.ax.axvline(x_peak + x_width / 2.0, color="red")
xleft_bg = main_window.ui.x_project.canvas.ax.axvline(bg_pos - bg_width / 2.0, color="black")
xright_bg = main_window.ui.x_project.canvas.ax.axvline(bg_pos + bg_width / 2.0, color="black")
self._y_projection = main_window.ui.y_project.plot(yproj, color="blue")[0]
# main_window.ui.y_project.set_xlabel(u'y [pix]')
# main_window.ui.y_project.set_ylabel(u'I$_{max}$')
yreg_left = main_window.ui.y_project.canvas.ax.axvline(y_pos - y_width / 2.0, color="green")
yreg_right = main_window.ui.y_project.canvas.ax.axvline(y_pos + y_width / 2.0, color="green")
ybg = main_window.ui.y_project.canvas.ax.axhline(self.y_bg, color="black")
self.proj_lines = (xleft, xpos, xright, xleft_bg, xright_bg, yreg_left, yreg_right, ybg)
self._x_projection = x_projection
else:
self._x_projection.set_ydata(xproj)
self._y_projection.set_ydata(yproj)
lines = self.proj_lines
lines[0].set_xdata([x_peak - x_width / 2.0, x_peak - x_width / 2.0])
lines[1].set_xdata([x_peak, x_peak])
lines[2].set_xdata([x_peak + x_width / 2.0, x_peak + x_width / 2.0])
lines[3].set_xdata([bg_pos - bg_width / 2.0, bg_pos - bg_width / 2.0])
lines[4].set_xdata([bg_pos + bg_width / 2.0, bg_pos + bg_width / 2.0])
lines[5].set_xdata([y_pos - y_width / 2.0, y_pos - y_width / 2.0])
lines[6].set_xdata([y_pos + y_width / 2.0, y_pos + y_width / 2.0])
lines[7].set_ydata([self.y_bg, self.y_bg])
if preserve_lim:
main_window.ui.x_project.canvas.ax.axis(xview)
main_window.ui.y_project.canvas.ax.axis(yview)
if main_window.ui.logarithmic_y.isChecked():
main_window.ui.x_project.set_yscale("log")
main_window.ui.y_project.set_yscale("log")
else:
main_window.ui.x_project.set_yscale("linear")
main_window.ui.y_project.set_yscale("linear")
main_window.ui.x_project.canvas.ax.set_xlim(*xxlim)
main_window.ui.x_project.canvas.ax.set_ylim(*xylim)
main_window.ui.y_project.canvas.ax.set_xlim(*yxlim)
main_window.ui.y_project.canvas.ax.set_ylim(*yylim)
main_window.ui.x_project.draw()
main_window.ui.y_project.draw()
[docs]
def plot_offspec(self, recalc=True, crop=False):
"""Plot off-specular data.
Create an offspecular plot for all cross sections of the datasets in the
reduction list. The user can define upper and lower bounds for the
plotted intensity and select the coordinates to be either kiz-kfz vs. Qz,
Qx vs. Qz or kiz vs. kfz.
"""
if self.main_window.data_manager.active_cross_section is None:
return
xlim = None
ylim = None
if crop and self.main_window.ui.offspec_pp.cplot is not None:
xlim = self.main_window.ui.offspec_pp.canvas.ax.get_xlim()
ylim = self.main_window.ui.offspec_pp.canvas.ax.get_ylim()
plots: List[MPLWidget] = [
self.main_window.ui.offspec_pp,
self.main_window.ui.offspec_mm,
self.main_window.ui.offspec_pm,
self.main_window.ui.offspec_mp,
]
for plot in plots:
plot.clear()
data_set_keys = list(self.main_window.data_manager.data_sets.keys())
for i in range(len(data_set_keys), 4):
if plots[i].cplot is not None:
plots[i].draw()
plots[i].hide()
i_min = 10 ** self.main_window.ui.offspecImin.value()
i_max = 10 ** self.main_window.ui.offspecImax.value()
qz_min = 0.5
qz_max = -0.1
qx_min = -0.001
qx_max = 0.001
ki_z_min = 0.1
ki_z_max = -0.1
kf_z_min = 0.1
kf_z_max = -0.1
k_diff_min = 0.01
k_diff_max = -0.01
progress = self.main_window.file_handler.new_progress_reporter()
n_total = len(self.main_window.data_manager.reduction_list)
if n_total == 0:
progress(100, message="No data to reduce: add data to the reduction list", out_of=100)
return
progress(0.1, message="Computing off-specular", out_of=n_total)
final_msg = "Off-specular calculation complete"
for i_run, nexus_data in enumerate(self.main_window.data_manager.reduction_list):
for i, xs in enumerate(data_set_keys):
plot = plots[i]
plot.show()
selected_data = nexus_data.cross_sections[xs]
progress(i_run + i / 4.0, message="Processed run %s %s" % (selected_data.number, xs), out_of=n_total)
PN = len(selected_data.tof) - selected_data.configuration.cut_first_n_points
P0 = selected_data.configuration.cut_last_n_points
ki_z, kf_z = selected_data.off_spec.ki_z, selected_data.off_spec.kf_z
Qx, Qz, S = selected_data.off_spec.Qx, selected_data.off_spec.Qz, selected_data.off_spec.S
try:
qz_max = max(Qz[S > 0].max(), qz_max)
qz_min = min(Qz[S > 0].min(), qz_min)
qx_min = min(qx_min, Qx[S > 0].min())
qx_max = max(qx_max, Qx[S > 0].max())
ki_z_min = min(ki_z_min, ki_z[S > 0].min())
ki_z_max = max(ki_z_max, ki_z[S > 0].max())
kf_z_min = min(kf_z_min, kf_z[S > 0].min())
kf_z_max = max(kf_z_max, kf_z[S > 0].max())
k_diff_min = min(k_diff_min, (ki_z - kf_z)[S > 0].min())
k_diff_max = max(k_diff_max, (ki_z - kf_z)[S > 0].max())
except:
logging.error("Error computing ranges: %s", sys.exc_info()[1])
if self.main_window.ui.kizmkfzVSqz.isChecked():
plot.pcolormesh(
(ki_z - kf_z)[:, P0:PN],
Qz[:, P0:PN],
S[:, P0:PN],
log=True,
imin=i_min,
imax=i_max,
cmap=self.color,
shading="gouraud",
)
elif self.main_window.ui.qxVSqz.isChecked():
plot.pcolormesh(
Qx[:, P0:PN],
Qz[:, P0:PN],
S[:, P0:PN],
log=True,
imin=i_min,
imax=i_max,
cmap=self.color,
shading="gouraud",
)
else:
plot.pcolormesh(
ki_z[:, P0:PN],
kf_z[:, P0:PN],
S[:, P0:PN],
log=True,
imin=i_min,
imax=i_max,
cmap=self.color,
shading="gouraud",
)
for i, xs in enumerate(data_set_keys):
plot = plots[i]
if self.main_window.ui.kizmkfzVSqz.isChecked():
plot.canvas.ax.set_xlim([k_diff_min, k_diff_max])
plot.canvas.ax.set_ylim([qz_min, qz_max])
plot.set_xlabel("k$_{i,z}$-k$_{f,z}$ [Å$^{-1}$]", fontsize=14)
plot.set_ylabel("Q$_z$ [Å$^{-1}$]", fontsize=14)
elif self.main_window.ui.qxVSqz.isChecked():
plot.canvas.ax.set_xlim([qx_min, qx_max])
plot.canvas.ax.set_ylim([qz_min, qz_max])
plot.set_xlabel("Q$_x$ [Å$^{-1}$]", fontsize=14)
plot.set_ylabel("Q$_z$ [Å$^{-1}$]", fontsize=14)
else:
plot.canvas.ax.set_xlim([ki_z_min, ki_z_max])
plot.canvas.ax.set_ylim([kf_z_min, kf_z_max])
plot.set_xlabel("k$_{i,z}$ [Å$^{-1}$]", fontsize=14)
plot.set_ylabel("k$_{f,z}$ [Å$^{-1}$]", fontsize=14)
plot.set_xticks_fontsize(8)
plot.set_yticks_fontsize(8)
plot.set_title(xs, fontsize=14)
if plot.cplot is not None:
plot.cplot.set_clim([i_min, i_max])
if self.main_window.ui.show_colorbars.isChecked() and plots[i].cbar is None:
plots[i].cbar = plots[i].canvas.fig.colorbar(plots[i].cplot)
if xlim is not None and ylim is not None:
plot.canvas.ax.set_xlim(*xlim)
plot.canvas.ax.set_ylim(*ylim)
plot.draw()
progress(100, message=final_msg, out_of=100)
def _plot_message(self, widget, message):
widget.canvas.ax.text(
0.5,
0.5,
message,
horizontalalignment="center",
verticalalignment="center",
fontsize=14,
transform=widget.canvas.ax.transAxes,
)
widget.draw()
def _prepare_intensity_plot(self):
"""Add the direct beam intensity from the current dataset to the reflectivity/intensity plot."""
# format and plot tof data
counts_normalized = self.main_window.data_manager.active_cross_section.get_tof_counts_table()[0][:, 2]
counts_normalized_error = self.main_window.data_manager.active_cross_section.get_tof_counts_table()[0][:, 3]
if self.main_window.ui.xLamda.isChecked():
self.main_window.ui.refl.errorbar(
self.main_window.data_manager.active_cross_section.wavelength,
counts_normalized,
yerr=counts_normalized_error,
)
self.main_window.ui.refl.set_xlabel("$\\lambda{}$ [Å]")
else:
# convert raw microsecond units to milliseconds to match xtof plot
tof_ms = self.main_window.data_manager.active_cross_section.get_tof_counts_table()[0][:, 0] / 1000
self.main_window.ui.refl.errorbar(
tof_ms,
counts_normalized,
yerr=counts_normalized_error,
)
self.main_window.ui.refl.set_xlabel("ToF [ms]")
# y-label is counts in either case
self.main_window.ui.refl.set_ylabel("Normalized ROI Counts")
[docs]
def plot_reflectivity_or_intensity(self):
"""Plot reflectivity data or intensity, depending on the type of the active run.
If the active run is a direct beam run (natively labeled as direct beam OR selected
from the direct beam table), the intensity vs ToF (or wavelength) in the
region-of-interest (ROI) is plotted.
If the active run is selected from the reduction/data table, the reflectivity of all
datasets is plotted, even if that same run also exists in the direct beam table.
This allows users to compare different ROIs by adding the same run to both tables,
with the plot type determined by which table the user is currently viewing from.
Returns
-------
True if the plot was successful, False otherwise
"""
self.main_window.ui.refl.clear()
if self.main_window.data_manager.active_cross_section is None:
self._plot_message(self.main_window.ui.refl, "No data")
return False
if self.main_window.data_manager.active_cross_section.is_direct_beam:
plot_title = "Intensity"
self._prepare_intensity_plot()
is_plotted = True
else:
plot_title = "Reflectivity"
is_plotted = self._prepare_reflectivity_plot()
if is_plotted:
# draw plot
if self.main_window.ui.logarithmic_y.isChecked():
self.main_window.ui.refl.set_yscale("log")
else:
self.main_window.ui.refl.set_yscale("linear")
self.main_window.ui.refl.legend()
self.main_window.ui.refl.toolbar.set_history_buttons()
self.main_window.ui.refl.draw()
# update plot title
self.main_window.ui.reflectivity_or_intensity_plot_title.setText(plot_title)
self.main_window.ui.compare_widget.update_preview()
return is_plotted
def _prepare_reflectivity_plot(self):
"""Add the reflectivity from the current dataset and any dataset stored to the reflectivity/intensity plot.
Returns
-------
True if the plot preparation was successful, False otherwise
"""
if (
self.main_window.data_manager.active_cross_section.r is None
or self.main_window.data_manager.active_cross_section.q is None
or self.main_window.data_manager.active_cross_section.dr is None
):
self._plot_message(self.main_window.ui.refl, "No data")
return False
data = self.main_window.data_manager.active_cross_section
if data.total_counts == 0:
self._plot_message(self.main_window.ui.refl, "No points to show\nin active dataset!")
return False
def _set_ymax(ymax, ynormed):
_ymax = max(ymax, ynormed.max())
if _ymax == np.inf:
_ymax = np.ma.array(ynormed, mask=~np.isfinite(ynormed)).max()
_ymax = max(ymax, _ymax)
return _ymax
ymin = 1.5
ymax = 1e-7
P0 = self.main_window.ui.rangeStart.value()
PN = len(self.main_window.data_manager.active_cross_section.q) - self.main_window.ui.rangeEnd.value()
ynormed = self.main_window.data_manager.active_cross_section.r[P0:PN]
if len(ynormed[ynormed > 0]) < 2:
self._plot_message(self.main_window.ui.refl, "No points to show\nin active dataset!")
return False
ymin = min(ymin, ynormed[ynormed > 0].min())
ymax = _set_ymax(ymax, ynormed)
self.main_window.ui.refl.errorbar(
self.main_window.data_manager.active_cross_section.q[P0:PN],
ynormed,
yerr=self.main_window.data_manager.active_cross_section.dr[P0:PN],
label="Active",
lw=2,
capsize=1,
color="black",
)
xs_name = self.main_window.data_manager.active_cross_section.name
for i, refli in enumerate(self.main_window.data_manager.reduction_list):
if refli.cross_sections[xs_name].q is None:
continue
P0i = refli.cross_sections[xs_name].configuration.cut_first_n_points
PNi = len(refli.cross_sections[xs_name].q) - refli.cross_sections[xs_name].configuration.cut_last_n_points
ynormed = refli.cross_sections[xs_name].r[P0i:PNi]
try:
ymin = min(ymin, ynormed[ynormed > 0].min())
except ValueError:
pass
try:
ymax = _set_ymax(ymax, ynormed)
except ValueError:
pass
self.main_window.ui.refl.errorbar(
refli.cross_sections[xs_name].q[P0i:PNi],
ynormed,
yerr=refli.cross_sections[xs_name].dr[P0i:PNi],
label=str(refli.number),
capsize=1,
color=self._refl_color_list[i % len(self._refl_color_list)],
)
self.main_window.ui.refl.canvas.ax.set_ylim((ymin * 0.9, ymax * 1.1))
self.main_window.ui.refl.set_xlabel("Q$_z$ [Å$^{-1}$]")
return True
[docs]
def plot_gisans(self):
"""Create GISANS plots of the current dataset with Qy-Qz maps."""
if self.main_window.data_manager.active_cross_section is None:
return
plots = [
self.main_window.ui.gisans_pp,
self.main_window.ui.gisans_mm,
self.main_window.ui.gisans_pm,
self.main_window.ui.gisans_mp,
]
for plot in plots:
plot.clear()
data_set_keys = list(self.main_window.data_manager.data_sets.keys())
for i in range(len(data_set_keys), 4):
if plots[i].cplot is not None:
plots[i].draw()
plots[i].hide()
Imin = 10 ** self.main_window.ui.gisansImin.value()
Imax = 10 ** self.main_window.ui.gisansImax.value()
for i, xs in enumerate(data_set_keys):
plot = plots[i]
plot.show()
selected_data = self.main_window.data_manager.data_sets[xs]
plots[i].clear_fig()
plots[i].pcolormesh(
selected_data.gisans_data.QyGrid,
selected_data.gisans_data.QzGrid,
selected_data.gisans_data.SGrid,
log=self.main_window.ui.logarithmic_colorscale.isChecked(),
imin=Imin,
imax=Imax,
cmap=self.color,
)
plots[i].set_xlabel("Q$_y$ [Å$^{-1}$]")
plots[i].set_ylabel("Q$_z$ [Å$^{-1}$]")
plots[i].set_title(xs)
if plots[i].cplot is not None:
plots[i].cplot.set_clim([Imin, Imax])
if plots[i].cplot is not None and self.main_window.ui.show_colorbars.isChecked() and plots[i].cbar is None:
plots[i].cbar = plots[i].canvas.fig.colorbar(plots[i].cplot)
plots[i].draw()