From 7de49feb663c9c48f9733f1301c6e440c864900d Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 9 Jan 2026 13:39:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(annotation):=20=E4=BC=98=E5=8C=96=E6=A0=87?= =?UTF-8?q?=E6=B3=A8=E6=A8=A1=E6=9D=BF=E7=94=9F=E6=88=90=E5=92=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加文本对象类型集合用于模板类型判断 - 将XML生成部分拆分为对象和控件两个独立部分 - 为文本类模板添加响应式布局支持长文本标注 - 修复配置验证器中对象和控件查找逻辑 - 优化标签控件在长文本场景下的显示位置 --- .../app/module/annotation/service/template.py | 33 +++++++++++++++---- .../annotation/utils/config_validator.py | 4 +-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/runtime/datamate-python/app/module/annotation/service/template.py b/runtime/datamate-python/app/module/annotation/service/template.py index 61abeb5..9e36950 100644 --- a/runtime/datamate-python/app/module/annotation/service/template.py +++ b/runtime/datamate-python/app/module/annotation/service/template.py @@ -36,7 +36,7 @@ class AnnotationTemplateService: """ tag_config = LabelStudioTagConfig() control_types = tag_config.get_control_types() - xml_parts = [''] + text_object_types = {"Text", "Paragraphs", "ParagraphLabels", "HyperText", "Markdown"} def normalize_control_type(raw: Optional[str]) -> str: if not raw: @@ -49,15 +49,19 @@ class AnnotationTemplateService: return ct return raw + is_text_template = any((obj.type in text_object_types) for obj in config.objects) + # 生成对象定义 + object_parts: List[str] = [] for obj in config.objects: obj_attrs = [ f'name="{obj.name}"', f'value="{obj.value}"' ] - xml_parts.append(f' <{obj.type} {" ".join(obj_attrs)}/>') + object_parts.append(f' <{obj.type} {" ".join(obj_attrs)}/>') # 生成标签定义 + control_parts: List[str] = [] for label in config.labels: label_attrs = [f'name="{label.from_name}"', f'toName="{label.to_name}"'] @@ -74,7 +78,7 @@ class AnnotationTemplateService: # 检查是否需要子元素 if label.options or label.labels: choices = label.options or label.labels or [] - xml_parts.append(f' <{tag_type} {" ".join(label_attrs)}>') + control_parts.append(f' <{tag_type} {" ".join(label_attrs)}>') # 从配置获取子元素标签名 child_tag = tag_config.get_child_tag(tag_type) @@ -83,12 +87,29 @@ class AnnotationTemplateService: child_tag = "Label" for choice in choices: - xml_parts.append(f' <{child_tag} value="{choice}"/>') - xml_parts.append(f' ') + control_parts.append(f' <{child_tag} value="{choice}"/>') + control_parts.append(f' ') else: # 处理简单标签类型(不需要子元素) - xml_parts.append(f' <{tag_type} {" ".join(label_attrs)}/>') + control_parts.append(f' <{tag_type} {" ".join(label_attrs)}/>') + if is_text_template: + # 长文本优化:将标签控件固定在右侧(sticky),避免需要滚动到页面底部才能看到控件 + xml_parts = [ + '', + ' ', + *object_parts, + ' ', + ' ', + *control_parts, + ' ', + '', + ] + return '\n'.join(xml_parts) + + xml_parts = [''] + xml_parts.extend(object_parts) + xml_parts.extend(control_parts) xml_parts.append('') return '\n'.join(xml_parts) diff --git a/runtime/datamate-python/app/module/annotation/utils/config_validator.py b/runtime/datamate-python/app/module/annotation/utils/config_validator.py index 2612cd6..f818fa3 100644 --- a/runtime/datamate-python/app/module/annotation/utils/config_validator.py +++ b/runtime/datamate-python/app/module/annotation/utils/config_validator.py @@ -35,13 +35,13 @@ class LabelStudioConfigValidator: # 检查是否有对象定义 object_types = config.get_object_types() - objects = [child for child in root if child.tag in object_types] + objects = [elem for elem in root.iter() if elem is not root and elem.tag in object_types] if not objects: return False, "No data objects (Image, Text, etc.) found" # 检查是否有控件定义 control_types = config.get_control_types() - controls = [child for child in root if child.tag in control_types] + controls = [elem for elem in root.iter() if elem is not root and elem.tag in control_types] if not controls: return False, "No annotation controls found"