Skip to content

Commit

Permalink
Merge pull request #1775 from HEXRD/various-fixes
Browse files Browse the repository at this point in the history
Various fixes and improvements
  • Loading branch information
psavery authored Jan 13, 2025
2 parents 8393744 + 2a55d49 commit ea32110
Show file tree
Hide file tree
Showing 20 changed files with 323 additions and 55 deletions.
70 changes: 64 additions & 6 deletions hexrdgui/calibration/calibration_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ def validate_parameters(self):
errors = []
path = []

has_tardis_constraints = self.has_tardis_constraints
tardis_ip4_y_path = self.tardis_ip4_y_path

def recurse(cur):
for k, v in cur.items():
path.append(k)
Expand All @@ -341,6 +344,26 @@ def recurse(cur):
# from raising an exception.
param.min -= 1e-8
param.max += 1e-8
elif (
has_tardis_constraints and
tuple(path) == tardis_ip4_y_path
):
# Don't allow the min/max to invalidate the value,
# because the value is computed, not set.
msg = (
'When TARDIS engineering constraints are set, '
'the Y translation of IMAGE-PLATE-4 is computed. '
'The min must be less than the computed value, '
'and the max must be greater than the computed '
'value.'
)
# We can't use `param.value`, because the min/max might
# affect that. Let's compute the expression instead.
value = param._expr_eval(param._expr)
if param.min > value:
errors.append(msg)
elif param.max < value:
errors.append(msg)
elif isinstance(v, dict):
recurse(v)
path.pop(-1)
Expand Down Expand Up @@ -458,8 +481,42 @@ def on_tilt_center_of_rotation_changed(self):
self.tilt_center_of_rotation_changed.emit(self.tilt_center_of_rotation)

def on_engineering_constraints_changed(self):
self.update_disabled_editor_paths()
self.engineering_constraints_changed.emit(self.engineering_constraints)

def update_disabled_editor_paths(self):
uneditable_paths = self.tree_view.model().uneditable_paths
disabled_paths = self.tree_view.disabled_editor_paths

uneditable_paths.clear()
disabled_paths.clear()
if self.has_tardis_constraints:
value_idx = self.tree_view_model_class.VALUE_IDX
vary_idx = self.tree_view_model_class.VARY_IDX

# The checkbox is disabled
disabled_paths.append(self.tardis_ip4_y_path + (vary_idx,))

# The value is uneditable
uneditable_paths.append(self.tardis_ip4_y_path + (value_idx,))

# A tree view update is necessary after changing the disabled editors
self.update_tree_view()

@property
def has_tardis_constraints(self):
return self.engineering_constraints == 'TARDIS'

@property
def tardis_ip4_y_path(self) -> tuple[str]:
return (
'detectors',
'IMAGE-PLATE-4',
'transform',
'translation',
'Y',
)

def on_delta_boundaries_toggled(self, b):
# The columns have changed, so we need to reinitialize the tree view
self.reinitialize_tree_view()
Expand Down Expand Up @@ -720,6 +777,8 @@ def initialize_tree_view(self):
# Make the key section a little larger
self.tree_view.header().resizeSection(0, 300)

self.update_disabled_editor_paths()

def reinitialize_tree_view(self):
# Keep the same scroll position
scrollbar = self.tree_view.verticalScrollBar()
Expand Down Expand Up @@ -790,11 +849,10 @@ def guess_engineering_constraints(instr) -> str | None:
# First guess the instrument type.
instr_type = guess_instrument_type(instr.detectors)

# If it matches one of our expected engineering constraints, use it.
expected_options = [
'TARDIS',
]
if instr_type in expected_options:
return instr_type
if instr_type == 'TARDIS':
# Make sure it contains both image plates
required_detectors = ['IMAGE-PLATE-2', 'IMAGE-PLATE-4']
if all(x in instr.detectors for x in required_detectors):
return instr_type

return None
19 changes: 16 additions & 3 deletions hexrdgui/calibration/calibration_dialog_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ def on_reset_relative_params_to_zero_clicked(self):

def save_constraint_params(self):
constraints = self.calibrator.relative_constraints
if constraints.type != RelativeConstraintsType.system:
# Instead of saving, reset them
self.reset_saved_constraint_params()
if constraints.type == RelativeConstraintsType.none:
# Nothing to save... Just make sure the old one is cleared.
HexrdConfig()._instrument_rigid_body_params.clear()
return

HexrdConfig()._instrument_rigid_body_params = copy.deepcopy(
Expand Down Expand Up @@ -243,15 +243,28 @@ def on_constraints_changed(self):
'brute_step',
'user_data',
]
blacklist_params = []

if self.has_tardis_constraints:
# If TARDIS engineering constraints are on, do not remember
# the previous value for the expression.
blacklist_params.append('IMAGE_PLATE_4_tvec_y')

for param_key, param in self.dialog.params_dict.items():
if param_key in blacklist_params:
continue

if param_key in self.calibrator.params:
current = self.calibrator.params[param_key]
for attr in to_remember:
setattr(current, attr, getattr(param, attr))

self.dialog.params_dict = self.calibrator.params

@property
def has_tardis_constraints(self) -> bool:
return self.calibrator.engineering_constraints == 'TARDIS'

def on_run_clicked(self):
self.async_runner.progress_title = 'Running calibration...'
self.async_runner.success_callback = self.on_calibration_finished
Expand Down
26 changes: 25 additions & 1 deletion hexrdgui/calibration/calibration_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def enable_focus_mode(self, b):
def clear_all_overlay_picks(self):
for overlay in self.active_overlays:
overlay.reset_calibration_picks()
overlay.pad_picks_data()

@property
def overlays(self):
Expand Down Expand Up @@ -152,6 +153,7 @@ def use_current_pick_points(self):
def hand_pick_points(self):
overlay = self.active_overlay
overlay.reset_calibration_picks()
overlay.pad_picks_data()

title = overlay.name

Expand Down Expand Up @@ -805,6 +807,7 @@ def auto_pick_points(self):
raise NotImplementedError(overlay.type)

overlay.reset_calibration_picks()
overlay.pad_picks_data()
return funcs[overlay.type]()

def auto_pick_powder_points(self):
Expand Down Expand Up @@ -978,8 +981,11 @@ def clear_drawn_picks(self):

def on_edit_picks_clicked(self):
dialog = self.edit_picks_dialog
tree_view = dialog.tree_view
model = tree_view.model()
model.disabled_paths.clear()

dialog.button_box_visible = True
dialog.ui.show()

def on_finished():
self.dialog.show()
Expand All @@ -991,6 +997,24 @@ def on_finished():
self.draw_picks_on_canvas()
self.dialog.hide()

# After the tree view is updated, disable paths that
# don't match this XRS.
if (
HexrdConfig().has_multi_xrs and
not self.showing_picks_from_all_xray_sources
):
# Disable paths that don't match this XRS
for item in model.root_item.child_items:
overlay_name = item.data(0)
overlay = Overlay.from_name(overlay_name)

if overlay.xray_source != HexrdConfig().active_beam_name:
model.disabled_paths.append((overlay_name,))

tree_view.collapse_disabled_paths()

dialog.ui.show()

def save_picks_to_file(self, selected_file):
# Reuse the same logic from the HKLPicksTreeViewDialog
self.edit_picks_dialog.export_picks(selected_file)
Expand Down
6 changes: 5 additions & 1 deletion hexrdgui/calibration/hkl_picks_tree_view_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def dictionary(self, v):

def setup_connections(self):
# Use accepted/rejected so these are called before on_finished()
self.ui.accepted.connect(self.on_finished)
self.ui.accepted.connect(self.on_accepted)
self.ui.rejected.connect(self.on_finished)

self.ui.export_picks.clicked.connect(self.export_picks_clicked)
Expand All @@ -64,6 +64,10 @@ def update_gui(self):
self.ui.show_overlays.setChecked(HexrdConfig().show_overlays)
self.ui.show_all_picks.setChecked(self.tree_view.show_all_picks)

def on_accepted(self):
self.tree_view.on_accepted()
self.on_finished()

def on_finished(self):
self.tree_view.clear_artists()
self.tree_view.clear_highlights()
Expand Down
6 changes: 3 additions & 3 deletions hexrdgui/calibration/polarview.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np

from skimage.filters.edges import binary_erosion
from skimage.morphology import rectangle
from skimage.morphology import footprint_rectangle
from skimage.transform import warp

from hexrd.transforms.xfcapi import mapAngle
Expand Down Expand Up @@ -428,10 +428,10 @@ def apply_snip(self, img):
# NOT done inside snip computation!
if HexrdConfig().polar_apply_erosion:
niter = HexrdConfig().polar_snip1d_numiter
structure = rectangle(
structure = footprint_rectangle((
1,
int(np.ceil(2.25*niter*snip_width_pixels()))
)
))
mask = binary_erosion(~self.raw_img.mask, structure)
img[~mask] = np.nan

Expand Down
9 changes: 9 additions & 0 deletions hexrdgui/calibration/structureless/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ def draw_picks_on_canvas(self):
def on_edit_picks_clicked(self):
# Convert to polar lines
data = cart_to_polar_lines(self.calibrator_lines, self.instr)
disabled_paths = []

# Now convert to a dictionary for the line labels
dictionary = {}
Expand All @@ -510,6 +511,12 @@ def on_edit_picks_clicked(self):
this_xrs[name] = v.tolist()
ring_indices[xray_source] = ring_idx

if (
not self.showing_picks_from_all_xray_sources and
xray_source != HexrdConfig().active_beam_name
):
disabled_paths.append((xray_source,))

def new_line_name_generator(path):
# Get the x-ray source
xray_source = path[0]
Expand All @@ -519,12 +526,14 @@ def new_line_name_generator(path):
dialog = GenericPicksTreeViewDialog(dictionary, canvas=self.canvas,
parent=self.canvas)
dialog.tree_view.new_line_name_generator = new_line_name_generator
dialog.tree_view.model().disabled_paths = disabled_paths
dialog.accepted.connect(self.on_edit_picks_accepted)
dialog.finished.connect(self.on_edit_picks_finished)
dialog.show()

self.edit_picks_dictionary = dictionary
self.edit_picks_dialog = dialog
dialog.tree_view.collapse_disabled_paths()

self.clear_drawn_picks()
self.dialog.hide()
Expand Down
23 changes: 23 additions & 0 deletions hexrdgui/calibration/tree_item_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@ def set_config_val(self, path, value):

setattr(param, attribute, value)

def data(self, index, role):
if (
role in (Qt.BackgroundRole, Qt.ForegroundRole) and
index.column() in (self.VALUE_IDX, self.VARY_IDX) and
self.has_uneditable_paths
):
# Check if this value is uneditable. If so, gray it out.
item = self.get_item(index)
path = tuple(self.path_to_item(item) + [self.VALUE_IDX])
if path in self.uneditable_paths:
color = 'gray'
if (
index.column() == self.VALUE_IDX and
role == Qt.ForegroundRole
):
color = 'white'

return QColor(color)

return super().data(index, role)


class DefaultCalibrationTreeItemModel(CalibrationTreeItemModel):
"""This model uses minimum/maximum for the boundary constraints"""
Expand All @@ -64,6 +85,7 @@ class DefaultCalibrationTreeItemModel(CalibrationTreeItemModel):
COLUMN_INDICES = _tree_columns_to_indices(COLUMNS)

VALUE_IDX = COLUMN_INDICES['Value']
VARY_IDX = COLUMN_INDICES['Vary']
MAX_IDX = COLUMN_INDICES['Maximum']
MIN_IDX = COLUMN_INDICES['Minimum']
BOUND_INDICES = (VALUE_IDX, MAX_IDX, MIN_IDX)
Expand Down Expand Up @@ -99,6 +121,7 @@ class DeltaCalibrationTreeItemModel(CalibrationTreeItemModel):
COLUMN_INDICES = _tree_columns_to_indices(COLUMNS)

VALUE_IDX = COLUMN_INDICES['Value']
VARY_IDX = COLUMN_INDICES['Vary']
DELTA_IDX = COLUMN_INDICES['Delta']
BOUND_INDICES = (VALUE_IDX, DELTA_IDX)

Expand Down
21 changes: 19 additions & 2 deletions hexrdgui/hexrd_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,19 @@ def get_statuses_instrument_format(self):
statuses.append(status[i])
continue

if path[0] == 'radius':
# Special case for radius
full_path = ['detectors', name] + path
try:
status = self.get_instrument_config_val(full_path)
except KeyError:
# There must not be a radius. Just skip over it.
pass
else:
statuses.append(status)

continue

full_path = ['detectors', name] + path
status = self.get_instrument_config_val(full_path)

Expand Down Expand Up @@ -1674,7 +1687,7 @@ def get_instrument_config_val(self, path):
except KeyError:
msg = ('Path: ' + str(path) + '\nwas not found in dict: ' +
str(self.config['instrument']))
raise Exception(msg)
raise KeyError(msg)

return cur_val

Expand Down Expand Up @@ -2215,6 +2228,10 @@ def clear_overlay_data(self):
for overlay in self.overlays:
overlay.update_needed = True

def reset_overlay_calibration_picks(self):
for overlay in self.overlays:
overlay.reset_calibration_picks()

def flag_overlay_updates_for_active_material(self):
self.flag_overlay_updates_for_material(self.active_material_name)

Expand Down Expand Up @@ -3045,7 +3062,7 @@ def apply_absorption_correction(self, v):
@property
def physics_package_dictified(self):
if not self.has_physics_package:
return None
return {}

return self.physics_package.serialize()

Expand Down
Loading

0 comments on commit ea32110

Please sign in to comment.