Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43d78de045 | ||
|
|
739fdeedf3 | ||
|
|
b7eda91ca7 | ||
|
|
763d80534c | ||
|
|
31fcd93607 | ||
|
|
df6a5f4343 | ||
|
|
32d0d26c5a | ||
|
|
afb6513e56 | ||
|
|
584fd32ebe |
@@ -8,7 +8,6 @@ dist/
|
|||||||
*.spec
|
*.spec
|
||||||
|
|
||||||
# 发布 zip: 可由 src/package_release.py 重新生成, 只上传 Gitea Release
|
# 发布 zip: 可由 src/package_release.py 重新生成, 只上传 Gitea Release
|
||||||
HDMI-Tool/release/*.zip
|
|
||||||
DLPilot/release/*.zip
|
DLPilot/release/*.zip
|
||||||
|
|
||||||
# ZCode 客户端本地会话数据(计划留档等), 非项目配置
|
# ZCode 客户端本地会话数据(计划留档等), 非项目配置
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ DLPilot/
|
|||||||
1. 左上显示器下拉选择目标屏(多屏时注意别选成主屏),"刷新"重新枚举;
|
1. 左上显示器下拉选择目标屏(多屏时注意别选成主屏),"刷新"重新枚举;
|
||||||
2. **切分辨率**:"支持的模式"卡片里选模式 → "应用";测完点"恢复";
|
2. **切分辨率**:"支持的模式"卡片里选模式 → "应用";测完点"恢复";
|
||||||
3. **投测试图**:"测试图案"卡片先选**测试卡区域**(640x360 / 800x600 / full)→
|
3. **投测试图**:"测试图案"卡片先选**测试卡区域**(640x360 / 800x600 / full)→
|
||||||
点图案按钮全屏投放,按 **ESC** 或点击退出;R/G/B 纯色按钮(55/AA/FF,按通道着色)
|
点图案按钮全屏投放(新图案自动替换旧图案),按 **ESC** 或点"停止测试图像"退出;
|
||||||
|
R/G/B 纯色按钮(55/AA/FF,按通道着色)
|
||||||
用于 RGB 位交换类测试;自定义图片点齿轮按钮配置(图片自动复制到 exe 旁),
|
用于 RGB 位交换类测试;自定义图片点齿轮按钮配置(图片自动复制到 exe 旁),
|
||||||
"校准图案"一排为 3D 光固化测试/校准图预留槽位;
|
"校准图案"一排为 3D 光固化测试/校准图预留槽位;
|
||||||
4. **光机控制**:右列选串口与参数(波特率/数据位/校验/停止位,默认 115200 8-N-1)→ 连接 →
|
4. **光机控制**:右列选串口与参数(波特率/数据位/校验/停止位,默认 115200 8-N-1)→ 连接 →
|
||||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -61,7 +61,8 @@ DLPilot/
|
|||||||
1. 左上显示器下拉选择目标屏(多屏时注意别选成主屏),"刷新"重新枚举;
|
1. 左上显示器下拉选择目标屏(多屏时注意别选成主屏),"刷新"重新枚举;
|
||||||
2. **切分辨率**:"支持的模式"卡片里选模式 → "应用";测完点"恢复";
|
2. **切分辨率**:"支持的模式"卡片里选模式 → "应用";测完点"恢复";
|
||||||
3. **投测试图**:"测试图案"卡片先选**测试卡区域**(640x360 / 800x600 / full)→
|
3. **投测试图**:"测试图案"卡片先选**测试卡区域**(640x360 / 800x600 / full)→
|
||||||
点图案按钮全屏投放,按 **ESC** 或点击退出;R/G/B 纯色按钮(55/AA/FF,按通道着色)
|
点图案按钮全屏投放(新图案自动替换旧图案),按 **ESC** 或点"停止测试图像"退出;
|
||||||
|
R/G/B 纯色按钮(55/AA/FF,按通道着色)
|
||||||
用于 RGB 位交换类测试;自定义图片点齿轮按钮配置(图片自动复制到 exe 旁),
|
用于 RGB 位交换类测试;自定义图片点齿轮按钮配置(图片自动复制到 exe 旁),
|
||||||
"校准图案"一排为 3D 光固化测试/校准图预留槽位;
|
"校准图案"一排为 3D 光固化测试/校准图预留槽位;
|
||||||
4. **光机控制**:右列选串口与参数(波特率/数据位/校验/停止位,默认 115200 8-N-1)→ 连接 →
|
4. **光机控制**:右列选串口与参数(波特率/数据位/校验/停止位,默认 115200 8-N-1)→ 连接 →
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -34,7 +34,7 @@ BOLD = ('Microsoft YaHei UI', 13, 'bold')
|
|||||||
TITLE_FONT = ('Microsoft YaHei UI', 18, 'bold')
|
TITLE_FONT = ('Microsoft YaHei UI', 18, 'bold')
|
||||||
|
|
||||||
APP_NAME = 'DLPilot'
|
APP_NAME = 'DLPilot'
|
||||||
APP_VERSION = '0.3.0'
|
APP_VERSION = '0.3.2'
|
||||||
REPO_URL = 'https://gitea.hulk.wang/hulk/LT8619C_Debug_Tools'
|
REPO_URL = 'https://gitea.hulk.wang/hulk/LT8619C_Debug_Tools'
|
||||||
|
|
||||||
|
|
||||||
@@ -67,7 +67,8 @@ def run_gui(auto_close=0):
|
|||||||
class App(ctk.CTk):
|
class App(ctk.CTk):
|
||||||
def __init__(self, auto_close=0):
|
def __init__(self, auto_close=0):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.title('DLPilot — 3D 光固化光机调试台 (EDID · 测试图案 · RS485 光机控制)')
|
self.title(f'DLPilot v{APP_VERSION} — 3D 光固化光机调试台 '
|
||||||
|
f'(EDID · 测试图案 · RS485 光机控制)')
|
||||||
try: # 任务栏独立分组
|
try: # 任务栏独立分组
|
||||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('hulk.DLPilot')
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('hulk.DLPilot')
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -94,6 +95,8 @@ class App(ctk.CTk):
|
|||||||
self._tl_order = [] # 温度查询应答顺序: 'DMD'/'LAMP' 先到先得
|
self._tl_order = [] # 温度查询应答顺序: 'DMD'/'LAMP' 先到先得
|
||||||
self._build()
|
self._build()
|
||||||
self._load_displays()
|
self._load_displays()
|
||||||
|
# 主窗口按 ESC 也能关闭所有投屏(投屏窗口失焦时的兜底出口)
|
||||||
|
self.bind('<Escape>', lambda _e: patterns.close_all())
|
||||||
if dlpc_rs485.HAS_SERIAL:
|
if dlpc_rs485.HAS_SERIAL:
|
||||||
self._poll_dlpc()
|
self._poll_dlpc()
|
||||||
if auto_close > 0:
|
if auto_close > 0:
|
||||||
@@ -208,7 +211,8 @@ class App(ctk.CTk):
|
|||||||
self.edid_scroll.pack(fill='both', expand=True)
|
self.edid_scroll.pack(fill='both', expand=True)
|
||||||
|
|
||||||
# -- 测试图案 --
|
# -- 测试图案 --
|
||||||
card, c = self._card(col,'测试图案 — 黑底 1:1 居中, 按 ESC 退出')
|
card, c = self._card(col,'测试图案 — 黑底 1:1 居中, 投屏按 ESC 或"停止测试图像"退出',
|
||||||
|
button='停止测试图像', command=patterns.close_all)
|
||||||
card.grid(row=3, column=0, sticky='ew', pady=(10, 0))
|
card.grid(row=3, column=0, sticky='ew', pady=(10, 0))
|
||||||
row1 = ctk.CTkFrame(c, fg_color='transparent')
|
row1 = ctk.CTkFrame(c, fg_color='transparent')
|
||||||
row1.pack(fill='x')
|
row1.pack(fill='x')
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
================================================================
|
================================================================
|
||||||
配合 demo 板当前 EDID 首选时序 800x600@60, 全屏纯色输出。
|
配合 demo 板当前 EDID 首选时序 800x600@60, 全屏纯色输出。
|
||||||
用法: python gen_solid_colors.py → 输出到 ../assets/solid_*.png
|
用法: python gen_solid_colors.py → 输出到 ../assets/solid_*.png
|
||||||
在 HDMI-Tool 中通过自定义栏位(⚙ 换图)加载, 发布包随附。
|
在 DLPilot 中通过自定义栏位(⚙ 换图)加载, 发布包随附。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
@@ -9,6 +9,19 @@
|
|||||||
|
|
||||||
from config import DEFAULT_AREA, HAS_PIL, Image, ImageTk, PAT_W, PAT_H
|
from config import DEFAULT_AREA, HAS_PIL, Image, ImageTk, PAT_W, PAT_H
|
||||||
|
|
||||||
|
# 当前打开的投屏窗口集合: 支持"新图案替换旧图案"与 close_all() 一键恢复桌面
|
||||||
|
_OPEN = set()
|
||||||
|
|
||||||
|
|
||||||
|
def close_all():
|
||||||
|
"""关闭所有打开的投屏窗口, 恢复桌面"""
|
||||||
|
for top in list(_OPEN):
|
||||||
|
try:
|
||||||
|
top.destroy()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
_OPEN.clear()
|
||||||
|
|
||||||
|
|
||||||
PATTERN_NAMES = {'Red': '纯红', 'Green': '纯绿', 'Blue': '纯蓝',
|
PATTERN_NAMES = {'Red': '纯红', 'Green': '纯绿', 'Blue': '纯蓝',
|
||||||
'Checker': '黑白棋盘格', 'Grid': '网格线'}
|
'Checker': '黑白棋盘格', 'Grid': '网格线'}
|
||||||
@@ -43,8 +56,9 @@ def _area_box(spec, w, h):
|
|||||||
def _open_fullscreen(parent, rect, auto_close=0):
|
def _open_fullscreen(parent, rect, auto_close=0):
|
||||||
"""创建覆盖 rect(x,y,w,h) 的无边框全屏黑底窗口, 返回 (top, cv)
|
"""创建覆盖 rect(x,y,w,h) 的无边框全屏黑底窗口, 返回 (top, cv)
|
||||||
|
|
||||||
只支持 ESC 退出: overrideredirect 窗口必须 focus_force 强取键盘焦点,
|
ESC 退出(需窗口持有键盘焦点); overrideredirect 窗口必须 focus_force
|
||||||
否则收不到按键(Windows 上该组合有效)。
|
强取键盘焦点, 否则收不到按键(Windows 上该组合有效)。
|
||||||
|
窗口登记进 _OPEN: 打开新投屏前会关旧的(替换语义), close_all() 一键全关。
|
||||||
"""
|
"""
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
rx, ry, rw, rh = rect
|
rx, ry, rw, rh = rect
|
||||||
@@ -58,9 +72,16 @@ def _open_fullscreen(parent, rect, auto_close=0):
|
|||||||
cv.pack(fill='both', expand=True)
|
cv.pack(fill='both', expand=True)
|
||||||
|
|
||||||
def close(_evt=None):
|
def close(_evt=None):
|
||||||
|
_OPEN.discard(top)
|
||||||
top.destroy()
|
top.destroy()
|
||||||
|
|
||||||
|
def _on_destroy(e):
|
||||||
|
if e.widget is top: # 子控件销毁也冒泡到此, 只认窗口本身
|
||||||
|
_OPEN.discard(top)
|
||||||
|
|
||||||
top.bind('<Escape>', close)
|
top.bind('<Escape>', close)
|
||||||
|
top.bind('<Destroy>', _on_destroy)
|
||||||
|
_OPEN.add(top)
|
||||||
if auto_close > 0:
|
if auto_close > 0:
|
||||||
top.after(auto_close * 1000, close)
|
top.after(auto_close * 1000, close)
|
||||||
top.focus_force()
|
top.focus_force()
|
||||||
@@ -68,7 +89,9 @@ def _open_fullscreen(parent, rect, auto_close=0):
|
|||||||
|
|
||||||
|
|
||||||
def show_pattern(parent, rect, kind, auto_close=0, area=DEFAULT_AREA):
|
def show_pattern(parent, rect, kind, auto_close=0, area=DEFAULT_AREA):
|
||||||
"""在 rect 指定的屏幕上全屏出居中测试卡(无任何 OSD 修饰), 区域由 area 指定"""
|
"""在 rect 指定的屏幕上全屏出居中测试卡(无任何 OSD 修饰), 区域由 area 指定。
|
||||||
|
打开前先关闭已有投屏窗口(替换语义, 不会多层叠罗汉)。"""
|
||||||
|
close_all()
|
||||||
top, cv = _open_fullscreen(parent, rect, auto_close)
|
top, cv = _open_fullscreen(parent, rect, auto_close)
|
||||||
|
|
||||||
def draw(_evt=None):
|
def draw(_evt=None):
|
||||||
@@ -107,7 +130,9 @@ def show_pattern(parent, rect, kind, auto_close=0, area=DEFAULT_AREA):
|
|||||||
def show_pattern_image(parent, rect, path, auto_close=0, area=DEFAULT_AREA):
|
def show_pattern_image(parent, rect, path, auto_close=0, area=DEFAULT_AREA):
|
||||||
"""全屏黑底, 图片 1:1 居中于 area 指定的测试卡区域:
|
"""全屏黑底, 图片 1:1 居中于 area 指定的测试卡区域:
|
||||||
小于区域 -> 真实像素居中, 四周黑边;
|
小于区域 -> 真实像素居中, 四周黑边;
|
||||||
大于区域 -> 等效从中心截取(区域外用黑矩形遮挡)"""
|
大于区域 -> 等效从中心截取(区域外用黑矩形遮挡)。
|
||||||
|
打开前先关闭已有投屏窗口(替换语义)。"""
|
||||||
|
close_all()
|
||||||
top, cv = _open_fullscreen(parent, rect, auto_close)
|
top, cv = _open_fullscreen(parent, rect, auto_close)
|
||||||
|
|
||||||
def draw(_evt=None):
|
def draw(_evt=None):
|
||||||
@@ -23,6 +23,7 @@ import re
|
|||||||
import ssl
|
import ssl
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import uuid
|
import uuid
|
||||||
@@ -59,7 +60,10 @@ class Api:
|
|||||||
self.headers = {'Authorization': f'token {token}'}
|
self.headers = {'Authorization': f'token {token}'}
|
||||||
self.ctx = ssl._create_unverified_context() if insecure else None
|
self.ctx = ssl._create_unverified_context() if insecure else None
|
||||||
|
|
||||||
def request(self, method, path, data=None, headers=None):
|
def request(self, method, path, data=None, headers=None, retries=4):
|
||||||
|
"""发请求; 实例 TLS 间歇性抽风(SSL UNEXPECTED_EOF), 连接级错误自动重试"""
|
||||||
|
last = None
|
||||||
|
for attempt in range(retries):
|
||||||
req = urllib.request.Request(self.api + path, data=data, method=method,
|
req = urllib.request.Request(self.api + path, data=data, method=method,
|
||||||
headers={**self.headers, **(headers or {})})
|
headers={**self.headers, **(headers or {})})
|
||||||
try:
|
try:
|
||||||
@@ -68,6 +72,10 @@ class Api:
|
|||||||
return resp.status, json.loads(body) if body else None
|
return resp.status, json.loads(body) if body else None
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
return e.code, json.loads(e.read() or b'{}')
|
return e.code, json.loads(e.read() or b'{}')
|
||||||
|
except (urllib.error.URLError, ssl.SSLError, OSError) as e:
|
||||||
|
last = e
|
||||||
|
time.sleep(1.0 + attempt)
|
||||||
|
raise RuntimeError(f'Gitea 请求连续 {retries} 次失败: {last}')
|
||||||
|
|
||||||
def get_json(self, path):
|
def get_json(self, path):
|
||||||
status, data = self.request('GET', path)
|
status, data = self.request('GET', path)
|
||||||
@@ -12,7 +12,7 @@ PORT = sys.argv[1] if len(sys.argv) > 1 else 'COM9'
|
|||||||
FULL = len(sys.argv) > 2 and sys.argv[2].lower() == 'full'
|
FULL = len(sys.argv) > 2 and sys.argv[2].lower() == 'full'
|
||||||
|
|
||||||
BANK, REG = '60', '6E'
|
BANK, REG = '60', '6E'
|
||||||
STEPS = [ # (写值, 含义, 写入后 HDMI-Tool 应投的图, LA 预期变化 vs 基线)
|
STEPS = [ # (写值, 含义, 写入后 DLPilot 应投的图, LA 预期变化 vs 基线)
|
||||||
('00', '关闭交换(基准)', 'solid_0000AA / solid_000055',
|
('00', '关闭交换(基准)', 'solid_0000AA / solid_000055',
|
||||||
'D0:AA=13%/55=84%, D1:AA=72%/55=0.9% (基线)'),
|
'D0:AA=13%/55=84%, D1:AA=72%/55=0.9% (基线)'),
|
||||||
('80', 'B通道镜像', 'solid_0000AA',
|
('80', 'B通道镜像', 'solid_0000AA',
|
||||||
|
|||||||