查看: 208|回复: 0

Python+Slint实现JSON编辑器:校验格式化与树形视图

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
原文给出一个基于 Python 与 Slint 的 JSON 编辑助手源码,核心不是界面展示,而是 JSON 校验、格式化、压缩、树形结构构建与 Slint 属性绑定、回调机制的配合。下面按数据流、核心函数、UI 联动、错误定位和兼容性整理实现要点,适合作为中小型 JSON 调试工具、脚本辅助编辑器或桌面端开发练习的参考。

一、整体数据流
Slint 侧 MainWindow 定义 in-out property:json-text、char-count、status、tree-nodes,并声明 validate-json、format-json、minify-json、clear-all、text-changed 回调。Python 侧通过 slint.load_file 加载 main.slint,取得 MainWindow 并实例化,初始化 status 为就绪状态,error-line 和 error-col 为 -1,tree-nodes 先赋值为 slint.ListModel([]),随后尝试用默认 json-text 构建树节点。

编辑区 TextInput 使用 text <=> root.json-text 双向绑定,edited 时触发 text-changed。Python 端把 on_text_changed 赋给 ui.text_changed,文本变化后更新 json_text、char_count,并调用 validate_json 校验。校验成功时,json.loads 得到对象,再调用 build_tree 生成扁平节点列表,最后包装成 slint.ListModel 赋给 ui.tree_nodes,Slint 的 for node[idx] in root.tree-nodes 负责渲染。按钮点击则走 Slint callback,进入 Python 端 on_validate、on_format、on_minify、on_clear 等函数。

二、JSON 处理函数设计
validate_json 返回四元组 success、error_msg、line、col。空文本返回 False、JSON 为空、1、1;json.loads 成功返回 True、JSON 格式正确、-1、-1;捕获 json.JSONDecodeError 时返回 False、解析错误消息、e.lineno、e.colno;其他异常返回 False、未知错误、1、1。其中 -1 表示没有错误位置,Slint 侧只有在 error-line >= 0 时才显示行列信息。
  1. def validate_json(text):
  2.     if not text.strip():
  3.         return False, 'JSON 为空', 1, 1
  4.     try:
  5.         json.loads(text)
  6.         return True, 'JSON 格式正确', -1, -1
  7.     except json.JSONDecodeError as e:
  8.         return False, f'解析错误: {e.msg}', e.lineno, e.colno
  9.     except Exception as e:
  10.         return False, f'未知错误: {str(e)}', 1, 1
复制代码

format_json 先 json.loads,再用 json.dumps(obj, indent=indent, ensure_ascii=False) 美化,indent 默认 2;失败时返回无法格式化消息并带 e.lineno。minify_json 同样先解析,再用 json.dumps(obj, separators=(',', ':'), ensure_ascii=False) 压缩空白;失败时返回无法压缩消息并带行号。ensure_ascii=False 让中文保持原样,separators 控制压缩后的键值分隔符。

get_type_name 负责把 Python 值映射成 null、boolean、number、string、array、object、unknown。判断顺序中 bool 必须放在 int/float 之前,否则 True 会被当成 number。get_preview 对 None 返回 null,对 bool 返回 true 或 false,对字符串超过 max_len=30 时截断并追加省略号,对 dict/list 返回空字符串,避免复杂类型在树节点中展示过长预览。

三、树形节点的扁平化构建
build_tree 递归返回扁平节点列表,每个节点包含 key、value-type、value-preview、depth、child-count。对象遍历 items,数组用 enumerate 生成 形式的 key;遇到 dict 或 list 子值时递归 depth + 1;child-count 只在子值是 dict/list 时取 len(v),标量为 0。Slint 不需要维护父子索引,而是通过 node.depth 乘以 16px 再加 8px 的 padding-left 表达层级。
  1. def build_tree(obj, key='', depth=0):
  2.     nodes = []
  3.     if isinstance(obj, dict):
  4.         for k, v in obj.items():
  5.             child_count = len(v) if isinstance(v, (dict, list)) else 0
  6.             nodes.append({
  7.                 'key': k,
  8.                 'value-type': get_type_name(v),
  9.                 'value-preview': get_preview(v),
  10.                 'depth': depth,
  11.                 'child-count': child_count,
  12.             })
  13.             if isinstance(v, (dict, list)):
  14.                 nodes.extend(build_tree(v, k, depth + 1))
  15.     elif isinstance(obj, list):
  16.         for i, v in enumerate(obj):
  17.             child_count = len(v) if isinstance(v, (dict, list)) else 0
  18.             nodes.append({
  19.                 'key': f'[{i}]',
  20.                 'value-type': get_type_name(v),
  21.                 'value-preview': get_preview(v),
  22.                 'depth': depth,
  23.                 'child-count': child_count,
  24.             })
  25.             if isinstance(v, (dict, list)):
  26.                 nodes.extend(build_tree(v, f'[{i}]', depth + 1))
  27.     return nodes
复制代码

四、Slint 侧联动与回调绑定
Slint 侧定义 TreeNode 和 StatusInfo 结构,TreeNode 对应树节点字段,StatusInfo 对应 message、is-error、error-line、error-col。ActionButton 组件封装按钮文字、背景色、悬停色和 clicked 回调;TreeNodeRow 根据 node.value-type 选择符号和颜色,显示 key、value-preview 和 child-count,并用 is-last 控制最后一行分隔线。

Python 端创建 MainWindow 后,把函数对象直接赋给 ui.validate_json、ui.format_json、ui.minify_json、ui.clear_all、ui.text_changed。on_format 成功时把格式化结果写回 ui.json_text,更新 char_count,status 设为格式化成功,并尝试重新 json.loads 后刷新树节点;失败时 status.is-error 为 True,message 为错误结果。on_minify 成功时写回压缩结果并更新字符数,但原文代码没有在压缩成功后重新构建树节点。on_clear 清空文本、字符数、状态和 tree_nodes。

on_text_changed 是输入时的主链路:更新 ui.json_text 与 ui.char_count;文本非空时调用 validate_json;成功则 json.loads 并重新 build_tree,失败则只更新状态,不强制清空已有树节点。on_validate 按钮成功时会刷新树节点,失败时保留现有树节点。状态反馈区根据 is-error 切换颜色和符号,底部只有在 error-line >= 0 时显示错误位置。

五、错误定位与排查流程
排查时先看 status.message,再看控制台输出的 [验证]、[格式化]、[压缩]、[清空] 日志。空输入会得到 JSON 为空,行列均为 1;解析失败会使用 JSONDecodeError 的 lineno 和 colno,适合直接定位到编辑区行列;格式化或压缩失败只返回带行号的消息,error-line 为 -1,因此 UI 不会显示行列。若 main.slint 编译失败,main.py 会打印 e.args[1] 中的诊断信息并退出,应先检查 slint.load_file 的路径和 main.slint 语法。默认树构建、格式化后刷新树等位置存在 try/except pass,失败时界面不一定报错,复现问题时要同时查看 Python 控制台。

六、性能与兼容性细节
当前实现每次 text-changed 都会完整执行 json.loads、validate_json、build_tree,并重新创建 slint.ListModel;输入频繁或 JSON 很大时,重复解析和全量建树会放大开销。基于这一实现可考虑输入防抖、只在需要时重建树、限制超大数组或对象的展示量、保留字符串预览上限 max_len=30。依赖方面,代码使用 Python 标准库 json、pathlib 和 Slint Python 绑定,main.slint 与 main.py 放在同一目录,通过 Path(__file__).parent.resolve() 定位资源。Slint 侧 ListModel 接收节点列表,TextInput 双向绑定 json-text,ScrollView 与 Flickable 分别承载编辑区和树形区。原文未给出安装命令和版本号,复现时应按本机 Slint Python 绑定环境调整。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-17 13:23 , Processed in 0.032338 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部