feat(EDID-Viewer): v0.1.0 入库 — Fluent EDID 查看器,目录对齐 DLPilot

- src/ 人写代码:main.py(Fluent 界面、Light/Dark/dark1/dark2/跟随系统主题、
  关于按钮;主题三件套 = setTheme + QPalette/应用级 QSS + DWM 标题栏,
  修复 Light 下窗口背景/页签/标题栏仍为系统深色的问题)、edid.py
  (EDID/CTA/HDMI/HDR 解析)、windows_edid.py(WMI 只读)、theme.py、
  test_edid.py(9 项解析测试,含随机畸形数据无崩溃检查)
- assets/ 图标与生成脚本;release/ 交付:EDID-Viewer.exe(onefile 63.7MB,
  启动/图标/主题已验证)+ README,与 DLPilot/release 同约定 exe 直接入库
- 流水线对齐 DLPilot:src/build_exe.py(spec 由脚本生成不入库,剔除 PATH 上
  Poppler/Conda 版本化 ICU,避免 QtCore 报 WinError 127)→
  package_release.py(组装 EDID-Viewer-v*-win64.zip,zip 只上传 Gitea)→
  publish_gitea.py(token 走 GITEA_TOKEN,自签证书 --insecure)
- agent.md 更名 AGENTS.md(DLPilot 双文档约定:README 给人 / AGENTS.md 给
  Agent);根 .gitignore 增加 EDID-viewer/release/*.zip
- validation/ 保留 EDID 本机/合成样本与窗口截图、关于对话框自检脚本
This commit is contained in:
2026-09-24 22:34:02 +08:00
parent b58cdc42b6
commit fe4bca5a33
25 changed files with 1468 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
import random
import unittest
from edid import parse,dtd
def checksum(b):
b[127]=(-sum(b[:127]))&255
return bytes(b)
def base(ext=0):
b=bytearray(128); b[:8]=bytes.fromhex('00ffffffffffff00'); b[8:10]=bytes.fromhex('4c2d')
b[18:20]=bytes((1,4)); b[20]=0xa2; b[38:54]=b'\x01\x01'*8; b[126]=ext
b[54:72]=bytes.fromhex('023a801871382d40582c4500fd1e1100001e')
b[72:90]=b'\0\0\0\xfc\0TEST PANEL\n '
return bytearray(checksum(b))
class Tests(unittest.TestCase):
def test_identity_and_dtd(self):
r=parse(base()); self.assertEqual(r['基本信息']['显示器名称'],'TEST PANEL')
self.assertEqual(r['基本信息']['厂家 PNP 代码'],'SAM')
t=r['时序'][0]; self.assertEqual(t['分辨率'],'1920×1080p'); self.assertEqual(t['刷新率'],'60.000 Hz')
self.assertEqual(t['同步'],'H+ V+'); self.assertEqual(t['水平有效/前肩/同步/后肩'],[1920,88,44,148])
def test_standard(self):
b=base(); b[38:40]=bytes((129,192)); r=parse(checksum(b))
self.assertEqual(r['时序'][0]['分辨率'],'1280×720'); self.assertEqual(r['时序'][0]['刷新率'],'60 Hz')
def test_cta(self):
b=bytearray(128); payload=bytes.fromhex('429061 23090707 67030c0012343078 e3060c01')
b[:4]=bytes((2,3,4+len(payload),0x70)); b[4:4+len(payload)]=payload
r=parse(bytes(base(1))+checksum(b)); text=str(r)
self.assertIn('1.2.3.4',text); self.assertIn('PQ / ST 2084',text); self.assertIn('3840×2160p 60 Hz',text)
self.assertIn('600 MHz',text); self.assertIn('LPCM',text)
def test_missing_extension(self):
self.assertTrue(parse(base(2))['警告'])
def test_bad_checksum_and_header(self):
b=base(); b[0]=1; r=parse(b); self.assertTrue(r['警告']); self.assertIn('失败',r['校验'][0])
def test_bounds(self):
with self.assertRaises(ValueError): parse(b'\0'*127)
b=bytearray(128); b[:5]=bytes((2,3,6,0,0x5f))
self.assertIn('越界',str(parse(bytes(base(1))+checksum(b))['警告']))
def test_unknown_extension(self):
b=bytearray(128); b[0]=0x70
self.assertIn('暂未解码',str(parse(bytes(base(1))+checksum(b))))
def test_interlaced(self):
b=bytes.fromhex('011d8018711c1620582c2500fd1e1100009e')
t=dtd(b,'test'); self.assertEqual(t['分辨率'],'1920×1080i'); self.assertEqual(t['刷新率'],'60.000 Hz(场率)')
def test_random_malformed(self):
rng=random.Random(42)
for _ in range(1000):
b=bytearray(rng.randbytes(rng.randint(128,600))); b[18]=1; parse(b)
if __name__=='__main__': unittest.main()