Skip to content

Commit bd1e317

Browse files
committed
Some stuff
1 parent 4afb78e commit bd1e317

File tree

4 files changed

+118
-98
lines changed

4 files changed

+118
-98
lines changed

example_schema.json

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,47 @@
11
{
2-
"$schema": "http://json-schema.org/draft-07/schema#",
3-
"title": "User Profile",
2+
"title": "Pet Registration",
43
"type": "object",
5-
"required": ["name", "age", "email"],
64
"properties": {
7-
"name": {
5+
"pet_type": {
86
"type": "string",
9-
"title": "Full Name"
7+
"enum": ["dog", "cat"],
8+
"title": "Pet Type"
109
},
11-
"age": {
12-
"type": "integer",
13-
"title": "Age",
14-
"minimum": 0
15-
},
16-
"email": {
17-
"type": "string",
18-
"format": "email",
19-
"title": "Email Address"
20-
},
21-
"subscribe": {
22-
"type": "boolean",
23-
"title": "Subscribe to Newsletter"
24-
},
25-
"bio": {
26-
"type": "string",
27-
"title": "Short Bio",
28-
"maxLength": 250
10+
"details": {
11+
"title": "Details",
12+
"if": {
13+
"properties": {
14+
"pet_type": { "const": "dog" }
15+
}
16+
},
17+
"then": {
18+
"properties": {
19+
"breed": {
20+
"type": "string",
21+
"title": "Dog Breed"
22+
},
23+
"trained": {
24+
"type": "boolean",
25+
"title": "Trained?"
26+
}
27+
},
28+
"required": ["breed"]
29+
},
30+
"else": {
31+
"properties": {
32+
"indoor": {
33+
"type": "boolean",
34+
"title": "Indoor Cat?"
35+
},
36+
"color": {
37+
"type": "string",
38+
"title": "Fur Color"
39+
}
40+
},
41+
"required": ["indoor"]
42+
},
43+
"type": "object"
2944
}
30-
}
45+
},
46+
"required": ["pet_type"]
3147
}
41.3 KB
Binary file not shown.
3.17 KB
Binary file not shown.

src/koreui.py

Lines changed: 78 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -708,38 +708,25 @@ def add_item(self, value: Any = None):
708708
item_container = QWidget()
709709
item_layout = QHBoxLayout(item_container)
710710

711-
# Create widget for item
711+
# Create widget for item - handle oneOf/anyOf items properly
712712
item_widget = SchemaWidgetFactory.create_widget(
713713
item_schema, self.resolver, self.validator, f"{self.path}[{index}]"
714714
)
715715

716-
# Set value if provided, with proper type checking
716+
# Set value if provided
717717
if value is not None:
718718
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
740720
item_widget.set_value(value)
741721
except Exception as e:
742722
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}")
743730

744731
# Remove button
745732
remove_button = QPushButton("−")
@@ -857,10 +844,10 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
857844

858845
layout.addWidget(label)
859846

860-
# Create widget for property - PASS PARENT VALUE PROVIDER for conditionals
847+
# Create widget for property
861848
prop_widget = SchemaWidgetFactory.create_widget(
862849
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()
864851
)
865852

866853
layout.addWidget(prop_widget)
@@ -947,7 +934,8 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
947934
# Create widgets for each option
948935
for i, option in enumerate(options):
949936
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()
951939
)
952940
self.option_widgets.append(option_widget)
953941
self.stacked_widget.addWidget(option_widget)
@@ -991,16 +979,38 @@ def get_value(self) -> Any:
991979
return None
992980

993981
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
995987
for i, widget in enumerate(self.option_widgets):
996988
try:
989+
# Test if this widget can handle the value
997990
widget.set_value(value)
998991
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:
10001001
self.selector.setCurrentIndex(i)
10011002
return
1002-
except:
1003+
1004+
except Exception as e:
1005+
# This option can't handle the value type
10031006
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
10041014

10051015

10061016
class AnyOfWidget(BaseFormWidget):
@@ -1038,7 +1048,8 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
10381048

10391049
checkbox = QCheckBox(title)
10401050
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()
10421053
)
10431054

10441055
# Initially hidden
@@ -1141,78 +1152,80 @@ def __init__(self, schema: Dict[str, Any], resolver: SchemaResolver, validator:
11411152
self._condition_state = None
11421153
self._current_value = None
11431154

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:
11541160
self.timer = QTimer(self)
1155-
self.timer.setInterval(200) # Check every 200ms
1161+
self.timer.setInterval(100) # Check every 100ms
11561162
self.timer.timeout.connect(self._check_condition)
11571163
self.timer.start()
11581164

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+
11591173
def _check_condition(self):
1160-
"""Check if condition state has changed and rebuild UI if necessary"""
1174+
"""Check if condition state has changed"""
11611175
if not self.parent_value_provider:
11621176
return
11631177

11641178
try:
11651179
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)
11691181

1170-
# Only rebuild if condition state changed
11711182
if condition_met != self._condition_state:
11721183
self._condition_state = condition_met
1173-
self._build_ui()
1184+
self._evaluate_and_build()
11741185
except Exception as e:
11751186
print(f"Error checking condition: {e}")
11761187

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
11801199
if self._condition_state is True and self.then_schema:
11811200
selected_schema = self.then_schema
11821201
elif self._condition_state is False and self.else_schema:
11831202
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
11861204
selected_schema = self.then_schema
11871205
elif self.else_schema:
1188-
# Default to 'else' if no 'then' is specified
11891206
selected_schema = self.else_schema
11901207
else:
1191-
# No conditional schemas available
1192-
return
1208+
# Create empty widget
1209+
selected_schema = {"type": "object", "properties": {}}
11931210

11941211
self._create_widget(selected_schema)
11951212

11961213
def _create_widget(self, schema: Dict[str, Any]):
11971214
"""Create widget for the given schema"""
1198-
# Store current value before switching
1215+
# Store current value
11991216
if self.active_widget:
12001217
try:
12011218
self._current_value = self.active_widget.get_value()
12021219
except:
1203-
self._current_value = None
1220+
pass
12041221

12051222
# Remove old widget
12061223
self.layout.removeWidget(self.active_widget)
12071224
self.active_widget.deleteLater()
1208-
self.active_widget = None
1209-
1210-
# Resolve the schema
1211-
resolved_schema = self.resolver.resolve_schema(schema)
12121225

12131226
# Create new widget
12141227
self.active_widget = SchemaWidgetFactory.create_widget(
1215-
resolved_schema, self.resolver, self.validator, self.path,
1228+
schema, self.resolver, self.validator, self.path,
12161229
parent_value_provider=self.parent_value_provider
12171230
)
12181231

@@ -1223,32 +1236,23 @@ def _create_widget(self, schema: Dict[str, Any]):
12231236
try:
12241237
self.active_widget.set_value(self._current_value)
12251238
except:
1226-
pass # Value not compatible with new widget type
1239+
pass
12271240

1228-
# Connect signals
12291241
self.active_widget.valueChanged.connect(self.update_validation)
12301242
self.update_validation()
12311243

12321244
def get_value(self) -> Any:
1233-
"""Get current value from active widget"""
12341245
if self.active_widget:
12351246
return self.active_widget.get_value()
12361247
return None
12371248

12381249
def set_value(self, value: Any):
1239-
"""Set value on active widget"""
12401250
self._current_value = value
12411251
if self.active_widget:
12421252
try:
12431253
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
12521256

12531257

12541258
class SchemaWidgetFactory:

0 commit comments

Comments
 (0)