Skip to content

Commit ea32110

Browse files
authored
Merge pull request #1775 from HEXRD/various-fixes
Various fixes and improvements
2 parents 8393744 + 2a55d49 commit ea32110

20 files changed

+323
-55
lines changed

hexrdgui/calibration/calibration_dialog.py

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ def validate_parameters(self):
327327
errors = []
328328
path = []
329329

330+
has_tardis_constraints = self.has_tardis_constraints
331+
tardis_ip4_y_path = self.tardis_ip4_y_path
332+
330333
def recurse(cur):
331334
for k, v in cur.items():
332335
path.append(k)
@@ -341,6 +344,26 @@ def recurse(cur):
341344
# from raising an exception.
342345
param.min -= 1e-8
343346
param.max += 1e-8
347+
elif (
348+
has_tardis_constraints and
349+
tuple(path) == tardis_ip4_y_path
350+
):
351+
# Don't allow the min/max to invalidate the value,
352+
# because the value is computed, not set.
353+
msg = (
354+
'When TARDIS engineering constraints are set, '
355+
'the Y translation of IMAGE-PLATE-4 is computed. '
356+
'The min must be less than the computed value, '
357+
'and the max must be greater than the computed '
358+
'value.'
359+
)
360+
# We can't use `param.value`, because the min/max might
361+
# affect that. Let's compute the expression instead.
362+
value = param._expr_eval(param._expr)
363+
if param.min > value:
364+
errors.append(msg)
365+
elif param.max < value:
366+
errors.append(msg)
344367
elif isinstance(v, dict):
345368
recurse(v)
346369
path.pop(-1)
@@ -458,8 +481,42 @@ def on_tilt_center_of_rotation_changed(self):
458481
self.tilt_center_of_rotation_changed.emit(self.tilt_center_of_rotation)
459482

460483
def on_engineering_constraints_changed(self):
484+
self.update_disabled_editor_paths()
461485
self.engineering_constraints_changed.emit(self.engineering_constraints)
462486

487+
def update_disabled_editor_paths(self):
488+
uneditable_paths = self.tree_view.model().uneditable_paths
489+
disabled_paths = self.tree_view.disabled_editor_paths
490+
491+
uneditable_paths.clear()
492+
disabled_paths.clear()
493+
if self.has_tardis_constraints:
494+
value_idx = self.tree_view_model_class.VALUE_IDX
495+
vary_idx = self.tree_view_model_class.VARY_IDX
496+
497+
# The checkbox is disabled
498+
disabled_paths.append(self.tardis_ip4_y_path + (vary_idx,))
499+
500+
# The value is uneditable
501+
uneditable_paths.append(self.tardis_ip4_y_path + (value_idx,))
502+
503+
# A tree view update is necessary after changing the disabled editors
504+
self.update_tree_view()
505+
506+
@property
507+
def has_tardis_constraints(self):
508+
return self.engineering_constraints == 'TARDIS'
509+
510+
@property
511+
def tardis_ip4_y_path(self) -> tuple[str]:
512+
return (
513+
'detectors',
514+
'IMAGE-PLATE-4',
515+
'transform',
516+
'translation',
517+
'Y',
518+
)
519+
463520
def on_delta_boundaries_toggled(self, b):
464521
# The columns have changed, so we need to reinitialize the tree view
465522
self.reinitialize_tree_view()
@@ -720,6 +777,8 @@ def initialize_tree_view(self):
720777
# Make the key section a little larger
721778
self.tree_view.header().resizeSection(0, 300)
722779

780+
self.update_disabled_editor_paths()
781+
723782
def reinitialize_tree_view(self):
724783
# Keep the same scroll position
725784
scrollbar = self.tree_view.verticalScrollBar()
@@ -790,11 +849,10 @@ def guess_engineering_constraints(instr) -> str | None:
790849
# First guess the instrument type.
791850
instr_type = guess_instrument_type(instr.detectors)
792851

793-
# If it matches one of our expected engineering constraints, use it.
794-
expected_options = [
795-
'TARDIS',
796-
]
797-
if instr_type in expected_options:
798-
return instr_type
852+
if instr_type == 'TARDIS':
853+
# Make sure it contains both image plates
854+
required_detectors = ['IMAGE-PLATE-2', 'IMAGE-PLATE-4']
855+
if all(x in instr.detectors for x in required_detectors):
856+
return instr_type
799857

800858
return None

hexrdgui/calibration/calibration_dialog_callbacks.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,9 @@ def on_reset_relative_params_to_zero_clicked(self):
190190

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

198198
HexrdConfig()._instrument_rigid_body_params = copy.deepcopy(
@@ -243,15 +243,28 @@ def on_constraints_changed(self):
243243
'brute_step',
244244
'user_data',
245245
]
246+
blacklist_params = []
247+
248+
if self.has_tardis_constraints:
249+
# If TARDIS engineering constraints are on, do not remember
250+
# the previous value for the expression.
251+
blacklist_params.append('IMAGE_PLATE_4_tvec_y')
246252

247253
for param_key, param in self.dialog.params_dict.items():
254+
if param_key in blacklist_params:
255+
continue
256+
248257
if param_key in self.calibrator.params:
249258
current = self.calibrator.params[param_key]
250259
for attr in to_remember:
251260
setattr(current, attr, getattr(param, attr))
252261

253262
self.dialog.params_dict = self.calibrator.params
254263

264+
@property
265+
def has_tardis_constraints(self) -> bool:
266+
return self.calibrator.engineering_constraints == 'TARDIS'
267+
255268
def on_run_clicked(self):
256269
self.async_runner.progress_title = 'Running calibration...'
257270
self.async_runner.success_callback = self.on_calibration_finished

hexrdgui/calibration/calibration_runner.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def enable_focus_mode(self, b):
8787
def clear_all_overlay_picks(self):
8888
for overlay in self.active_overlays:
8989
overlay.reset_calibration_picks()
90+
overlay.pad_picks_data()
9091

9192
@property
9293
def overlays(self):
@@ -152,6 +153,7 @@ def use_current_pick_points(self):
152153
def hand_pick_points(self):
153154
overlay = self.active_overlay
154155
overlay.reset_calibration_picks()
156+
overlay.pad_picks_data()
155157

156158
title = overlay.name
157159

@@ -805,6 +807,7 @@ def auto_pick_points(self):
805807
raise NotImplementedError(overlay.type)
806808

807809
overlay.reset_calibration_picks()
810+
overlay.pad_picks_data()
808811
return funcs[overlay.type]()
809812

810813
def auto_pick_powder_points(self):
@@ -978,8 +981,11 @@ def clear_drawn_picks(self):
978981

979982
def on_edit_picks_clicked(self):
980983
dialog = self.edit_picks_dialog
984+
tree_view = dialog.tree_view
985+
model = tree_view.model()
986+
model.disabled_paths.clear()
987+
981988
dialog.button_box_visible = True
982-
dialog.ui.show()
983989

984990
def on_finished():
985991
self.dialog.show()
@@ -991,6 +997,24 @@ def on_finished():
991997
self.draw_picks_on_canvas()
992998
self.dialog.hide()
993999

1000+
# After the tree view is updated, disable paths that
1001+
# don't match this XRS.
1002+
if (
1003+
HexrdConfig().has_multi_xrs and
1004+
not self.showing_picks_from_all_xray_sources
1005+
):
1006+
# Disable paths that don't match this XRS
1007+
for item in model.root_item.child_items:
1008+
overlay_name = item.data(0)
1009+
overlay = Overlay.from_name(overlay_name)
1010+
1011+
if overlay.xray_source != HexrdConfig().active_beam_name:
1012+
model.disabled_paths.append((overlay_name,))
1013+
1014+
tree_view.collapse_disabled_paths()
1015+
1016+
dialog.ui.show()
1017+
9941018
def save_picks_to_file(self, selected_file):
9951019
# Reuse the same logic from the HKLPicksTreeViewDialog
9961020
self.edit_picks_dialog.export_picks(selected_file)

hexrdgui/calibration/hkl_picks_tree_view_dialog.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def dictionary(self, v):
5050

5151
def setup_connections(self):
5252
# Use accepted/rejected so these are called before on_finished()
53-
self.ui.accepted.connect(self.on_finished)
53+
self.ui.accepted.connect(self.on_accepted)
5454
self.ui.rejected.connect(self.on_finished)
5555

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

67+
def on_accepted(self):
68+
self.tree_view.on_accepted()
69+
self.on_finished()
70+
6771
def on_finished(self):
6872
self.tree_view.clear_artists()
6973
self.tree_view.clear_highlights()

hexrdgui/calibration/polarview.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22

33
from skimage.filters.edges import binary_erosion
4-
from skimage.morphology import rectangle
4+
from skimage.morphology import footprint_rectangle
55
from skimage.transform import warp
66

77
from hexrd.transforms.xfcapi import mapAngle
@@ -428,10 +428,10 @@ def apply_snip(self, img):
428428
# NOT done inside snip computation!
429429
if HexrdConfig().polar_apply_erosion:
430430
niter = HexrdConfig().polar_snip1d_numiter
431-
structure = rectangle(
431+
structure = footprint_rectangle((
432432
1,
433433
int(np.ceil(2.25*niter*snip_width_pixels()))
434-
)
434+
))
435435
mask = binary_erosion(~self.raw_img.mask, structure)
436436
img[~mask] = np.nan
437437

hexrdgui/calibration/structureless/runner.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ def draw_picks_on_canvas(self):
499499
def on_edit_picks_clicked(self):
500500
# Convert to polar lines
501501
data = cart_to_polar_lines(self.calibrator_lines, self.instr)
502+
disabled_paths = []
502503

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

514+
if (
515+
not self.showing_picks_from_all_xray_sources and
516+
xray_source != HexrdConfig().active_beam_name
517+
):
518+
disabled_paths.append((xray_source,))
519+
513520
def new_line_name_generator(path):
514521
# Get the x-ray source
515522
xray_source = path[0]
@@ -519,12 +526,14 @@ def new_line_name_generator(path):
519526
dialog = GenericPicksTreeViewDialog(dictionary, canvas=self.canvas,
520527
parent=self.canvas)
521528
dialog.tree_view.new_line_name_generator = new_line_name_generator
529+
dialog.tree_view.model().disabled_paths = disabled_paths
522530
dialog.accepted.connect(self.on_edit_picks_accepted)
523531
dialog.finished.connect(self.on_edit_picks_finished)
524532
dialog.show()
525533

526534
self.edit_picks_dictionary = dictionary
527535
self.edit_picks_dialog = dialog
536+
dialog.tree_view.collapse_disabled_paths()
528537

529538
self.clear_drawn_picks()
530539
self.dialog.hide()

hexrdgui/calibration/tree_item_models.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,27 @@ def set_config_val(self, path, value):
5252

5353
setattr(param, attribute, value)
5454

55+
def data(self, index, role):
56+
if (
57+
role in (Qt.BackgroundRole, Qt.ForegroundRole) and
58+
index.column() in (self.VALUE_IDX, self.VARY_IDX) and
59+
self.has_uneditable_paths
60+
):
61+
# Check if this value is uneditable. If so, gray it out.
62+
item = self.get_item(index)
63+
path = tuple(self.path_to_item(item) + [self.VALUE_IDX])
64+
if path in self.uneditable_paths:
65+
color = 'gray'
66+
if (
67+
index.column() == self.VALUE_IDX and
68+
role == Qt.ForegroundRole
69+
):
70+
color = 'white'
71+
72+
return QColor(color)
73+
74+
return super().data(index, role)
75+
5576

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

6687
VALUE_IDX = COLUMN_INDICES['Value']
88+
VARY_IDX = COLUMN_INDICES['Vary']
6789
MAX_IDX = COLUMN_INDICES['Maximum']
6890
MIN_IDX = COLUMN_INDICES['Minimum']
6991
BOUND_INDICES = (VALUE_IDX, MAX_IDX, MIN_IDX)
@@ -99,6 +121,7 @@ class DeltaCalibrationTreeItemModel(CalibrationTreeItemModel):
99121
COLUMN_INDICES = _tree_columns_to_indices(COLUMNS)
100122

101123
VALUE_IDX = COLUMN_INDICES['Value']
124+
VARY_IDX = COLUMN_INDICES['Vary']
102125
DELTA_IDX = COLUMN_INDICES['Delta']
103126
BOUND_INDICES = (VALUE_IDX, DELTA_IDX)
104127

hexrdgui/hexrd_config.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,19 @@ def get_statuses_instrument_format(self):
14191419
statuses.append(status[i])
14201420
continue
14211421

1422+
if path[0] == 'radius':
1423+
# Special case for radius
1424+
full_path = ['detectors', name] + path
1425+
try:
1426+
status = self.get_instrument_config_val(full_path)
1427+
except KeyError:
1428+
# There must not be a radius. Just skip over it.
1429+
pass
1430+
else:
1431+
statuses.append(status)
1432+
1433+
continue
1434+
14221435
full_path = ['detectors', name] + path
14231436
status = self.get_instrument_config_val(full_path)
14241437

@@ -1674,7 +1687,7 @@ def get_instrument_config_val(self, path):
16741687
except KeyError:
16751688
msg = ('Path: ' + str(path) + '\nwas not found in dict: ' +
16761689
str(self.config['instrument']))
1677-
raise Exception(msg)
1690+
raise KeyError(msg)
16781691

16791692
return cur_val
16801693

@@ -2215,6 +2228,10 @@ def clear_overlay_data(self):
22152228
for overlay in self.overlays:
22162229
overlay.update_needed = True
22172230

2231+
def reset_overlay_calibration_picks(self):
2232+
for overlay in self.overlays:
2233+
overlay.reset_calibration_picks()
2234+
22182235
def flag_overlay_updates_for_active_material(self):
22192236
self.flag_overlay_updates_for_material(self.active_material_name)
22202237

@@ -3045,7 +3062,7 @@ def apply_absorption_correction(self, v):
30453062
@property
30463063
def physics_package_dictified(self):
30473064
if not self.has_physics_package:
3048-
return None
3065+
return {}
30493066

30503067
return self.physics_package.serialize()
30513068

0 commit comments

Comments
 (0)