feat(devtool): ImageAnnotation 新增 description 字段

用于给标注添加描述,描述文本会进入 R.py 的注释中
This commit is contained in:
XcantloadX 2025-03-02 10:16:10 +08:00
parent 79927a7b7a
commit 425057d3ef
5 changed files with 44 additions and 10 deletions

View File

@ -13,6 +13,10 @@ interface PropertyRenderInputOptions {
checkbox: {
value: boolean,
onChange: (value: boolean) => void
},
'long-text': {
value: string,
onChange: (value: string) => void
}
}
type RenderType = keyof PropertyRenderInputOptions;
@ -70,6 +74,18 @@ const PropertyContent = styled.div`
min-height: 32px;
display: flex;
align-items: center;
textarea {
width: 100%;
min-height: 80px;
resize: vertical;
padding: 4px;
}
input[type="text"] {
width: 100%;
padding: 4px;
}
`;
const FullWidthPropertyContent = styled(PropertyContent)`
@ -143,6 +159,9 @@ const PropertyGrid: React.FC<PropertyGridProps> = ({ properties, titleColumnWidt
} else if (type === 'checkbox') {
const propertyCheckbox = property.render as PropertyRenderInputOptions['checkbox'];
field = <input type="checkbox" checked={propertyCheckbox.value} onChange={(e) => propertyCheckbox.onChange(e.target.checked)} />;
} else if (type === 'long-text') {
const propertyLongText = property.render as PropertyRenderInputOptions['long-text'];
field = <textarea value={propertyLongText.value} onChange={(e) => propertyLongText.onChange(e.target.value)} />;
}
} else {
console.error('Invalid property render type:', property.render);

View File

@ -9,6 +9,8 @@ export interface Definition {
name: string;
/** 显示在调试器与调试输出中的名称 */
displayName: string;
/** 描述信息 */
description: string;
type: DefinitionType;
/** 标注 ID */
annotationId: string;
@ -52,8 +54,6 @@ function toString(data: ImageMetaData): string {
function useImageMetaData(data?: ImageMetaData) {
const [imageMetaData, updateImageMetaData] = useImmer<ImageMetaData>(data || {
definitions: {},
annotations: [],
});

View File

@ -235,6 +235,15 @@ const usePropertyGridData = (
onChange: (value: string) => onDefinitionChange?.(selectedAnnotation.id, { displayName: value }),
}
},
{
title: '描述',
render: {
type: 'long-text',
required: false,
value: definition.description || '',
onChange: (value: string) => onDefinitionChange?.(selectedAnnotation.id, { description: value }),
}
},
{
title: '类型',
render: () => definition.type,

View File

@ -1 +1 @@
{"definitions":{"be6836bd-ee42-432b-9166-469c74f32f0b":{"name":"InPurodyuusu.BoxWeeksUntilExam","displayName":"培育中下次考试剩余周","type":"hint-box","annotationId":"be6836bd-ee42-432b-9166-469c74f32f0b","useHintRect":false}},"annotations":[{"id":"be6836bd-ee42-432b-9166-469c74f32f0b","type":"rect","data":{"x1":11,"y1":8,"x2":237,"y2":196}}]}
{"definitions":{"be6836bd-ee42-432b-9166-469c74f32f0b":{"name":"InPurodyuusu.BoxWeeksUntilExam","displayName":"考试剩余周","type":"hint-box","annotationId":"be6836bd-ee42-432b-9166-469c74f32f0b","useHintRect":false,"description":"培育中左上角的下次考试剩余周数"}},"annotations":[{"id":"be6836bd-ee42-432b-9166-469c74f32f0b","type":"rect","data":{"x1":11,"y1":8,"x2":237,"y2":196}}]}

View File

@ -6,7 +6,7 @@ import shutil
import uuid
import jinja2
import argparse
from typing import Any, TypeGuard, Literal, Union, cast
from typing import Any, Optional, TypeGuard, Literal, Union, cast
from dataclasses import dataclass
from dataclasses_json import dataclass_json, DataClassJsonMixin
@ -22,6 +22,8 @@ SpriteType = Literal['basic', 'metadata']
class Resource:
type: Literal['template', 'hint-box', 'hint-point']
data: 'Sprite | HintBox | HintPoint'
description: str
"""资源的描述信息"""
@dataclass
class Sprite:
@ -102,6 +104,8 @@ class Definition(DataClassJsonMixin):
"""标注类型"""
annotationId: str
"""标注 ID"""
description: Optional[str] = None
"""描述信息"""
class TemplateDefinition(Definition):
"""模板匹配类型的资源定义"""
@ -166,7 +170,7 @@ def load_metadata(root_path: str, png_file: str) -> list[Resource]:
"""加载 metadata 类型的标注"""
json_path = png_file + '.json'
with open(json_path, 'r', encoding='utf-8') as f:
metadata = SpriteMetadata.from_json(f.read()) # 使用 dataclass-json 的解析方法
metadata = SpriteMetadata.from_json(f.read())
# 遍历标注,裁剪、保存图片
clips: dict[str, str] = {} # id -> 文件路径
image = cv2.imread(png_file)
@ -196,12 +200,11 @@ def load_metadata(root_path: str, png_file: str) -> list[Resource]:
name=definition.name.split('.')[-1],
display_name=definition.displayName,
class_path=definition.name.split('.')[:-1],
rel_path=png_file,
abs_path=os.path.abspath(clips[definition.annotationId]),
origin_file=os.path.abspath(png_file),
)
resources.append(Resource('template', spr))
resources.append(Resource('template', spr, definition.description or ''))
elif definition.type == 'hint-box':
annotation = query_annotation(metadata.annotations, definition.annotationId)
rect = annotation.data
@ -216,7 +219,7 @@ def load_metadata(root_path: str, png_file: str) -> list[Resource]:
y2=rect.y2,
origin_file=os.path.abspath(png_file),
)
resources.append(Resource('hint-box', hb))
resources.append(Resource('hint-box', hb, definition.description or ''))
elif definition.type == 'hint-point':
annotation = query_annotation(metadata.annotations, definition.annotationId)
pt = annotation.data
@ -229,7 +232,7 @@ def load_metadata(root_path: str, png_file: str) -> list[Resource]:
class_path=definition.name.split('.')[:-1],
origin_file=os.path.abspath(png_file),
)
resources.append(Resource('hint-point', hp))
resources.append(Resource('hint-point', hp, definition.description or ''))
else:
raise ValueError(f'Unknown definition type: {definition.type}')
@ -250,7 +253,7 @@ def load_basic_sprite(root_path: str, png_file: str) -> Resource:
abs_path=os.path.abspath(png_file),
origin_file=os.path.abspath(png_file)
)
return Resource('template', spr)
return Resource('template', spr, "")
def load_sprites(root_path: str, png_files: list[str]) -> list[Resource]:
""""""
@ -322,6 +325,7 @@ def make_classes(resources: list[Resource], output_path: str) -> list[OutputClas
height = ''
docstring = (
f"名称:{sprite.display_name}\\n\n"
f"描述:{resource.description}\\n\n"
f"路径:{escape(sprite.rel_path)}\\n\n"
f"模块:`{'.'.join(sprite.class_path)}`\\n\n"
f'<img src="vscode-file://vscode-app/{escape(sprite.abs_path)}" title="{sprite.display_name}" />\\n\n'
@ -353,6 +357,7 @@ def make_classes(resources: list[Resource], output_path: str) -> list[OutputClas
docstring = (
f"名称:{hint_box.display_name}\\n\n"
f"描述:{resource.description}\\n\n"
f"模块:`{'.'.join(hint_box.class_path)}`\\n\n"
f"x1={hint_box.x1}, y1={hint_box.y1}, x2={hint_box.x2}, y2={hint_box.y2}\\n\n"
f"裁剪区域:\\n\n"
@ -377,6 +382,7 @@ def make_classes(resources: list[Resource], output_path: str) -> list[OutputClas
assert isinstance(hint_point, HintPoint)
docstring = (
f"名称:{hint_point.display_name}\\n\n"
f"描述:{resource.description}\\n\n"
f"模块:`{'.'.join(hint_point.class_path)}`\\n\n"
f"坐标:(x={hint_point.x}, y={hint_point.y})\\n\n"
)