Source code for rayoptics.gui.dashboards

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © 2020 Michael J. Hayford
"""Dashboards constructed with ipywidgets.

.. Created on Tue Apr 28 21:15:20 2020

.. codeauthor: Michael J. Hayford
"""

import numpy as np
import ipywidgets as widgets


[docs]def create_focus_dashboard(figs, ray_data_items, foc: float, dfoc_rng: float, shift_rng: float, image_delta, continuous_update: bool=True): """Create 3 ipywidgets to modify defocus and image shift interactively. This function creates 3 ipywidgets (FloatSliders) controlling defoucus, x_shift and y_shift. When the user moves one of the sliders, the callback function applies the new slider value to the ray_data_items and causes them to 'update'. The ray_data items in the :mod:`~raytr.analyses` package all provide a lightweight 'update' implementation that doesn't require retracing the model. Arguments: figs: list of Figures to be cleared and replotted ray_data_items: list of ray data items to be updated to the new values foc: the current defocus dfoc_range: the maximum range for the defocus slider shift_rng: the maximum range for the image shift sliders image_delta: the current image shift, [delta_x: float, delta_y: float] continuous_update: if True, model is updated for any slider movement """ if image_delta is None: image_delta = [0., 0.] defocus = widgets.FloatSlider(min=-dfoc_rng, max=+dfoc_rng, step=.01*dfoc_rng, description='defocus', value=foc, readout_format='.4f', continuous_update=continuous_update) x_shift = widgets.FloatSlider(min=-shift_rng, max=+shift_rng, step=.01*shift_rng, description='x shift', value=image_delta[0], readout_format='.4f', continuous_update=continuous_update) y_shift = widgets.FloatSlider(min=-shift_rng, max=+shift_rng, step=.01*shift_rng, description='y shift', value=image_delta[1], readout_format='.4f', continuous_update=continuous_update) def slider_update(change): dfoc_val = defocus.value dx = x_shift.value dy = y_shift.value # apply changes to fans and grids for ray_data in ray_data_items: ray_data.foc = dfoc_val ray_data.image_delta = np.array([dx, dy]) ray_data.update_data(build='update') # update and plot results for fig in figs: fig.clf() fig.plot() defocus.observe(slider_update, names='value') x_shift.observe(slider_update, names='value') y_shift.observe(slider_update, names='value') return defocus, x_shift, y_shift
[docs]def create_mirror_tilt_dashboard(mirror, app_mgr, figs, ray_data_items, foc, tilt_rng, shift_rng, oa_ray=None, continuous_update=True): tilt0 = mirror.decenter.euler[0] alpha_tilt = widgets.FloatSlider(min=tilt0-tilt_rng, max=tilt0+tilt_rng, step=.05*tilt_rng, description='alpha tilt', value=tilt0, readout_format='.4f', continuous_update=continuous_update) x_shift = widgets.FloatSlider(min=-shift_rng, max=+shift_rng, step=.05*shift_rng, description='x shift', value=0., readout_format='.4f', continuous_update=continuous_update) y_shift = widgets.FloatSlider(min=-shift_rng, max=+shift_rng, step=.05*shift_rng, description='y shift', value=0., readout_format='.4f', continuous_update=continuous_update) def make_slider_callback(changer): def slider_update(change): changer.set(change['new']) app_mgr.refresh_gui(build='update') if oa_ray is not None: oa_ray.update_data(build='rebuild') # apply changes to fans and grids for ray_data in ray_data_items: # if oa_ray is not None: # ray_data.image_pt_2d = oa_ray.t_abr ray_data.update_data(build='rebuild') # update and plot results for fig in figs: fig.clf() fig.plot() return slider_update alpha_tilt.observe(make_slider_callback(AttrChanger(mirror.decenter, 'euler', index=0)), names='value') x_shift.observe(make_slider_callback(AttrChanger(mirror.decenter, 'dec', index=0)), names='value') y_shift.observe(make_slider_callback(AttrChanger(mirror.decenter, 'dec', index=1)), names='value') return alpha_tilt, x_shift, y_shift
[docs]class AttrChanger(): """Changer built on an object/attribute pair. """ def __init__(self, obj, attr, index=None): self.object = obj self.attr = attr self.index = index
[docs] def get(self): value = getattr(self.object, self.attr, None) #print('AttrChanger.get:', self.attr, value, self.index) value = value if self.index is None else value[self.index] return value
[docs] def set(self, value): if self.index is not None: seq = getattr(self.object, self.attr, None) seq[self.index] = value value = seq setattr(self.object, self.attr, value)
#print('AttrChanger.set:', self.attr, value)