|
|
"""
|
|
|
Tkinter 图形界面模块(白板 GUI)
|
|
|
为各模块提供可视化的测试入口:密钥/证书、加密/解密、数字信封、P2P 传输、日志。
|
|
|
仅提供界面与事件占位,核心逻辑由 core/* 实现后接入。
|
|
|
"""
|
|
|
import os
|
|
|
import tkinter as tk
|
|
|
from tkinter import filedialog, messagebox, ttk
|
|
|
from typing import Optional
|
|
|
|
|
|
from ui.about_tab import create_about_tab
|
|
|
from ui.context import UIContext
|
|
|
from ui.crypto_tab import create_crypto_tab
|
|
|
from ui.envelope_tab import create_envelope_tab
|
|
|
from ui.keys_tab import create_keys_tab
|
|
|
from ui.p2p_tab import create_p2p_tab
|
|
|
|
|
|
|
|
|
# 尝试懒加载核心模块,未安装依赖时不抛出异常,界面仍可运行
|
|
|
try:
|
|
|
from core import crypto as crypto_mod
|
|
|
except Exception:
|
|
|
crypto_mod = None
|
|
|
|
|
|
try:
|
|
|
from core import protocol as protocol_mod
|
|
|
except Exception:
|
|
|
protocol_mod = None
|
|
|
|
|
|
try:
|
|
|
from core import transfer as transfer_mod
|
|
|
except Exception:
|
|
|
transfer_mod = None
|
|
|
|
|
|
try:
|
|
|
from core import signaling_client as signaling_mod
|
|
|
except Exception:
|
|
|
signaling_mod = None
|
|
|
|
|
|
|
|
|
class MainWindow:
|
|
|
"""主窗口:提供标签页白板 GUI 以便功能联调与演示"""
|
|
|
|
|
|
def __init__(
|
|
|
self,
|
|
|
root: tk.Tk,
|
|
|
*,
|
|
|
default_sig_host: str = "127.0.0.1",
|
|
|
default_sig_port: int = 9999,
|
|
|
default_user: str = "alice",
|
|
|
default_listen_port: int = 8001,
|
|
|
):
|
|
|
self.root = root
|
|
|
self.root.title("SecureFileProject - 白板测试 GUI")
|
|
|
self.root.geometry("900x600")
|
|
|
|
|
|
self.notebook = ttk.Notebook(self.root)
|
|
|
self.notebook.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
self.log_text = tk.Text(self.root, height=6, state=tk.DISABLED)
|
|
|
self.log_text.pack(fill=tk.X, side=tk.BOTTOM)
|
|
|
|
|
|
self.status_var = tk.StringVar(value="就绪")
|
|
|
status_bar = ttk.Label(self.root, textvariable=self.status_var, anchor=tk.W)
|
|
|
status_bar.pack(fill=tk.X, side=tk.BOTTOM)
|
|
|
|
|
|
self.selected_file: Optional[str] = None
|
|
|
|
|
|
self.ctx = UIContext(
|
|
|
log=self._log,
|
|
|
set_status=self._set_status,
|
|
|
choose_file=self._choose_file,
|
|
|
ensure_impl=self._ensure_impl,
|
|
|
get_selected_file=self._get_selected_file,
|
|
|
set_selected_file=self._set_selected_file,
|
|
|
)
|
|
|
|
|
|
create_keys_tab(self.notebook, self.ctx, crypto_mod)
|
|
|
create_crypto_tab(self.notebook, self.ctx, crypto_mod)
|
|
|
create_envelope_tab(self.notebook, self.ctx, protocol_mod)
|
|
|
create_p2p_tab(
|
|
|
self.notebook,
|
|
|
self.ctx,
|
|
|
signaling_mod,
|
|
|
transfer_mod,
|
|
|
default_sig_host=default_sig_host,
|
|
|
default_sig_port=default_sig_port,
|
|
|
default_user=default_user,
|
|
|
default_listen_port=default_listen_port,
|
|
|
)
|
|
|
create_about_tab(self.notebook, self.ctx)
|
|
|
|
|
|
self._log("GUI 初始化完成。若功能未实现,将提示 TODO。")
|
|
|
|
|
|
def _log(self, msg: str):
|
|
|
self.log_text.configure(state=tk.NORMAL)
|
|
|
self.log_text.insert(tk.END, f"[LOG] {msg}\n")
|
|
|
self.log_text.see(tk.END)
|
|
|
self.log_text.configure(state=tk.DISABLED)
|
|
|
|
|
|
def _set_status(self, msg: str):
|
|
|
self.status_var.set(msg)
|
|
|
|
|
|
def _get_selected_file(self) -> Optional[str]:
|
|
|
return self.selected_file
|
|
|
|
|
|
def _set_selected_file(self, path: str):
|
|
|
self.selected_file = path
|
|
|
self._set_status(f"已选择文件:{os.path.basename(path)}")
|
|
|
self._log(f"选择文件: {path}")
|
|
|
|
|
|
def _choose_file(self) -> Optional[str]:
|
|
|
path = filedialog.askopenfilename()
|
|
|
if path:
|
|
|
self._set_selected_file(path)
|
|
|
return path
|
|
|
return None
|
|
|
|
|
|
def _ensure_impl(self, module, feature_name: str) -> bool:
|
|
|
if module is None:
|
|
|
messagebox.showwarning("功能未就绪", f"{feature_name} 依赖模块尚未可用或依赖未安装。请先完成核心实现或安装依赖。")
|
|
|
self._log(f"{feature_name} 未就绪:模块或依赖缺失")
|
|
|
return False
|
|
|
return True
|
|
|
def run_app(
|
|
|
*,
|
|
|
default_sig_host: str = "127.0.0.1",
|
|
|
default_sig_port: int = 9999,
|
|
|
default_user: str = "alice",
|
|
|
default_listen_port: int = 8001,
|
|
|
):
|
|
|
root = tk.Tk()
|
|
|
MainWindow(
|
|
|
root,
|
|
|
default_sig_host=default_sig_host,
|
|
|
default_sig_port=default_sig_port,
|
|
|
default_user=default_user,
|
|
|
default_listen_port=default_listen_port,
|
|
|
)
|
|
|
root.mainloop()
|
|
|
|