@@ -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
0 commit comments