Files
ykxmz/_build/patch_image_modes.py
T
xmz7891 b62ac865ea ykxmz 1.5.0:修复手表端 TLS 证书校验 + MTU 修复网络稳定性
被控端
- 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%)
- 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败)
- 每分钟回头找局域网,自动切回快 400 倍的路径
- 画面心跳帧:静止时省流量,客户端仍知道流是活的
- 锁屏时给出可操作的提示

手表端
- 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException
- 连接错误翻译成人话(证书/时间/解析/超时分别提示)
- 经中转连上后用服务器报的地址刷新已配对记录
- 新增局域网连接页面、服务器延迟检测

服务器端
- 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502)
- 网络调优脚本 net-tune.sh(开机自动应用)
- 管理页:批量删除、网络体检、下载区
- 隧道改 HTTP/2 + 双连接

文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
2026-10-06 19:01:05 +08:00

307 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""给投图加上「全屏 / 居中窗口 / 自定义大小」三种方式。
1. config.py 新增 image_display / image_custom_width / image_custom_height
2. actions.py show_image 按设置选择显示方式
3. ui.py 主窗口加一行「图片显示」下拉 + 自定义宽高输入框
"""
from __future__ import annotations
import ast
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
CONFIG = ROOT / "windows" / "lanremote" / "config.py"
ACTIONS = ROOT / "windows" / "lanremote" / "actions.py"
UI = ROOT / "windows" / "lanremote" / "ui.py"
# ---------------------------------------------------------------- config
CONFIG_OLD = ''' "image_timeout": 0, # 图片全屏显示秒数,0=手动关闭
'''
CONFIG_NEW = ''' "image_timeout": 0, # 图片显示秒数,0=手动关闭
"image_display": "window", # 投图方式: window=屏幕中间普通窗口 / fullscreen=全屏 / custom=自定义大小
"image_custom_width": 0, # 自定义宽度(像素),0=自动
"image_custom_height": 0, # 自定义高度(像素),0=自动
'''
# --------------------------------------------------------------- actions
ACTIONS_OLD_START = " def show_image(self, path: str) -> str:"
ACTIONS_OLD_END = ' return f"已在屏幕中间显示 {p.name}"\n'
ACTIONS_NEW = ''' def show_image(self, path: str) -> str:
"""显示图片。
方式由设置 image_display 决定:
window —— 屏幕正中间的普通 Windows 窗口(默认)
fullscreen —— 全屏
custom —— 按 image_custom_width/height 指定大小
宽或高填 0 表示该方向自动。
"""
p = Path(path)
if not p.is_file():
raise ActionError(f"图片不存在: {path}")
from PIL import Image, ImageTk
try:
img = Image.open(p)
img.load()
except Exception as exc:
raise ActionError(f"无法读取图片: {exc}") from exc
mode = str(self.store.get("image_display", "window") or "window")
if mode not in ("window", "fullscreen", "custom"):
mode = "window"
screen_w, screen_h = self.ui.screen_size()
img = img.convert("RGB")
if mode == "fullscreen":
target = (screen_w, screen_h)
elif mode == "custom":
cw = int(self.store.get("image_custom_width", 0) or 0)
ch = int(self.store.get("image_custom_height", 0) or 0)
if cw <= 0 and ch <= 0:
# 两个都没填就退回默认窗口大小
cw, ch = int(screen_w * 0.78), int(screen_h * 0.78)
self.store.set("image_custom_width", cw)
self.store.set("image_custom_height", ch)
elif cw <= 0:
cw = screen_w
elif ch <= 0:
ch = screen_h
target = (max(80, cw), max(80, ch))
else:
target = (int(screen_w * 0.78), int(screen_h * 0.78))
img.thumbnail(target, Image.LANCZOS)
fullscreen = (mode == "fullscreen")
backdrop = "#000000" if fullscreen else "#ffffff"
def build():
import tkinter as tk
if self._image_window is not None:
try:
self._image_window.destroy()
except Exception:
pass
win = tk.Toplevel(self.ui.root)
win.title(f"图片 - {p.name}")
win.configure(bg=backdrop)
win.resizable(False, False)
def close(_event=None):
with self._lock:
self._image_window = None
try:
win.destroy()
except Exception:
pass
photo = ImageTk.PhotoImage(img)
label = tk.Label(win, image=photo, bg=backdrop, bd=0,
highlightthickness=0)
label.image = photo # 防止被 GC
label.pack(fill="both", expand=True)
if fullscreen:
win.overrideredirect(True)
win.attributes("-fullscreen", True)
win.attributes("-topmost", True)
tk.Label(win, text="按 Esc 或点击关闭", bg=backdrop, fg="#888888",
font=("Microsoft YaHei UI", 10)).place(
relx=0.5, rely=0.97, anchor="center")
else:
foot = tk.Frame(win, bg=backdrop)
foot.pack(fill="x")
tk.Button(foot, text="关闭", command=close,
bg="#eaeef2", fg="#1f2328", relief="flat",
font=("Microsoft YaHei UI", 9), padx=14, pady=2,
cursor="hand2").pack(side="right", padx=10, pady=8)
# 摆到屏幕正中间 —— 普通窗口,有标题栏和关闭按钮
win.update_idletasks()
width = win.winfo_reqwidth()
height = win.winfo_reqheight()
x = max(0, (win.winfo_screenwidth() - width) // 2)
y = max(0, (win.winfo_screenheight() - height) // 2)
win.geometry(f"{width}x{height}+{x}+{y}")
win.lift()
win.focus_force()
win.bind("<Escape>", close)
win.bind("<Button-1>", close)
win.protocol("WM_DELETE_WINDOW", close)
self._image_window = win
timeout = int(self.store.get("image_timeout", 0) or 0)
if timeout > 0:
win.after(timeout * 1000, close)
self.ui.ui_call(build)
label_text = {"window": "屏幕中间", "fullscreen": "全屏",
"custom": f"{img.width}x{img.height}"}[mode]
return f"已显示 {p.name}({label_text})"
'''
# -------------------------------------------------------------------- ui
UI_ANCHOR = " self.lbl_conn.pack(fill=\"x\", pady=(2, 2), **pad)\n"
UI_ROW = ''' self.lbl_conn.pack(fill="x", pady=(2, 2), **pad)
# 投图方式:屏幕中间普通窗口 / 全屏 / 自定义大小
mode_row = tk.Frame(self.root, bg=BG)
mode_row.pack(fill="x", pady=(0, 2), **pad)
tk.Label(mode_row, text="图片显示", bg=BG, fg=DIM,
font=FONT_SMALL).pack(side="left")
self.var_image_mode = tk.StringVar(value="屏幕中间")
self.opt_image_mode = tk.OptionMenu(
mode_row, self.var_image_mode, "屏幕中间", "全屏", "自定义",
command=self.on_image_mode_changed)
self.opt_image_mode.configure(
bg=CARD, fg=FG, activebackground="#eaeef2", activeforeground=FG,
relief="flat", highlightthickness=1, highlightbackground=BORDER,
font=FONT_SMALL, width=7, anchor="w", cursor="hand2", padx=2)
try:
self.opt_image_mode["menu"].configure(
bg=CARD, fg=FG, font=FONT_SMALL, activebackground="#eaeef2",
activeforeground=FG, relief="flat", bd=0)
except tk.TclError:
pass
self.opt_image_mode.pack(side="left", padx=(6, 6))
# 自定义宽高(只在选「自定义」时显示)
self.size_box = tk.Frame(mode_row, bg=BG)
self.ent_img_w = tk.Entry(
self.size_box, width=5, bg=CARD, fg=FG, relief="flat",
font=FONT_SMALL, justify="center", highlightthickness=1,
highlightbackground=BORDER, highlightcolor=ACCENT)
self.ent_img_w.pack(side="left", ipady=2)
tk.Label(self.size_box, text="×", bg=BG, fg=DIM,
font=FONT_SMALL).pack(side="left", padx=2)
self.ent_img_h = tk.Entry(
self.size_box, width=5, bg=CARD, fg=FG, relief="flat",
font=FONT_SMALL, justify="center", highlightthickness=1,
highlightbackground=BORDER, highlightcolor=ACCENT)
self.ent_img_h.pack(side="left", ipady=2)
tk.Label(self.size_box, text="像素", bg=BG, fg=DIM,
font=FONT_SMALL).pack(side="left", padx=(3, 0))
for entry in (self.ent_img_w, self.ent_img_h):
entry.bind("<Return>", self.on_custom_size_changed)
entry.bind("<FocusOut>", self.on_custom_size_changed)
self.var_image_mode.set(
self.MODE_LABELS.get(str(self.store.get("image_display", "window")),
"屏幕中间"))
width_now = int(self.store.get("image_custom_width", 0) or 0)
height_now = int(self.store.get("image_custom_height", 0) or 0)
if width_now:
self.ent_img_w.insert(0, str(width_now))
if height_now:
self.ent_img_h.insert(0, str(height_now))
self.sync_image_mode_row()
'''
UI_HANDLERS_ANCHOR = " def refresh_firewall(self) -> None:\n"
UI_HANDLERS = ''' # ---- 图片显示方式 ----
MODE_LABELS = {"window": "屏幕中间", "fullscreen": "全屏", "custom": "自定义"}
LABEL_MODES = {"屏幕中间": "window", "全屏": "fullscreen", "自定义": "custom"}
def on_image_mode_changed(self, label: str) -> None:
self.store.set("image_display", self.LABEL_MODES.get(label, "window"))
self.sync_image_mode_row()
self.log(f"图片显示方式已改为「{label}」")
def sync_image_mode_row(self) -> None:
mode = str(self.store.get("image_display", "window"))
if mode == "custom":
self.size_box.pack(side="left")
else:
self.size_box.pack_forget()
try:
self.root.update_idletasks()
except tk.TclError:
pass
def on_custom_size_changed(self, _event=None) -> None:
for key, entry in (("image_custom_width", self.ent_img_w),
("image_custom_height", self.ent_img_h)):
raw = entry.get().strip()
self.store.set(key, int(raw) if raw.isdigit() else 0)
def refresh_firewall(self) -> None:
'''
def patch_config() -> bool:
text = CONFIG.read_text(encoding="utf-8")
if CONFIG_OLD not in text:
print(" [失败] config.py 锚点未找到")
return False
text = text.replace(CONFIG_OLD, CONFIG_NEW)
CONFIG.write_text(text, encoding="utf-8")
print(" config.py: 已加 image_display / image_custom_width / image_custom_height")
return True
def patch_actions() -> bool:
text = ACTIONS.read_text(encoding="utf-8")
start = text.index(ACTIONS_OLD_START)
end = text.index(ACTIONS_OLD_END, start) + len(ACTIONS_OLD_END)
text = text[:start] + ACTIONS_NEW + text[end:]
ACTIONS.write_text(text, encoding="utf-8")
print(" actions.py: show_image 已支持三种显示方式")
return True
def patch_ui() -> bool:
text = UI.read_text(encoding="utf-8")
if UI_ANCHOR not in text:
print(" [失败] ui.py 锚点 lbl_conn.pack 未找到")
return False
text = text.replace(UI_ANCHOR, UI_ROW, 1)
if UI_HANDLERS_ANCHOR not in text:
print(" [失败] ui.py 锚点 refresh_firewall 未找到")
return False
text = text.replace(UI_HANDLERS_ANCHOR, UI_HANDLERS, 1)
# 多了一行,窗口要加高
text = text.replace("BASE_H = 336", "BASE_H = 372")
UI.write_text(text, encoding="utf-8")
print(" ui.py: 已加「图片显示」设置行,窗口高度 336 -> 372")
return True
def main() -> int:
ok = True
print("修改文件:")
ok &= patch_config()
ok &= patch_actions()
ok &= patch_ui()
print("\n语法检查:")
for name, path in (("config.py", CONFIG), ("actions.py", ACTIONS), ("ui.py", UI)):
try:
ast.parse(path.read_text(encoding="utf-8"))
print(f" {name}: OK")
except SyntaxError as exc:
print(f" {name}: 语法错误 {exc}")
ok = False
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())