@@ -708,38 +708,25 @@ def add_item(self, value: Any = None):
708
708
item_container = QWidget ()
709
709
item_layout = QHBoxLayout (item_container )
710
710
711
- # Create widget for item
711
+ # Create widget for item - handle oneOf/anyOf items properly
712
712
item_widget = SchemaWidgetFactory .create_widget (
713
713
item_schema , self .resolver , self .validator , f"{ self .path } [{ index } ]"
714
714
)
715
715
716
- # Set value if provided, with proper type checking
716
+ # Set value if provided
717
717
if value is not None :
718
718
try :
719
- expected_type = item_schema .get ("type" )
720
-
721
- # Handle type conversion based on schema
722
- if expected_type == "object" and not isinstance (value , dict ):
723
- if isinstance (value , bool ):
724
- # Don't try to set boolean values on object widgets
725
- value = {}
726
- else :
727
- value = {}
728
- elif expected_type == "array" and not isinstance (value , list ):
729
- value = []
730
- elif expected_type == "string" and not isinstance (value , str ):
731
- value = str (value ) if not isinstance (value , (dict , list )) else ""
732
- elif expected_type == "boolean" and not isinstance (value , bool ):
733
- value = bool (value ) if not isinstance (value , (dict , list )) else False
734
- elif expected_type in ["integer" , "number" ] and not isinstance (value , (int , float )):
735
- try :
736
- value = float (value ) if expected_type == "number" else int (value )
737
- except (ValueError , TypeError ):
738
- value = 0
739
-
719
+ # For oneOf/anyOf widgets, let them handle the value matching
740
720
item_widget .set_value (value )
741
721
except Exception as e :
742
722
print (f"Warning: Could not set default value { value } : { e } " )
723
+
724
+ # Fallback: try to convert based on schema type
725
+ try :
726
+ converted_value = self ._convert_value_for_schema (value , item_schema )
727
+ item_widget .set_value (converted_value )
728
+ except Exception as e2 :
729
+ print (f"Warning: Could not convert value { value } : { e2 } " )
743
730
744
731
# Remove button
745
732
remove_button = QPushButton ("−" )
@@ -857,10 +844,10 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
857
844
858
845
layout .addWidget (label )
859
846
860
- # Create widget for property - PASS PARENT VALUE PROVIDER for conditionals
847
+ # Create widget for property
861
848
prop_widget = SchemaWidgetFactory .create_widget (
862
849
prop_schema , self .resolver , self .validator , prop_path ,
863
- parent_value_provider = self .get_value # This enables conditional evaluation
850
+ parent_value_provider = lambda : self .get_value ()
864
851
)
865
852
866
853
layout .addWidget (prop_widget )
@@ -947,7 +934,8 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
947
934
# Create widgets for each option
948
935
for i , option in enumerate (options ):
949
936
option_widget = SchemaWidgetFactory .create_widget (
950
- option , self .resolver , self .validator , f"{ path } /oneOf[{ i } ]"
937
+ option , self .resolver , self .validator , f"{ path } /oneOf[{ i } ]" ,
938
+ parent_value_provider = lambda : self .get_value ()
951
939
)
952
940
self .option_widgets .append (option_widget )
953
941
self .stacked_widget .addWidget (option_widget )
@@ -991,16 +979,38 @@ def get_value(self) -> Any:
991
979
return None
992
980
993
981
def set_value (self , value : Any ):
994
- # Try to find which option matches the value
982
+ """Set value by finding the best matching option"""
983
+ best_match_index = 0
984
+ best_match_score = float ('inf' )
985
+
986
+ # Try each option to find the best match
995
987
for i , widget in enumerate (self .option_widgets ):
996
988
try :
989
+ # Test if this widget can handle the value
997
990
widget .set_value (value )
998
991
errors = widget .validate_value ()
999
- if not errors :
992
+ error_score = len (errors )
993
+
994
+ # Prefer options with fewer validation errors
995
+ if error_score < best_match_score :
996
+ best_match_score = error_score
997
+ best_match_index = i
998
+
999
+ # If we find a perfect match, use it immediately
1000
+ if error_score == 0 :
1000
1001
self .selector .setCurrentIndex (i )
1001
1002
return
1002
- except :
1003
+
1004
+ except Exception as e :
1005
+ # This option can't handle the value type
1003
1006
continue
1007
+
1008
+ # Use the best match we found
1009
+ self .selector .setCurrentIndex (best_match_index )
1010
+ try :
1011
+ self .option_widgets [best_match_index ].set_value (value )
1012
+ except :
1013
+ pass
1004
1014
1005
1015
1006
1016
class AnyOfWidget (BaseFormWidget ):
@@ -1038,7 +1048,8 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
1038
1048
1039
1049
checkbox = QCheckBox (title )
1040
1050
option_widget = SchemaWidgetFactory .create_widget (
1041
- option , self .resolver , self .validator , f"{ path } /anyOf[{ i } ]"
1051
+ option , self .resolver , self .validator , f"{ path } /anyOf[{ i } ]" ,
1052
+ parent_value_provider = lambda : self .get_value ()
1042
1053
)
1043
1054
1044
1055
# Initially hidden
@@ -1141,78 +1152,80 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
1141
1152
self ._condition_state = None
1142
1153
self ._current_value = None
1143
1154
1144
- # For object properties, we need the parent to provide context
1145
- if not self .parent_value_provider :
1146
- # Create a default widget (use 'else' if available, otherwise 'then')
1147
- default_schema = self .else_schema if self .else_schema else self .then_schema
1148
- if default_schema :
1149
- self ._create_widget (default_schema )
1150
- else :
1151
- self ._build_ui ()
1152
-
1153
- # Set up timer for live monitoring of parent data
1155
+ # Create initial widget
1156
+ self ._evaluate_and_build ()
1157
+
1158
+ # For object properties, monitor parent changes
1159
+ if self .parent_value_provider :
1154
1160
self .timer = QTimer (self )
1155
- self .timer .setInterval (200 ) # Check every 200ms
1161
+ self .timer .setInterval (100 ) # Check every 100ms
1156
1162
self .timer .timeout .connect (self ._check_condition )
1157
1163
self .timer .start ()
1158
1164
1165
+ def _evaluate_condition (self , data : Any ) -> bool :
1166
+ """Evaluate the if condition against data"""
1167
+ try :
1168
+ if_errors = self .validator .validate (data , self .if_schema )
1169
+ return len (if_errors ) == 0
1170
+ except :
1171
+ return False
1172
+
1159
1173
def _check_condition (self ):
1160
- """Check if condition state has changed and rebuild UI if necessary """
1174
+ """Check if condition state has changed"""
1161
1175
if not self .parent_value_provider :
1162
1176
return
1163
1177
1164
1178
try :
1165
1179
parent_data = self .parent_value_provider ()
1166
- # Evaluate the 'if' condition against parent data
1167
- if_errors = self .validator .validate (parent_data , self .if_schema )
1168
- condition_met = len (if_errors ) == 0
1180
+ condition_met = self ._evaluate_condition (parent_data )
1169
1181
1170
- # Only rebuild if condition state changed
1171
1182
if condition_met != self ._condition_state :
1172
1183
self ._condition_state = condition_met
1173
- self ._build_ui ()
1184
+ self ._evaluate_and_build ()
1174
1185
except Exception as e :
1175
1186
print (f"Error checking condition: { e } " )
1176
1187
1177
- def _build_ui (self ):
1178
- """Build the UI based on current condition state"""
1179
- # Determine which schema to use
1188
+ def _evaluate_and_build (self ):
1189
+ """Evaluate condition and build appropriate UI"""
1190
+ # Determine condition state if we have parent data
1191
+ if self .parent_value_provider :
1192
+ try :
1193
+ parent_data = self .parent_value_provider ()
1194
+ self ._condition_state = self ._evaluate_condition (parent_data )
1195
+ except :
1196
+ self ._condition_state = None
1197
+
1198
+ # Choose schema based on condition
1180
1199
if self ._condition_state is True and self .then_schema :
1181
1200
selected_schema = self .then_schema
1182
1201
elif self ._condition_state is False and self .else_schema :
1183
1202
selected_schema = self .else_schema
1184
- elif self .then_schema :
1185
- # Default to 'then' if no 'else' is specified
1203
+ elif self .then_schema : # Default fallback
1186
1204
selected_schema = self .then_schema
1187
1205
elif self .else_schema :
1188
- # Default to 'else' if no 'then' is specified
1189
1206
selected_schema = self .else_schema
1190
1207
else :
1191
- # No conditional schemas available
1192
- return
1208
+ # Create empty widget
1209
+ selected_schema = { "type" : "object" , "properties" : {}}
1193
1210
1194
1211
self ._create_widget (selected_schema )
1195
1212
1196
1213
def _create_widget (self , schema : Dict [str , Any ]):
1197
1214
"""Create widget for the given schema"""
1198
- # Store current value before switching
1215
+ # Store current value
1199
1216
if self .active_widget :
1200
1217
try :
1201
1218
self ._current_value = self .active_widget .get_value ()
1202
1219
except :
1203
- self . _current_value = None
1220
+ pass
1204
1221
1205
1222
# Remove old widget
1206
1223
self .layout .removeWidget (self .active_widget )
1207
1224
self .active_widget .deleteLater ()
1208
- self .active_widget = None
1209
-
1210
- # Resolve the schema
1211
- resolved_schema = self .resolver .resolve_schema (schema )
1212
1225
1213
1226
# Create new widget
1214
1227
self .active_widget = SchemaWidgetFactory .create_widget (
1215
- resolved_schema , self .resolver , self .validator , self .path ,
1228
+ schema , self .resolver , self .validator , self .path ,
1216
1229
parent_value_provider = self .parent_value_provider
1217
1230
)
1218
1231
@@ -1223,32 +1236,23 @@ def _create_widget(self, schema: Dict[str, Any]):
1223
1236
try :
1224
1237
self .active_widget .set_value (self ._current_value )
1225
1238
except :
1226
- pass # Value not compatible with new widget type
1239
+ pass
1227
1240
1228
- # Connect signals
1229
1241
self .active_widget .valueChanged .connect (self .update_validation )
1230
1242
self .update_validation ()
1231
1243
1232
1244
def get_value (self ) -> Any :
1233
- """Get current value from active widget"""
1234
1245
if self .active_widget :
1235
1246
return self .active_widget .get_value ()
1236
1247
return None
1237
1248
1238
1249
def set_value (self , value : Any ):
1239
- """Set value on active widget"""
1240
1250
self ._current_value = value
1241
1251
if self .active_widget :
1242
1252
try :
1243
1253
self .active_widget .set_value (value )
1244
- except Exception as e :
1245
- print (f"Error setting value on conditional widget: { e } " )
1246
-
1247
- def validate_value (self ) -> List [ValidationError ]:
1248
- """Validate current value"""
1249
- if self .active_widget :
1250
- return self .active_widget .validate_value ()
1251
- return []
1254
+ except :
1255
+ pass
1252
1256
1253
1257
1254
1258
class SchemaWidgetFactory :
0 commit comments