摘要
本文将构建一个功能完整的综合电子战指挥控制台,重点展示tkinter/ttk在多视图协同、实时数据流处理、插件化架构和高级主题引擎方面的能力。通过实现一个具有多个功能视图、可动态加载插件、支持实时数据可视化的现代化指挥控制台,展示如何利用Python标准库构建专业级、可扩展的复杂应用系统。
目录
-
引言:综合电子战指挥控制台的需求与挑战
-
系统架构设计
-
多视图协同显示系统实现
-
实时数据流处理框架
-
插件化架构设计与实现
-
高级主题引擎与皮肤系统
-
分布式系统架构概念
-
系统集成与测试
-
性能优化与部署
-
总结与展望
1. 引言:综合电子战指挥控制台的需求与挑战
1.1 需求分析
综合电子战指挥控制台是现代电子战系统的核心,需要满足以下关键需求:

1.2 技术挑战
-
复杂布局管理:多个视图的布局、拖拽调整、最大化/最小化
-
实时性能:保证大量数据实时刷新时的界面流畅性
-
模块化设计:实现高内聚、低耦合的插件系统
-
可扩展性:方便未来功能扩展和定制
-
数据一致性:多视图间的数据同步和一致性维护
2. 系统架构设计
2.1 整体架构
python
"""
ewcc_architecture.py
综合电子战指挥控制台架构设计
"""
from abc import ABC, abstractmethod
from typing import Dict, List, Any, Optional, Callable, Set
from enum import Enum
from dataclasses import dataclass, field
import threading
import queue
import time
from datetime import datetime
class ComponentType(Enum):
"""组件类型"""
CORE = "core" # 核心组件
PLUGIN = "plugin" # 插件组件
VIEW = "view" # 视图组件
SERVICE = "service" # 服务组件
WIDGET = "widget" # 控件组件
class ComponentState(Enum):
"""组件状态"""
UNINITIALIZED = "uninitialized" # 未初始化
INITIALIZING = "initializing" # 初始化中
RUNNING = "running" # 运行中
PAUSED = "paused" # 已暂停
STOPPED = "stopped" # 已停止
ERROR = "error" # 错误状态
@dataclass
class ComponentInfo:
"""组件信息"""
id: str
name: str
type: ComponentType
version: str
description: str
author: str
dependencies: List[str] = field(default_factory=list)
state: ComponentState = ComponentState.UNINITIALIZED
config: Dict[str, Any] = field(default_factory=dict)
class EventType(Enum):
"""事件类型"""
SYSTEM_START = "system_start"
SYSTEM_STOP = "system_stop"
DATA_UPDATE = "data_update"
VIEW_CREATE = "view_create"
VIEW_DESTROY = "view_destroy"
PLUGIN_LOAD = "plugin_load"
PLUGIN_UNLOAD = "plugin_unload"
ALERT = "alert"
COMMAND = "command"
@dataclass
class Event:
"""事件对象"""
id: str
type: EventType
source: str
target: str = ""
timestamp: float = field(default_factory=time.time)
data: Dict[str, Any] = field(default_factory=dict)
priority: int = 1 # 优先级: 1-最低, 5-最高
class BaseComponent(ABC):
"""组件基类"""
def __init__(self, info: ComponentInfo):
self.info = info
self.event_handlers: Dict[EventType, List[Callable]] = {}
self.message_queue = queue.Queue()
self.running = False
self.thread: Optional[threading.Thread] = None
@abstractmethod
def initialize(self, config: Dict[str, Any]) -> bool:
"""初始化组件"""
pass
@abstractmethod
def start(self) -> bool:
"""启动组件"""
pass
@abstractmethod
def stop(self) -> bool:
"""停止组件"""
pass
@abstractmethod
def process_message(self, message: Any):
"""处理消息"""
pass
def register_handler(self, event_type: EventType, handler: Callable):
"""注册事件处理函数"""
if event_type not in self.event_handlers:
self.event_handlers[event_type] = []
self.event_handlers[event_type].append(handler)
def emit_event(self, event: Event):
"""发送事件"""
if event.type in self.event_handlers:
for handler in self.event_handlers[event.type]:
try:
handler(event)
except Exception as e:
print(f"事件处理错误: {e}")
def run_loop(self):
"""运行循环"""
while self.running:
try:
# 处理消息
if not self.message_queue.empty():
message = self.message_queue.get_nowait()
self.process_message(message)
# 执行组件特定逻辑
self.update()
# 休眠以避免CPU占用过高
time.sleep(0.01)
except Exception as e:
print(f"组件 {self.info.name} 运行错误: {e}")
def update(self):
"""更新组件状态(子类可重写)"""
pass
class EWCCSystem:
"""综合电子战指挥控制台系统"""
def __init__(self):
self.components: Dict[str, BaseComponent] = {}
self.event_bus = EventBus()
self.plugin_manager = PluginManager(self)
self.view_manager = ViewManager(self)
self.data_bus = DataBus(self)
self.theme_engine = ThemeEngine()
self.command_dispatcher = CommandDispatcher(self)
# 系统状态
self.system_state = ComponentState.UNINITIALIZED
self.start_time: Optional[float] = None
self.performance_metrics: Dict[str, Any] = {}
def initialize(self, config: Dict[str, Any]) -> bool:
"""初始化系统"""
try:
self.system_state = ComponentState.INITIALIZING
# 1. 初始化核心组件
core_components = [
self.event_bus,
self.plugin_manager,
self.view_manager,
self.data_bus,
self.theme_engine,
self.command_dispatcher
]
for component in core_components:
component.initialize(config)
self.register_component(component)
# 2. 加载插件
self.plugin_manager.load_plugins()
# 3. 创建默认视图
self.view_manager.create_default_views()
# 4. 启动所有组件
for component in self.components.values():
component.start()
self.system_state = ComponentState.RUNNING
self.start_time = time.time()
return True
except Exception as e:
print(f"系统初始化失败: {e}")
self.system_state = ComponentState.ERROR
return False
def register_component(self, component: BaseComponent):
"""注册组件"""
self.components[component.info.id] = component
def get_component(self, component_id: str) -> Optional[BaseComponent]:
"""获取组件"""
return self.components.get(component_id)
def shutdown(self):
"""关闭系统"""
# 停止所有组件
for component in reversed(list(self.components.values())):
try:
component.stop()
except Exception as e:
print(f"停止组件 {component.info.name} 失败: {e}")
self.system_state = ComponentState.STOPPED
def get_system_info(self) -> Dict[str, Any]:
"""获取系统信息"""
return {
"state": self.system_state.value,
"uptime": time.time() - self.start_time if self.start_time else 0,
"component_count": len(self.components),
"active_plugins": len(self.plugin_manager.get_active_plugins()),
"active_views": len(self.view_manager.get_active_views()),
"performance": self.performance_metrics
}
class EventBus(BaseComponent):
"""事件总线"""
def __init__(self):
super().__init__(ComponentInfo(
id="event_bus",
name="事件总线",
type=ComponentType.CORE,
version="1.0.0",
description="系统事件分发中心",
author="EWCC Team"
))
self.subscribers: Dict[EventType, Set[str]] = {}
def initialize(self, config: Dict[str, Any]) -> bool:
self.subscribers = {event_type: set() for event_type in EventType}
return True
def publish(self, event: Event):
"""发布事件"""
# 这里简化实现,实际中会发送给所有订阅者
pass
def subscribe(self, event_type: EventType, component_id: str):
"""订阅事件"""
if event_type in self.subscribers:
self.subscribers[event_type].add(component_id)
def unsubscribe(self, event_type: EventType, component_id: str):
"""取消订阅"""
if event_type in self.subscribers:
self.subscribers[event_type].discard(component_id)
class DataBus(BaseComponent):
"""数据总线"""
def __init__(self, system: EWCCSystem):
super().__init__(ComponentInfo(
id="data_bus",
name="数据总线",
type=ComponentType.CORE,
version="1.0.0",
description="系统数据交换中心",
author="EWCC Team"
))
self.system = system
self.data_channels: Dict[str, List[Callable]] = {}
def initialize(self, config: Dict[str, Any]) -> bool:
return True
def register_channel(self, channel_id: str, handler: Callable):
"""注册数据通道"""
if channel_id not in self.data_channels:
self.data_channels[channel_id] = []
self.data_channels[channel_id].append(handler)
def send_data(self, channel_id: str, data: Any):
"""发送数据"""
if channel_id in self.data_channels:
for handler in self.data_channels[channel_id]:
try:
handler(data)
except Exception as e:
print(f"数据处理错误: {e}")
# 其他核心组件的简化定义
class PluginManager(BaseComponent):
"""插件管理器"""
def __init__(self, system: EWCCSystem):
super().__init__(ComponentInfo(
id="plugin_manager",
name="插件管理器",
type=ComponentType.CORE,
version="1.0.0",
description="管理插件加载和卸载",
author="EWCC Team"
))
self.system = system
self.plugins: Dict[str, BaseComponent] = {}
def load_plugins(self):
"""加载插件"""
pass
def get_active_plugins(self) -> List[str]:
"""获取活动插件列表"""
return list(self.plugins.keys())
class ViewManager(BaseComponent):
"""视图管理器"""
def __init__(self, system: EWCCSystem):
super().__init__(ComponentInfo(
id="view_manager",
name="视图管理器",
type=ComponentType.CORE,
version="1.0.0",
description="管理视图创建和布局",
author="EWCC Team"
))
self.system = system
self.views: Dict[str, Any] = {}
def create_default_views(self):
"""创建默认视图"""
pass
def get_active_views(self) -> List[str]:
"""获取活动视图列表"""
return list(self.views.keys())
class ThemeEngine(BaseComponent):
"""主题引擎"""
def __init__(self):
super().__init__(ComponentInfo(
id="theme_engine",
name="主题引擎",
type=ComponentType.CORE,
version="1.0.0",
description="管理界面主题和皮肤",
author="EWCC Team"
))
self.current_theme = "default"
class CommandDispatcher(BaseComponent):
"""命令分发器"""
def __init__(self, system: EWCCSystem):
super().__init__(ComponentInfo(
id="command_dispatcher",
name="命令分发器",
type=ComponentType.CORE,
version="1.0.0",
description="处理系统命令",
author="EWCC Team"
))
self.system = system
2.2 架构图示

3. 多视图协同显示系统实现
3.1 视图管理器完整实现
python
"""
view_manager.py
多视图协同显示系统
"""
import tkinter as tk
from tkinter import ttk
from typing import Dict, List, Any, Optional, Tuple, Callable
from enum import Enum
from dataclasses import dataclass, field
import json
import pickle
from abc import ABC, abstractmethod
class ViewType(Enum):
"""视图类型"""
BATTLEFIELD = "battlefield" # 战场态势
SPECTRUM = "spectrum" # 频谱监测
RESOURCE = "resource" # 资源管理
LOG = "log" # 日志信息
ALERT = "alert" # 告警显示
ANALYSIS = "analysis" # 分析视图
CUSTOM = "custom" # 自定义视图
class ViewState(Enum):
"""视图状态"""
HIDDEN = "hidden" # 隐藏
NORMAL = "normal" # 正常
MAXIMIZED = "maximized" # 最大化
MINIMIZED = "minimized" # 最小化
DOCKED = "docked" # 停靠
FLOATING = "floating" # 浮动
@dataclass
class ViewConfig:
"""视图配置"""
id: str
type: ViewType
title: str
position: Tuple[int, int] = (0, 0) # (x, y)
size: Tuple[int, int] = (400, 300) # (width, height)
state: ViewState = ViewState.NORMAL
z_order: int = 0
visible: bool = True
dock_area: Optional[str] = None
config: Dict[str, Any] = field(default_factory=dict)
class BaseView(ABC):
"""视图基类"""
def __init__(self, config: ViewConfig):
self.config = config
self.frame: Optional[ttk.Frame] = None
self.widgets: Dict[str, tk.Widget] = {}
self.data_sources: List[str] = []
self.event_handlers: Dict[str, List[Callable]] = {}
@abstractmethod
def create_ui(self, parent: tk.Widget):
"""创建UI界面"""
pass
@abstractmethod
def update_data(self, data: Dict[str, Any]):
"""更新数据"""
pass
def show(self):
"""显示视图"""
if self.frame:
self.frame.pack(fill=tk.BOTH, expand=True)
def hide(self):
"""隐藏视图"""
if self.frame:
self.frame.pack_forget()
def destroy(self):
"""销毁视图"""
if self.frame:
self.frame.destroy()
def register_event_handler(self, event_type: str, handler: Callable):
"""注册事件处理函数"""
if event_type not in self.event_handlers:
self.event_handlers[event_type] = []
self.event_handlers[event_type].append(handler)
def emit_event(self, event_type: str, data: Any = None):
"""触发事件"""
if event_type in self.event_handlers:
for handler in self.event_handlers[event_type]:
try:
handler(data)
except Exception as e:
print(f"事件处理错误: {e}")
class ViewManager:
"""视图管理器"""
def __init__(self, parent: tk.Widget):
self.parent = parent
self.views: Dict[str, BaseView] = {}
self.view_frames: Dict[str, tk.Widget] = {}
self.layout_engine = LayoutEngine()
self.current_layout = "default"
self.dock_manager = DockManager(parent)
# 视图注册表
self.view_registry: Dict[ViewType, type] = {}
# 注册内置视图类型
self.register_view_type(ViewType.BATTLEFIELD, BattlefieldView)
self.register_view_type(ViewType.SPECTRUM, SpectrumView)
self.register_view_type(ViewType.RESOURCE, ResourceView)
self.register_view_type(ViewType.LOG, LogView)
self.register_view_type(ViewType.ALERT, AlertView)
self.register_view_type(ViewType.ANALYSIS, AnalysisView)
def register_view_type(self, view_type: ViewType, view_class: type):
"""注册视图类型"""
self.view_registry[view_type] = view_class
def create_view(self, config: ViewConfig) -> BaseView:
"""创建视图"""
if config.type not in self.view_registry:
raise ValueError(f"未注册的视图类型: {config.type}")
# 创建视图实例
view_class = self.view_registry[config.type]
view = view_class(config)
# 创建容器框架
container = self._create_view_container(view.config)
view.frame = container
# 创建UI
view.create_ui(container)
# 存储视图
self.views[config.id] = view
self.view_frames[config.id] = container
return view
def _create_view_container(self, config: ViewConfig) -> tk.Widget:
"""创建视图容器"""
if config.state == ViewState.DOCKED and config.dock_area:
# 创建停靠容器
return self.dock_manager.create_dock_container(
config.id, config.title, config.dock_area
)
elif config.state == ViewState.FLOATING:
# 创建浮动窗口
return self._create_floating_window(config)
else:
# 创建普通框架
frame = ttk.Frame(self.parent)
# 添加标题栏
self._add_title_bar(frame, config.title, config.id)
return frame
def _add_title_bar(self, parent: tk.Widget, title: str, view_id: str):
"""添加标题栏"""
title_bar = ttk.Frame(parent)
title_bar.pack(fill=tk.X, padx=1, pady=(1, 0))
# 标题
ttk.Label(title_bar, text=title, font=("微软雅黑", 10)).pack(side=tk.LEFT, padx=5)
# 控制按钮
btn_frame = ttk.Frame(title_bar)
btn_frame.pack(side=tk.RIGHT)
buttons = [
("🗕", lambda: self.minimize_view(view_id)),
("🗖", lambda: self.maximize_view(view_id)),
("✕", lambda: self.close_view(view_id))
]
for text, command in buttons:
btn = ttk.Button(btn_frame, text=text, width=3, command=command)
btn.pack(side=tk.LEFT, padx=1)
def _create_floating_window(self, config: ViewConfig) -> tk.Toplevel:
"""创建浮动窗口"""
window = tk.Toplevel(self.parent)
window.title(config.title)
window.geometry(f"{config.size[0]}x{config.size[1]}+{config.position[0]}+{config.position[1]}")
# 设置窗口属性
window.transient(self.parent)
window.grab_set()
return window
def close_view(self, view_id: str):
"""关闭视图"""
if view_id in self.views:
view = self.views[view_id]
view.destroy()
del self.views[view_id]
del self.view_frames[view_id]
def minimize_view(self, view_id: str):
"""最小化视图"""
if view_id in self.view_frames:
frame = self.view_frames[view_id]
frame.pack_forget()
def maximize_view(self, view_id: str):
"""最大化视图"""
if view_id in self.views:
view = self.views[view_id]
if view.config.state == ViewState.MAXIMIZED:
# 恢复原大小
self._restore_view(view)
else:
# 最大化
self._maximize_view(view)
def _maximize_view(self, view: BaseView):
"""最大化视图"""
# 保存原状态
view.config.state = ViewState.MAXIMIZED
# 隐藏其他视图
for other_id, other_view in self.views.items():
if other_id != view.config.id:
other_view.hide()
# 最大化当前视图
if view.frame:
view.frame.pack(fill=tk.BOTH, expand=True)
def _restore_view(self, view: BaseView):
"""恢复视图"""
view.config.state = ViewState.NORMAL
self.apply_layout(self.current_layout)
def apply_layout(self, layout_name: str):
"""应用布局"""
layout = self.layout_engine.load_layout(layout_name)
if layout:
self.current_layout = layout_name
self._apply_layout_config(layout)
def _apply_layout_config(self, layout: Dict[str, Any]):
"""应用布局配置"""
# 清除现有布局
for frame in self.view_frames.values():
frame.pack_forget()
# 应用新布局
for view_config in layout.get("views", []):
view_id = view_config.get("id")
if view_id in self.views:
view = self.views[view_id]
config = view_config.get("config", {})
# 更新视图位置和大小
if "position" in config:
view.config.position = tuple(config["position"])
if "size" in config:
view.config.size = tuple(config["size"])
# 应用布局
self._place_view(view, config)
def _place_view(self, view: BaseView, config: Dict[str, Any]):
"""放置视图"""
if view.frame:
pack_config = config.get("pack", {})
grid_config = config.get("grid", {})
place_config = config.get("place", {})
if pack_config:
view.frame.pack(**pack_config)
elif grid_config:
view.frame.grid(**grid_config)
elif place_config:
view.frame.place(**place_config)
def save_layout(self, layout_name: str):
"""保存布局"""
layout = {
"name": layout_name,
"timestamp": time.time(),
"views": []
}
for view_id, view in self.views.items():
view_config = {
"id": view_id,
"type": view.config.type.value,
"config": {
"position": view.config.position,
"size": view.config.size,
"state": view.config.state.value
}
}
layout["views"].append(view_config)
self.layout_engine.save_layout(layout_name, layout)
def get_view(self, view_id: str) -> Optional[BaseView]:
"""获取视图"""
return self.views.get(view_id)
def get_all_views(self) -> List[BaseView]:
"""获取所有视图"""
return list(self.views.values())
def update_all_views(self, data: Dict[str, Any]):
"""更新所有视图"""
for view in self.views.values():
try:
view.update_data(data)
except Exception as e:
print(f"更新视图 {view.config.id} 失败: {e}")
class LayoutEngine:
"""布局引擎"""
def __init__(self):
self.layouts: Dict[str, Dict] = {}
self.load_builtin_layouts()
def load_builtin_layouts(self):
"""加载内置布局"""
self.layouts["default"] = {
"name": "default",
"description": "默认布局",
"views": [
{
"id": "battlefield",
"type": "battlefield",
"config": {
"grid": {"row": 0, "column": 0, "sticky": "nsew", "rowspan": 2}
}
},
{
"id": "spectrum",
"type": "spectrum",
"config": {
"grid": {"row": 0, "column": 1, "sticky": "nsew"}
}
},
{
"id": "resource",
"type": "resource",
"config": {
"grid": {"row": 1, "column": 1, "sticky": "nsew"}
}
}
]
}
def load_layout(self, layout_name: str) -> Optional[Dict]:
"""加载布局"""
if layout_name in self.layouts:
return self.layouts[layout_name]
return None
def save_layout(self, layout_name: str, layout: Dict):
"""保存布局"""
self.layouts[layout_name] = layout
class DockManager:
"""停靠管理器"""
def __init__(self, parent: tk.Widget):
self.parent = parent
self.dock_areas: Dict[str, ttk.PanedWindow] = {}
self.dock_widgets: Dict[str, List[str]] = {}
def create_dock_area(self, area_id: str, orientation: str = "horizontal"):
"""创建停靠区域"""
paned = ttk.PanedWindow(self.parent, orient=orientation)
self.dock_areas[area_id] = paned
self.dock_widgets[area_id] = []
return paned
def create_dock_container(self, view_id: str, title: str, area_id: str) -> tk.Widget:
"""创建停靠容器"""
if area_id not in self.dock_areas:
self.create_dock_area(area_id)
# 创建可停靠框架
frame = ttk.Frame(self.dock_areas[area_id])
# 添加标题栏
title_bar = ttk.Frame(frame)
title_bar.pack(fill=tk.X, padx=1, pady=(1, 0))
ttk.Label(title_bar, text=title).pack(side=tk.LEFT, padx=5)
# 添加拖拽手柄
ttk.Label(title_bar, text="⋮⋮", cursor="fleur").pack(side=tk.RIGHT, padx=5)
# 添加到停靠区域
self.dock_areas[area_id].add(frame)
self.dock_widgets[area_id].append(view_id)
return frame
# 具体视图实现示例
class BattlefieldView(BaseView):
"""战场态势视图"""
def create_ui(self, parent: tk.Widget):
# 创建主画布
self.canvas = tk.Canvas(parent, bg="#1C2833", highlightthickness=0)
self.canvas.pack(fill=tk.BOTH, expand=True)
# 添加控制工具栏
self._create_toolbar(parent)
def _create_toolbar(self, parent: tk.Widget):
toolbar = ttk.Frame(parent)
toolbar.pack(fill=tk.X, padx=5, pady=5)
tools = ["选择", "平移", "缩放", "测量", "标记"]
for tool in tools:
ttk.Button(toolbar, text=tool, width=8).pack(side=tk.LEFT, padx=2)
def update_data(self, data: Dict[str, Any]):
# 更新战场态势显示
if "battlefield" in data:
self._draw_battlefield(data["battlefield"])
def _draw_battlefield(self, data: Dict[str, Any]):
"""绘制战场态势"""
self.canvas.delete("all")
width = self.canvas.winfo_width()
height = self.canvas.winfo_height()
if width < 10 or height < 10:
return
# 绘制网格
self._draw_grid()
# 绘制单位
if "units" in data:
for unit in data["units"]:
self._draw_unit(unit)
# 绘制轨迹
if "tracks" in data:
for track in data["tracks"]:
self._draw_track(track)
def _draw_grid(self):
"""绘制网格"""
width = self.canvas.winfo_width()
height = self.canvas.winfo_height()
grid_size = 50
for x in range(0, width, grid_size):
self.canvas.create_line(x, 0, x, height, fill="#2C3E50", width=1)
for y in range(0, height, grid_size):
self.canvas.create_line(0, y, width, y, fill="#2C3E50", width=1)
def _draw_unit(self, unit: Dict[str, Any]):
"""绘制单位"""
x = unit.get("x", 0)
y = unit.get("y", 0)
unit_type = unit.get("type", "unknown")
colors = {
"friendly": "#2ECC71",
"hostile": "#E74C3C",
"neutral": "#3498DB",
"unknown": "#95A5A6"
}
color = colors.get(unit_type, "#95A5A6")
# 绘制单位符号
size = 10
self.canvas.create_oval(x-size, y-size, x+size, y+size,
fill=color, outline="white", width=2)
def _draw_track(self, track: Dict[str, Any]):
"""绘制轨迹"""
points = track.get("points", [])
if len(points) >= 2:
# 绘制轨迹线
for i in range(len(points)-1):
x1, y1 = points[i]
x2, y2 = points[i+1]
self.canvas.create_line(x1, y1, x2, y2,
fill="#F39C12", width=2, dash=(4, 2))
class SpectrumView(BaseView):
"""频谱监测视图"""
def create_ui(self, parent: tk.Widget):
# 创建频谱显示区域
self.canvas = tk.Canvas(parent, bg="#0F1C2E", highlightthickness=0)
self.canvas.pack(fill=tk.BOTH, expand=True)
# 创建控制面板
self._create_control_panel(parent)
def _create_control_panel(self, parent: tk.Widget):
control_frame = ttk.Frame(parent)
control_frame.pack(fill=tk.X, padx=5, pady=5)
# 频率范围控制
ttk.Label(control_frame, text="频率(MHz):").pack(side=tk.LEFT, padx=5)
self.freq_start = ttk.Entry(control_frame, width=10)
self.freq_start.insert(0, "1000")
self.freq_start.pack(side=tk.LEFT, padx=2)
ttk.Label(control_frame, text="-").pack(side=tk.LEFT)
self.freq_end = ttk.Entry(control_frame, width=10)
self.freq_end.insert(0, "3000")
self.freq_end.pack(side=tk.LEFT, padx=2)
# 更新按钮
ttk.Button(control_frame, text="更新", command=self.update_spectrum).pack(side=tk.LEFT, padx=10)
def update_data(self, data: Dict[str, Any]):
if "spectrum" in data:
self._draw_spectrum(data["spectrum"])
def _draw_spectrum(self, data: Dict[str, Any]):
"""绘制频谱"""
self.canvas.delete("all")
width = self.canvas.winfo_width()
height = self.canvas.winfo_height()
if width < 10 or height < 10:
return
frequencies = data.get("frequencies", [])
amplitudes = data.get("amplitudes", [])
if not frequencies or not amplitudes:
return
# 计算坐标映射
x_scale = width / (max(frequencies) - min(frequencies))
y_scale = height / (max(amplitudes) - min(amplitudes)) if max(amplitudes) > min(amplitudes) else 1
# 绘制频谱曲线
points = []
for i, (freq, amp) in enumerate(zip(frequencies, amplitudes)):
x = (freq - min(frequencies)) * x_scale
y = height - (amp - min(amplitudes)) * y_scale
points.extend([x, y])
if len(points) >= 4:
self.canvas.create_line(points, fill="#2ECC71", width=2, smooth=True)
# 绘制频率网格
self._draw_frequency_grid(frequencies, width, height)
def _draw_frequency_grid(self, frequencies, width, height):
"""绘制频率网格"""
if not frequencies:
return
freq_min = min(frequencies)
freq_max = max(frequencies)
# 计算主要频率刻度
freq_range = freq_max - freq_min
major_step = 10 ** (int(freq_range / 10))
for freq in range(int(freq_min), int(freq_max) + 1, major_step):
if freq_min <= freq <= freq_max:
x = (freq - freq_min) / (freq_max - freq_min) * width
self.canvas.create_line(x, 0, x, height, fill="#2C3E50", width=1)
self.canvas.create_text(x, height-15, text=f"{freq}MHz",
fill="#7F8C8D", font=("Arial", 8))
def update_spectrum(self):
"""更新频谱显示"""
# 这里可以触发数据更新
self.emit_event("spectrum_update", {
"start": float(self.freq_start.get()),
"end": float(self.freq_end.get())
})
# 其他视图的简化定义
class ResourceView(BaseView):
"""资源管理视图"""
def create_ui(self, parent: tk.Widget):
self.tree = ttk.Treeview(parent, columns=("状态", "类型", "位置"), show="tree headings")
self.tree.pack(fill=tk.BOTH, expand=True)
def update_data(self, data: Dict[str, Any]):
pass
class LogView(BaseView):
"""日志信息视图"""
def create_ui(self, parent: tk.Widget):
self.text = tk.Text(parent, bg="#1C2833", fg="#ECF0F1", font=("Consolas", 10))
scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.text.yview)
self.text.configure(yscrollcommand=scrollbar.set)
self.text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def update_data(self, data: Dict[str, Any]):
if "log" in data:
self.text.insert(tk.END, f"{data['log']}\n")
self.text.see(tk.END)
class AlertView(BaseView):
"""告警显示视图"""
def create_ui(self, parent: tk.Widget):
self.listbox = tk.Listbox(parent, bg="#1C2833", fg="#ECF0F1", font=("Consolas", 10))
scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.listbox.yview)
self.listbox.configure(yscrollcommand=scrollbar.set)
self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def update_data(self, data: Dict[str, Any]):
if "alert" in data:
self.listbox.insert(0, f"[{data.get('time', '')}] {data['alert']}")
class AnalysisView(BaseView):
"""分析视图"""
def create_ui(self, parent: tk.Widget):
self.canvas = tk.Canvas(parent, bg="#1C2833", highlightthickness=0)
self.canvas.pack(fill=tk.BOTH, expand=True)
def update_data(self, data: Dict[str, Any]):
pass
3.2 视图协同与通信机制
python
"""
view_coordination.py
视图协同与通信机制
"""
import threading
import queue
import time
from typing import Dict, List, Any, Optional, Set
from enum import Enum
from dataclasses import dataclass
class CoordinationMode(Enum):
"""协同模式"""
INDEPENDENT = "independent" # 独立
LINKED = "linked" # 链接
SYNC = "sync" # 同步
MASTER_SLAVE = "master_slave" # 主从
@dataclass
class ViewLink:
"""视图链接"""
source_view: str
target_view: str
link_type: str
data_fields: List[str]
update_mode: str = "push" # push, pull, both
class ViewCoordinator:
"""视图协调器"""
def __init__(self):
self.links: Dict[str, List[ViewLink]] = {}
self.coordination_groups: Dict[str, Set[str]] = {}
self.data_cache: Dict[str, Any] = {}
self.message_queue = queue.Queue()
self.running = False
def add_link(self, link: ViewLink):
"""添加视图链接"""
if link.source_view not in self.links:
self.links[link.source_view] = []
self.links[link.source_view].append(link)
def remove_link(self, source_view: str, target_view: str):
"""移除视图链接"""
if source_view in self.links:
self.links[source_view] = [
link for link in self.links[source_view]
if link.target_view != target_view
]
def create_coordination_group(self, group_id: str, view_ids: List[str]):
"""创建协同组"""
self.coordination_groups[group_id] = set(view_ids)
def coordinate_views(self, source_view_id: str, data: Dict[str, Any]):
"""协调视图"""
if source_view_id in self.links:
for link in self.links[source_view_id]:
self._propagate_data(link, data)
def _propagate_data(self, link: ViewLink, data: Dict[str, Any]):
"""传播数据"""
filtered_data = {}
for field in link.data_fields:
if field in data:
filtered_data[field] = data[field]
if filtered_data:
# 在实际应用中,这里会调用目标视图的更新方法
self.message_queue.put({
"target": link.target_view,
"data": filtered_data,
"link_type": link.link_type
})
def start(self):
"""启动协调器"""
self.running = True
self.thread = threading.Thread(target=self._run, daemon=True)
self.thread.start()
def stop(self):
"""停止协调器"""
self.running = False
def _run(self):
"""运行协调器"""
while self.running:
try:
message = self.message_queue.get(timeout=0.1)
self._process_message(message)
except queue.Empty:
continue
def _process_message(self, message: Dict[str, Any]):
"""处理消息"""
# 这里会调用具体的视图更新方法
target_view = message["target"]
data = message["data"]
link_type = message["link_type"]
# 在实际系统中,这里会分发到具体的视图
print(f"协调器: 向视图 {target_view} 发送 {link_type} 数据: {data}")
class ViewSyncManager:
"""视图同步管理器"""
def __init__(self):
self.sync_groups: Dict[str, Dict] = {}
self.view_states: Dict[str, Dict] = {}
def create_sync_group(self, group_id: str, config: Dict[str, Any]):
"""创建同步组"""
self.sync_groups[group_id] = {
"config": config,
"views": set(),
"sync_data": {}
}
def add_view_to_sync(self, group_id: str, view_id: str):
"""添加视图到同步组"""
if group_id in self.sync_groups:
self.sync_groups[group_id]["views"].add(view_id)
def sync_view_state(self, view_id: str, state: Dict[str, Any]):
"""同步视图状态"""
self.view_states[view_id] = state
# 找到视图所在的同步组
for group_id, group in self.sync_groups.items():
if view_id in group["views"]:
self._propagate_sync(group_id, view_id, state)
def _propagate_sync(self, group_id: str, source_view: str, state: Dict[str, Any]):
"""传播同步"""
group = self.sync_groups[group_id]
sync_config = group["config"]
for view_id in group["views"]:
if view_id != source_view:
# 根据配置同步状态
sync_data = self._prepare_sync_data(state, sync_config)
# 发送同步数据
self._send_sync_data(view_id, sync_data)
def _prepare_sync_data(self, state: Dict[str, Any], config: Dict[str, Any]) -> Dict[str, Any]:
"""准备同步数据"""
sync_data = {}
# 根据配置过滤和转换数据
sync_fields = config.get("sync_fields", [])
if sync_fields:
for field in sync_fields:
if field in state:
sync_data[field] = state[field]
else:
# 同步所有字段
sync_data = state.copy()
return sync_data
def _send_sync_data(self, view_id: str, data: Dict[str, Any]):
"""发送同步数据"""
# 在实际系统中,这里会调用视图的同步方法
print(f"同步管理器: 向视图 {view_id} 发送同步数据")
4. 实时数据流处理框架
4.1 数据流水线架构
python
"""
data_pipeline.py
实时数据流处理框架
"""
import asyncio
import threading
import queue
import time
from typing import Dict, List, Any, Optional, Callable, Set
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
import numpy as np
from collections import deque
import json
class DataType(Enum):
"""数据类型"""
TELEMETRY = "telemetry" # 遥测数据
SPECTRUM = "spectrum" # 频谱数据
TARGET = "target" # 目标数据
ALERT = "alert" # 告警数据
LOG = "log" # 日志数据
COMMAND = "command" # 命令数据
STATUS = "status" # 状态数据
class ProcessingStage(Enum):
"""处理阶段"""
RAW = "raw" # 原始数据
FILTERED = "filtered" # 滤波后
ENHANCED = "enhanced" # 增强后
ANALYZED = "analyzed" # 分析后
VISUALIZED = "visualized" # 可视化
@dataclass
class DataPacket:
"""数据包"""
id: str
type: DataType
source: str
timestamp: float = field(default_factory=time.time)
data: Any = None
metadata: Dict[str, Any] = field(default_factory=dict)
processing_stage: ProcessingStage = ProcessingStage.RAW
@dataclass
class PipelineConfig:
"""流水线配置"""
name: str
stages: List[str]
buffer_size: int = 1000
processing_rate: float = 100.0 # Hz
parallel_workers: int = 1
class DataSource(ABC):
"""数据源基类"""
def __init__(self, source_id: str, config: Dict[str, Any]):
self.source_id = source_id
self.config = config
self.callbacks: List[Callable] = []
self.running = False
self.data_count = 0
@abstractmethod
def start(self):
"""启动数据源"""
pass
@abstractmethod
def stop(self):
"""停止数据源"""
pass
def register_callback(self, callback: Callable):
"""注册回调函数"""
self.callbacks.append(callback)
def emit_data(self, data: Any, metadata: Optional[Dict] = None):
"""发射数据"""
packet = DataPacket(
id=f"{self.source_id}_{self.data_count}",
type=DataType.TELEMETRY,
source=self.source_id,
data=data,
metadata=metadata or {}
)
for callback in self.callbacks:
try:
callback(packet)
except Exception as e:
print(f"数据源 {self.source_id} 回调错误: {e}")
self.data_count += 1
class ProcessingStageBase(ABC):
"""处理阶段基类"""
def __init__(self, stage_id: str, config: Dict[str, Any]):
self.stage_id = stage_id
self.config = config
self.input_queue = queue.Queue(maxsize=1000)
self.output_queue = queue.Queue(maxsize=1000)
self.running = False
self.processing_time = 0.0
self.processed_count = 0
@abstractmethod
def process(self, packet: DataPacket) -> DataPacket:
"""处理数据包"""
pass
def start(self):
"""启动处理阶段"""
self.running = True
self.thread = threading.Thread(target=self._run, daemon=True)
self.thread.start()
def stop(self):
"""停止处理阶段"""
self.running = False
def _run(self):
"""运行处理循环"""
while self.running:
try:
packet = self.input_queue.get(timeout=0.1)
start_time = time.time()
processed_packet = self.process(packet)
processing_time = time.time() - start_time
self.processing_time += processing_time
self.processed_count += 1
self.output_queue.put(processed_packet)
except queue.Empty:
continue
except Exception as e:
print(f"处理阶段 {self.stage_id} 错误: {e}")
def get_stats(self) -> Dict[str, Any]:
"""获取统计信息"""
avg_time = self.processing_time / self.processed_count if self.processed_count > 0 else 0
queue_size = self.input_queue.qsize()
return {
"processed_count": self.processed_count,
"avg_processing_time": avg_time,
"input_queue_size": queue_size,
"throughput": self.processed_count / (time.time() - self.start_time)
if hasattr(self, 'start_time') else 0
}
class FilterStage(ProcessingStageBase):
"""滤波阶段"""
def __init__(self, stage_id: str, config: Dict[str, Any]):
super().__init__(stage_id, config)
self.filter_type = config.get("filter_type", "lowpass")
self.filter_params = config.get("filter_params", {})
def process(self, packet: DataPacket) -> DataPacket:
# 应用滤波器
if isinstance(packet.data, np.ndarray):
filtered_data = self._apply_filter(packet.data)
else:
filtered_data = packet.data
return DataPacket(
id=packet.id + "_filtered",
type=packet.type,
source=packet.source,
timestamp=packet.timestamp,
data=filtered_data,
metadata={**packet.metadata, "filter_type": self.filter_type},
processing_stage=ProcessingStage.FILTERED
)
def _apply_filter(self, data: np.ndarray) -> np.ndarray:
"""应用滤波器"""
if self.filter_type == "lowpass":
return self._lowpass_filter(data)
elif self.filter_type == "highpass":
return self._highpass_filter(data)
elif self.filter_type == "bandpass":
return self._bandpass_filter(data)
else:
return data
def _lowpass_filter(self, data: np.ndarray) -> np.ndarray:
"""低通滤波"""
# 简化实现
window_size = self.filter_params.get("window_size", 5)
if len(data) > window_size:
kernel = np.ones(window_size) / window_size
return np.convolve(data, kernel, mode='same')
return data
class EnhancementStage(ProcessingStageBase):
"""增强阶段"""
def process(self, packet: DataPacket) -> DataPacket:
# 数据增强处理
enhanced_data = self._enhance_data(packet.data)
return DataPacket(
id=packet.id + "_enhanced",
type=packet.type,
source=packet.source,
timestamp=packet.timestamp,
data=enhanced_data,
metadata={**packet.metadata, "enhancement": "applied"},
processing_stage=ProcessingStage.ENHANCED
)
def _enhance_data(self, data: Any) -> Any:
"""增强数据"""
if isinstance(data, np.ndarray):
# 归一化
if np.max(data) > np.min(data):
return (data - np.min(data)) / (np.max(data) - np.min(data))
return data
class AnalysisStage(ProcessingStageBase):
"""分析阶段"""
def process(self, packet: DataPacket) -> DataPacket:
# 数据分析
analysis_results = self._analyze_data(packet.data)
return DataPacket(
id=packet.id + "_analyzed",
type=packet.type,
source=packet.source,
timestamp=packet.timestamp,
data=analysis_results,
metadata={**packet.metadata, "analysis": "completed"},
processing_stage=ProcessingStage.ANALYZED
)
def _analyze_data(self, data: Any) -> Dict[str, Any]:
"""分析数据"""
if isinstance(data, np.ndarray):
return {
"mean": float(np.mean(data)),
"std": float(np.std(data)),
"max": float(np.max(data)),
"min": float(np.min(data)),
"rms": float(np.sqrt(np.mean(data**2))),
"peak_to_peak": float(np.max(data) - np.min(data))
}
elif isinstance(data, dict):
return data
else:
return {"value": data}
class DataPipeline:
"""数据流水线"""
def __init__(self, config: PipelineConfig):
self.config = config
self.stages: Dict[str, ProcessingStageBase] = {}
self.sources: Dict[str, DataSource] = {}
self.sinks: Dict[str, List[Callable]] = {}
self.running = False
def add_stage(self, stage: ProcessingStageBase):
"""添加处理阶段"""
self.stages[stage.stage_id] = stage
def add_source(self, source: DataSource):
"""添加数据源"""
self.sources[source.source_id] = source
def register_sink(self, sink_id: str, callback: Callable):
"""注册数据接收器"""
if sink_id not in self.sinks:
self.sinks[sink_id] = []
self.sinks[sink_id].append(callback)
def start(self):
"""启动流水线"""
self.running = True
# 启动所有处理阶段
for stage in self.stages.values():
stage.start()
# 启动所有数据源
for source in self.sources.values():
source.start()
# 将数据源连接到第一个处理阶段
if self.config.stages:
first_stage = self.stages.get(self.config.stages[0])
if first_stage:
source.register_callback(first_stage.input_queue.put)
# 连接处理阶段
self._connect_stages()
# 启动监控线程
self.monitor_thread = threading.Thread(target=self._monitor, daemon=True)
self.monitor_thread.start()
def stop(self):
"""停止流水线"""
self.running = False
for source in self.sources.values():
source.stop()
for stage in self.stages.values():
stage.stop()
def _connect_stages(self):
"""连接处理阶段"""
for i in range(len(self.config.stages) - 1):
current_stage = self.stages.get(self.config.stages[i])
next_stage = self.stages.get(self.config.stages[i + 1])
if current_stage and next_stage:
# 连接输出队列到输入队列
threading.Thread(
target=self._forward_data,
args=(current_stage.output_queue, next_stage.input_queue),
daemon=True
).start()
# 连接最后一个阶段到接收器
if self.config.stages:
last_stage = self.stages.get(self.config.stages[-1])
if last_stage and self.sinks:
threading.Thread(
target=self._deliver_to_sinks,
args=(last_stage.output_queue,),
daemon=True
).start()
def _forward_data(self, source_queue: queue.Queue, target_queue: queue.Queue):
"""转发数据"""
while self.running:
try:
data = source_queue.get(timeout=0.1)
target_queue.put(data)
except queue.Empty:
continue
def _deliver_to_sinks(self, source_queue: queue.Queue):
"""分发数据到接收器"""
while self.running:
try:
data = source_queue.get(timeout=0.1)
for sink_callbacks in self.sinks.values():
for callback in sink_callbacks:
try:
callback(data)
except Exception as e:
print(f"数据接收器错误: {e}")
except queue.Empty:
continue
def _monitor(self):
"""监控流水线"""
while self.running:
stats = self.get_pipeline_stats()
# 这里可以添加监控逻辑,如告警、日志等
if stats["throughput"] < self.config.processing_rate * 0.5:
print(f"警告: 流水线吞吐量低于预期: {stats['throughput']:.1f} packets/s")
time.sleep(5.0)
def get_pipeline_stats(self) -> Dict[str, Any]:
"""获取流水线统计信息"""
total_processed = 0
total_time = 0.0
stage_stats = {}
for stage_id, stage in self.stages.items():
stats = stage.get_stats()
stage_stats[stage_id] = stats
total_processed += stats.get("processed_count", 0)
total_time += stats.get("avg_processing_time", 0) * stats.get("processed_count", 0)
avg_time = total_time / total_processed if total_processed > 0 else 0
return {
"total_processed": total_processed,
"avg_processing_time": avg_time,
"stage_stats": stage_stats,
"source_count": len(self.sources),
"sink_count": sum(len(callbacks) for callbacks in self.sinks.values()),
"throughput": total_processed / (time.time() - self.start_time)
if hasattr(self, 'start_time') else 0
}
class TelemetrySource(DataSource):
"""遥测数据源"""
def start(self):
self.running = True
self.thread = threading.Thread(target=self._generate_data, daemon=True)
self.thread.start()
def stop(self):
self.running = False
def _generate_data(self):
"""生成遥测数据"""
import random
while self.running:
# 生成模拟遥测数据
telemetry_data = {
"timestamp": time.time(),
"position": {
"x": random.uniform(-1000, 1000),
"y": random.uniform(-1000, 1000),
"z": random.uniform(0, 10000)
},
"velocity": {
"vx": random.uniform(-100, 100),
"vy": random.uniform(-100, 100),
"vz": random.uniform(-10, 10)
},
"attitude": {
"roll": random.uniform(-180, 180),
"pitch": random.uniform(-90, 90),
"yaw": random.uniform(0, 360)
},
"system_status": {
"power": random.uniform(0, 100),
"temperature": random.uniform(20, 80),
"cpu_usage": random.uniform(0, 100)
}
}
self.emit_data(telemetry_data)
# 控制数据生成速率
time.sleep(1.0 / self.config.get("rate", 10.0))
class SpectrumSource(DataSource):
"""频谱数据源"""
def start(self):
self.running = True
self.thread = threading.Thread(target=self._generate_spectrum, daemon=True)
self.thread.start()
def _generate_spectrum(self):
"""生成频谱数据"""
import random
while self.running:
# 生成模拟频谱数据
frequencies = np.linspace(1000, 3000, 1000)
# 添加一些信号
amplitudes = np.random.randn(len(frequencies)) * 0.1
# 添加几个信号峰值
peaks = [
(1500, 1.0, 10),
(2200, 0.8, 15),
(2800, 0.6, 8)
]
for freq, amp, width in peaks:
idx = np.argmin(np.abs(frequencies - freq))
if 0 <= idx < len(amplitudes):
# 添加高斯峰
x = np.linspace(-3, 3, width*2+1)
gaussian = amp * np.exp(-x**2)
start = max(0, idx - width)
end = min(len(amplitudes), idx + width + 1)
gaussian_start = max(0, width - (idx - start))
gaussian_end = min(len(gaussian), width + (end - idx))
amplitudes[start:end] += gaussian[gaussian_start:gaussian_end]
spectrum_data = {
"frequencies": frequencies.tolist(),
"amplitudes": amplitudes.tolist(),
"timestamp": time.time(),
"center_frequency": 2000,
"bandwidth": 2000
}
self.emit_data(spectrum_data)
time.sleep(1.0 / self.config.get("rate", 20.0))
4.2 数据可视化流水线
python
"""
visualization_pipeline.py
数据可视化流水线
"""
from typing import Dict, List, Any, Optional, Tuple
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
import io
from PIL import Image
import tkinter as tk
class VisualizationStage(ProcessingStageBase):
"""可视化阶段"""
def __init__(self, stage_id: str, config: Dict[str, Any]):
super().__init__(stage_id, config)
self.viz_type = config.get("viz_type", "plot")
self.figure_config = config.get("figure", {})
def process(self, packet: DataPacket) -> DataPacket:
# 生成可视化图像
image_data = self._create_visualization(packet.data, packet.metadata)
return DataPacket(
id=packet.id + "_visualized",
type=packet.type,
source=packet.source,
timestamp=packet.timestamp,
data=image_data,
metadata={**packet.metadata, "viz_type": self.viz_type},
processing_stage=ProcessingStage.VISUALIZED
)
def _create_visualization(self, data: Any, metadata: Dict[str, Any]) -> bytes:
"""创建可视化"""
if self.viz_type == "plot":
return self._create_plot(data, metadata)
elif self.viz_type == "heatmap":
return self._create_heatmap(data, metadata)
elif self.viz_type == "3d":
return self._create_3d_plot(data, metadata)
else:
return self._create_default_plot(data, metadata)
def _create_plot(self, data: Any, metadata: Dict[str, Any]) -> bytes:
"""创建折线图"""
fig, ax = plt.subplots(figsize=(8, 4))
if isinstance(data, dict) and "frequencies" in data and "amplitudes" in data:
# 频谱图
frequencies = data["frequencies"]
amplitudes = data["amplitudes"]
ax.plot(frequencies, amplitudes, 'b-', linewidth=1)
ax.set_xlabel("频率 (MHz)")
ax.set_ylabel("幅度 (dB)")
ax.set_title("频谱图")
ax.grid(True, alpha=0.3)
elif isinstance(data, np.ndarray):
# 时间序列
ax.plot(data, 'g-', linewidth=1)
ax.set_xlabel("时间")
ax.set_ylabel("值")
ax.set_title("时间序列")
ax.grid(True, alpha=0.3)
else:
ax.text(0.5, 0.5, "不支持的数据类型",
ha='center', va='center', transform=ax.transAxes)
fig.tight_layout()
# 转换为图像数据
return self._figure_to_bytes(fig)
def _create_heatmap(self, data: Any, metadata: Dict[str, Any]) -> bytes:
"""创建热力图"""
fig, ax = plt.subplots(figsize=(8, 6))
if isinstance(data, np.ndarray) and data.ndim == 2:
im = ax.imshow(data, cmap='hot', aspect='auto')
fig.colorbar(im, ax=ax)
ax.set_title("热力图")
elif isinstance(data, dict) and "matrix" in data:
matrix = np.array(data["matrix"])
im = ax.imshow(matrix, cmap='viridis', aspect='auto')
fig.colorbar(im, ax=ax)
ax.set_title(data.get("title", "热力图"))
else:
ax.text(0.5, 0.5, "不支持的热力图数据",
ha='center', va='center', transform=ax.transAxes)
fig.tight_layout()
return self._figure_to_bytes(fig)
def _create_3d_plot(self, data: Any, metadata: Dict[str, Any]) -> bytes:
"""创建3D图"""
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
if isinstance(data, dict) and "x" in data and "y" in data and "z" in data:
# 3D散点图
x = np.array(data["x"])
y = np.array(data["y"])
z = np.array(data["z"])
if "c" in data:
c = np.array(data["c"])
scatter = ax.scatter(x, y, z, c=c, cmap='viridis', s=20)
fig.colorbar(scatter, ax=ax)
else:
ax.scatter(x, y, z, s=20)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("3D散点图")
elif isinstance(data, np.ndarray) and data.ndim == 2 and data.shape[1] == 3:
# 3D点云
x = data[:, 0]
y = data[:, 1]
z = data[:, 2]
ax.scatter(x, y, z, s=5)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("3D点云")
elif isinstance(data, dict) and "surface" in data:
# 3D曲面
x = np.array(data.get("x", np.arange(100)))
y = np.array(data.get("y", np.arange(100)))
Z = np.array(data["surface"])
if Z.ndim == 2:
X, Y = np.meshgrid(x, y)
surf = ax.plot_surface(X, Y, Z, cmap='coolwarm', alpha=0.8)
fig.colorbar(surf, ax=ax)
ax.set_title("3D曲面")
else:
ax.text(0.5, 0.5, 0.5, "无效的曲面数据",
ha='center', va='center', transform=ax.transAxes)
else:
ax.text(0.5, 0.5, 0.5, "不支持3D显示的数据",
ha='center', va='center', transform=ax.transAxes)
fig.tight_layout()
return self._figure_to_bytes(fig)
def _create_default_plot(self, data: Any, metadata: Dict[str, Any]) -> bytes:
"""创建默认图"""
fig, ax = plt.subplots(figsize=(8, 4))
if isinstance(data, (list, np.ndarray)):
ax.plot(data, 'r-', linewidth=1)
ax.set_title("数据曲线")
elif isinstance(data, dict):
# 尝试绘制字典中的第一个数组
for key, value in data.items():
if isinstance(value, (list, np.ndarray)):
ax.plot(value, label=key)
ax.legend()
ax.set_title("多数据曲线")
else:
ax.text(0.5, 0.5, f"数据: {str(data)[:50]}...",
ha='center', va='center', transform=ax.transAxes)
ax.set_xlabel("索引")
ax.set_ylabel("值")
ax.grid(True, alpha=0.3)
fig.tight_layout()
return self._figure_to_bytes(fig)
def _figure_to_bytes(self, fig) -> bytes:
"""将matplotlib图形转换为字节流"""
# 使用内存缓冲区
buf = io.BytesIO()
fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
plt.close(fig) # 关闭图形以释放内存
buf.seek(0)
return buf.read()
class ImageDisplaySink:
"""图像显示接收器(用于tkinter)"""
def __init__(self, parent: tk.Widget, width: int = 400, height: int = 300):
self.parent = parent
self.width = width
self.height = height
# 创建标签用于显示图像
self.image_label = tk.Label(parent, bg='black')
self.image_label.pack(fill=tk.BOTH, expand=True)
# 缓存图像引用
self.current_image = None
def display_image(self, image_data: bytes):
"""显示图像"""
try:
# 将字节流转换为PIL图像
from PIL import Image, ImageTk
import io
pil_image = Image.open(io.BytesIO(image_data))
# 调整大小以适应显示区域
pil_image.thumbnail((self.width, self.height), Image.Resampling.LANCZOS)
# 转换为PhotoImage
photo = ImageTk.PhotoImage(pil_image)
# 更新标签
self.image_label.configure(image=photo)
self.image_label.image = photo # 保持引用
# 保存当前图像引用
self.current_image = photo
except Exception as e:
print(f"显示图像错误: {e}")
def clear(self):
"""清除显示"""
self.image_label.configure(image='')
self.current_image = None
class DataPipelineManager:
"""数据流水线管理器"""
def __init__(self):
self.pipelines: Dict[str, DataPipeline] = {}
self.viz_displays: Dict[str, ImageDisplaySink] = {}
def create_telemetry_pipeline(self, pipeline_id: str,
display_widget: tk.Widget = None):
"""创建遥测数据流水线"""
config = PipelineConfig(
name="telemetry_pipeline",
stages=["filter", "enhance", "analyze", "visualize"],
buffer_size=1000,
processing_rate=50.0,
parallel_workers=2
)
pipeline = DataPipeline(config)
# 添加数据源
telemetry_source = TelemetrySource("telemetry_1", {"rate": 10.0})
pipeline.add_source(telemetry_source)
# 添加处理阶段
filter_stage = FilterStage("filter", {
"filter_type": "lowpass",
"filter_params": {"window_size": 5}
})
pipeline.add_stage(filter_stage)
enhance_stage = EnhancementStage("enhance", {})
pipeline.add_stage(enhance_stage)
analyze_stage = AnalysisStage("analyze", {})
pipeline.add_stage(analyze_stage)
visualize_stage = VisualizationStage("visualize", {
"viz_type": "plot"
})
pipeline.add_stage(visualize_stage)
# 添加显示接收器
if display_widget:
display_sink = ImageDisplaySink(display_widget)
self.viz_displays[pipeline_id] = display_sink
def display_callback(packet: DataPacket):
if isinstance(packet.data, bytes):
display_sink.display_image(packet.data)
pipeline.register_sink("display", display_callback)
self.pipelines[pipeline_id] = pipeline
return pipeline
def create_spectrum_pipeline(self, pipeline_id: str,
display_widget: tk.Widget = None):
"""创建频谱数据流水线"""
config = PipelineConfig(
name="spectrum_pipeline",
stages=["filter", "analyze", "visualize"],
buffer_size=2000,
processing_rate=100.0,
parallel_workers=1
)
pipeline = DataPipeline(config)
# 添加数据源
spectrum_source = SpectrumSource("spectrum_1", {"rate": 20.0})
pipeline.add_source(spectrum_source)
# 添加处理阶段
filter_stage = FilterStage("filter", {
"filter_type": "lowpass",
"filter_params": {"window_size": 3}
})
pipeline.add_stage(filter_stage)
analyze_stage = AnalysisStage("analyze", {})
pipeline.add_stage(analyze_stage)
visualize_stage = VisualizationStage("visualize", {
"viz_type": "plot"
})
pipeline.add_stage(visualize_stage)
# 添加显示接收器
if display_widget:
display_sink = ImageDisplaySink(display_widget)
self.viz_displays[pipeline_id] = display_sink
def display_callback(packet: DataPacket):
if isinstance(packet.data, bytes):
display_sink.display_image(packet.data)
pipeline.register_sink("display", display_callback)
self.pipelines[pipeline_id] = pipeline
return pipeline
def start_pipeline(self, pipeline_id: str):
"""启动流水线"""
if pipeline_id in self.pipelines:
self.pipelines[pipeline_id].start()
def stop_pipeline(self, pipeline_id: str):
"""停止流水线"""
if pipeline_id in self.pipelines:
self.pipelines[pipeline_id].stop()
def get_pipeline_stats(self, pipeline_id: str) -> Optional[Dict]:
"""获取流水线统计信息"""
if pipeline_id in self.pipelines:
return self.pipelines[pipeline_id].get_pipeline_stats()
return None
def clear_display(self, pipeline_id: str):
"""清除显示"""
if pipeline_id in self.viz_displays:
self.viz_displays[pipeline_id].clear()
5. 插件化架构设计与实现
5.1 插件管理系统
python
"""
plugin_system.py
插件化架构系统
"""
import os
import sys
import importlib
import inspect
from typing import Dict, List, Any, Optional, Type, Set
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
import json
import threading
import time
from pathlib import Path
class PluginType(Enum):
"""插件类型"""
VIEW = "view" # 视图插件
DATA_SOURCE = "data_source" # 数据源插件
PROCESSOR = "processor" # 处理器插件
VISUALIZATION = "visualization" # 可视化插件
TOOL = "tool" # 工具插件
EXTENSION = "extension" # 扩展插件
class PluginState(Enum):
"""插件状态"""
UNLOADED = "unloaded" # 未加载
LOADED = "loaded" # 已加载
ENABLED = "enabled" # 已启用
DISABLED = "disabled" # 已禁用
ERROR = "error" # 错误
@dataclass
class PluginInfo:
"""插件信息"""
id: str
name: str
type: PluginType
version: str
description: str
author: str
dependencies: List[str] = field(default_factory=list)
config_schema: Dict[str, Any] = field(default_factory=dict)
state: PluginState = PluginState.UNLOADED
path: str = ""
metadata: Dict[str, Any] = field(default_factory=dict)
class BasePlugin(ABC):
"""插件基类"""
def __init__(self, info: PluginInfo):
self.info = info
self.config: Dict[str, Any] = {}
@abstractmethod
def initialize(self, system: Any, config: Dict[str, Any]) -> bool:
"""初始化插件"""
pass
@abstractmethod
def start(self) -> bool:
"""启动插件"""
pass
@abstractmethod
def stop(self) -> bool:
"""停止插件"""
pass
@abstractmethod
def get_widget(self, parent: tk.Widget) -> Optional[tk.Widget]:
"""获取插件控件(用于视图插件)"""
pass
def update_config(self, config: Dict[str, Any]):
"""更新配置"""
self.config.update(config)
def get_status(self) -> Dict[str, Any]:
"""获取插件状态"""
return {
"id": self.info.id,
"name": self.info.name,
"state": self.info.state.value,
"config": self.config
}
class PluginManager:
"""插件管理器"""
def __init__(self, system: Any):
self.system = system
self.plugins: Dict[str, BasePlugin] = {}
self.plugin_info: Dict[str, PluginInfo] = {}
self.plugin_dirs: List[str] = []
self.plugin_loaders: Dict[str, Type] = {}
# 插件事件
self.event_handlers: Dict[str, List[Callable]] = {}
def add_plugin_dir(self, dir_path: str):
"""添加插件目录"""
if os.path.isdir(dir_path) and dir_path not in self.plugin_dirs:
self.plugin_dirs.append(dir_path)
def register_plugin_loader(self, plugin_type: str, loader_class: Type):
"""注册插件加载器"""
self.plugin_loaders[plugin_type] = loader_class
def discover_plugins(self) -> List[PluginInfo]:
"""发现插件"""
discovered_plugins = []
for plugin_dir in self.plugin_dirs:
if not os.path.isdir(plugin_dir):
continue
for item in os.listdir(plugin_dir):
plugin_path = os.path.join(plugin_dir, item)
# 检查是否是目录
if os.path.isdir(plugin_path):
# 检查是否有plugin.json文件
plugin_json = os.path.join(plugin_path, "plugin.json")
if os.path.isfile(plugin_json):
try:
with open(plugin_json, 'r', encoding='utf-8') as f:
plugin_data = json.load(f)
# 创建插件信息
plugin_info = PluginInfo(
id=plugin_data.get("id", ""),
name=plugin_data.get("name", ""),
type=PluginType(plugin_data.get("type", "extension")),
version=plugin_data.get("version", "1.0.0"),
description=plugin_data.get("description", ""),
author=plugin_data.get("author", ""),
dependencies=plugin_data.get("dependencies", []),
config_schema=plugin_data.get("config_schema", {}),
path=plugin_path,
metadata=plugin_data.get("metadata", {})
)
if plugin_info.id:
discovered_plugins.append(plugin_info)
except Exception as e:
print(f"加载插件信息失败 {plugin_path}: {e}")
return discovered_plugins
def load_plugin(self, plugin_info: PluginInfo) -> bool:
"""加载插件"""
try:
# 检查依赖
for dep in plugin_info.dependencies:
if dep not in self.plugins or self.plugins[dep].info.state != PluginState.ENABLED:
print(f"插件 {plugin_info.id} 依赖 {dep} 未满足")
return False
# 动态加载插件模块
plugin_dir = plugin_info.path
if not plugin_dir or not os.path.isdir(plugin_dir):
print(f"插件目录不存在: {plugin_dir}")
return False
# 添加到Python路径
if plugin_dir not in sys.path:
sys.path.insert(0, plugin_dir)
# 导入插件模块
module_name = plugin_info.metadata.get("module", "plugin")
try:
module = importlib.import_module(module_name)
except ImportError as e:
print(f"导入插件模块失败: {e}")
return False
# 查找插件类
plugin_class = None
for name, obj in inspect.getmembers(module):
if (inspect.isclass(obj) and
issubclass(obj, BasePlugin) and
obj != BasePlugin):
plugin_class = obj
break
if not plugin_class:
print(f"未找到插件类: {module_name}")
return False
# 创建插件实例
plugin = plugin_class(plugin_info)
# 初始化插件
config = plugin_info.metadata.get("config", {})
if plugin.initialize(self.system, config):
self.plugins[plugin_info.id] = plugin
self.plugin_info[plugin_info.id] = plugin_info
plugin_info.state = PluginState.LOADED
# 触发插件加载事件
self._emit_event("plugin_loaded", {
"plugin_id": plugin_info.id,
"plugin_info": plugin_info
})
return True
else:
print(f"插件初始化失败: {plugin_info.id}")
return False
except Exception as e:
print(f"加载插件 {plugin_info.id} 失败: {e}")
plugin_info.state = PluginState.ERROR
return False
def enable_plugin(self, plugin_id: str) -> bool:
"""启用插件"""
if plugin_id not in self.plugins:
print(f"插件未加载: {plugin_id}")
return False
plugin = self.plugins[plugin_id]
try:
if plugin.start():
plugin.info.state = PluginState.ENABLED
# 触发插件启用事件
self._emit_event("plugin_enabled", {
"plugin_id": plugin_id,
"plugin": plugin
})
return True
else:
print(f"插件启动失败: {plugin_id}")
plugin.info.state = PluginState.ERROR
return False
except Exception as e:
print(f"启用插件 {plugin_id} 失败: {e}")
plugin.info.state = PluginState.ERROR
return False
def disable_plugin(self, plugin_id: str) -> bool:
"""禁用插件"""
if plugin_id not in self.plugins:
print(f"插件未加载: {plugin_id}")
return False
plugin = self.plugins[plugin_id]
try:
if plugin.stop():
plugin.info.state = PluginState.DISABLED
# 触发插件禁用事件
self._emit_event("plugin_disabled", {
"plugin_id": plugin_id,
"plugin": plugin
})
return True
else:
print(f"插件停止失败: {plugin_id}")
return False
except Exception as e:
print(f"禁用插件 {plugin_id} 失败: {e}")
return False
def unload_plugin(self, plugin_id: str) -> bool:
"""卸载插件"""
if plugin_id not in self.plugins:
print(f"插件未加载: {plugin_id}")
return False
# 先禁用插件
if self.plugins[plugin_id].info.state == PluginState.ENABLED:
self.disable_plugin(plugin_id)
# 移除插件
del self.plugins[plugin_id]
del self.plugin_info[plugin_id]
# 触发插件卸载事件
self._emit_event("plugin_unloaded", {
"plugin_id": plugin_id
})
return True
def get_plugin(self, plugin_id: str) -> Optional[BasePlugin]:
"""获取插件"""
return self.plugins.get(plugin_id)
def get_plugin_info(self, plugin_id: str) -> Optional[PluginInfo]:
"""获取插件信息"""
return self.plugin_info.get(plugin_id)
def get_all_plugins(self) -> List[BasePlugin]:
"""获取所有插件"""
return list(self.plugins.values())
def get_enabled_plugins(self) -> List[BasePlugin]:
"""获取启用的插件"""
return [p for p in self.plugins.values()
if p.info.state == PluginState.ENABLED]
def register_event_handler(self, event_type: str, handler: Callable):
"""注册事件处理函数"""
if event_type not in self.event_handlers:
self.event_handlers[event_type] = []
self.event_handlers[event_type].append(handler)
def _emit_event(self, event_type: str, data: Any):
"""触发事件"""
if event_type in self.event_handlers:
for handler in self.event_handlers[event_type]:
try:
handler(data)
except Exception as e:
print(f"插件事件处理错误: {e}")
# 插件示例
class ExampleViewPlugin(BasePlugin):
"""示例视图插件"""
def __init__(self, info: PluginInfo):
super().__init__(info)
self.frame = None
self.canvas = None
def initialize(self, system: Any, config: Dict[str, Any]) -> bool:
self.config = config
return True
def start(self) -> bool:
return True
def stop(self) -> bool:
if self.frame:
self.frame.destroy()
return True
def get_widget(self, parent: tk.Widget) -> Optional[tk.Widget]:
"""获取插件控件"""
self.frame = ttk.Frame(parent)
# 创建标题
title = ttk.Label(self.frame,
text=self.info.name,
font=("微软雅黑", 12, "bold"))
title.pack(pady=10)
# 创建画布
self.canvas = tk.Canvas(self.frame, bg="#1C2833", height=200)
self.canvas.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 绘制示例内容
self._draw_example()
return self.frame
def _draw_example(self):
"""绘制示例内容"""
if not self.canvas:
return
width = 300
height = 150
# 绘制背景
self.canvas.create_rectangle(0, 0, width, height,
fill="#2C3E50", outline="")
# 绘制文本
self.canvas.create_text(width//2, height//2,
text="示例插件",
fill="#ECF0F1",
font=("微软雅黑", 16))
# 绘制进度条
progress = 0.7
bar_width = 200
bar_height = 20
bar_x = (width - bar_width) // 2
bar_y = height - 50
# 背景
self.canvas.create_rectangle(bar_x, bar_y,
bar_x + bar_width, bar_y + bar_height,
fill="#34495E", outline="")
# 进度
self.canvas.create_rectangle(bar_x, bar_y,
bar_x + bar_width * progress, bar_y + bar_height,
fill="#2ECC71", outline="")
# 进度文本
self.canvas.create_text(bar_x + bar_width//2, bar_y + bar_height//2,
text=f"{progress*100:.0f}%",
fill="#ECF0F1",
font=("微软雅黑", 10))
class ExampleDataSourcePlugin(BasePlugin):
"""示例数据源插件"""
def __init__(self, info: PluginInfo):
super().__init__(info)
self.data_callback = None
self.running = False
self.thread = None
def initialize(self, system: Any, config: Dict[str, Any]) -> bool:
self.system = system
self.config = config
return True
def start(self) -> bool:
self.running = True
self.thread = threading.Thread(target=self._generate_data, daemon=True)
self.thread.start()
return True
def stop(self) -> bool:
self.running = False
if self.thread:
self.thread.join(timeout=1.0)
return True
def _generate_data(self):
"""生成数据"""
import random
while self.running:
# 生成示例数据
data = {
"timestamp": time.time(),
"value": random.uniform(0, 100),
"status": "正常" if random.random() > 0.1 else "异常"
}
# 发送数据
if hasattr(self.system, 'data_bus'):
self.system.data_bus.send_data("example_data", data)
time.sleep(1.0)
def get_widget(self, parent: tk.Widget) -> Optional[tk.Widget]:
"""获取插件控件"""
frame = ttk.Frame(parent)
label = ttk.Label(frame, text="数据源插件: 运行中")
label.pack(pady=10)
return frame
class PluginBrowser:
"""插件浏览器界面"""
def __init__(self, parent: tk.Widget, plugin_manager: PluginManager):
self.parent = parent
self.plugin_manager = plugin_manager
self.create_ui()
def create_ui(self):
"""创建UI界面"""
# 主框架
main_frame = ttk.Frame(self.parent)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 标题
title = ttk.Label(main_frame,
text="插件管理器",
font=("微软雅黑", 16, "bold"))
title.pack(pady=(0, 10))
# 插件列表
list_frame = ttk.LabelFrame(main_frame, text="可用插件", padding=10)
list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
# 创建Treeview
columns = ("ID", "名称", "版本", "状态", "操作")
self.tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=10)
for col in columns:
self.tree.heading(col, text=col)
if col == "操作":
self.tree.column(col, width=150)
else:
self.tree.column(col, width=100)
scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscrollcommand=scrollbar.set)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 控制按钮
button_frame = ttk.Frame(main_frame)
button_frame.pack(fill=tk.X, pady=5)
ttk.Button(button_frame, text="刷新",
command=self.refresh_plugins).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="加载选中",
command=self.load_selected).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="启用选中",
command=self.enable_selected).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="禁用选中",
command=self.disable_selected).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="卸载选中",
command=self.unload_selected).pack(side=tk.LEFT, padx=5)
# 插件详情
detail_frame = ttk.LabelFrame(main_frame, text="插件详情", padding=10)
detail_frame.pack(fill=tk.BOTH, expand=True)
self.detail_text = tk.Text(detail_frame, height=8, width=60,
font=("Consolas", 9))
detail_scrollbar = ttk.Scrollbar(detail_frame, orient=tk.VERTICAL,
command=self.detail_text.yview)
self.detail_text.configure(yscrollcommand=detail_scrollbar.set)
self.detail_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
detail_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 绑定选择事件
self.tree.bind("<<TreeviewSelect>>", self.on_plugin_selected)
# 初始刷新
self.refresh_plugins()
def refresh_plugins(self):
"""刷新插件列表"""
# 清空现有项
for item in self.tree.get_children():
self.tree.delete(item)
# 发现插件
discovered_plugins = self.plugin_manager.discover_plugins()
# 添加已加载的插件
loaded_plugins = self.plugin_manager.get_all_plugins()
loaded_ids = {p.info.id for p in loaded_plugins}
# 合并插件列表
all_plugins = {}
for plugin_info in discovered_plugins:
all_plugins[plugin_info.id] = {
"info": plugin_info,
"loaded": plugin_info.id in loaded_ids
}
# 添加已加载但未发现的插件
for plugin in loaded_plugins:
if plugin.info.id not in all_plugins:
all_plugins[plugin.info.id] = {
"info": plugin.info,
"loaded": True
}
# 添加到Treeview
for plugin_id, plugin_data in all_plugins.items():
plugin_info = plugin_data["info"]
loaded = plugin_data["loaded"]
if loaded:
plugin = self.plugin_manager.get_plugin(plugin_id)
state = plugin.info.state.value if plugin else "未知"
else:
state = "未加载"
# 操作按钮文本
if loaded:
if plugin and plugin.info.state == PluginState.ENABLED:
action = "禁用"
elif plugin and plugin.info.state == PluginState.DISABLED:
action = "启用"
else:
action = "卸载"
else:
action = "加载"
self.tree.insert("", tk.END, values=(
plugin_info.id,
plugin_info.name,
plugin_info.version,
state,
action
))
def on_plugin_selected(self, event):
"""插件选择事件"""
selection = self.tree.selection()
if not selection:
return
item = selection[0]
values = self.tree.item(item, "values")
plugin_id = values[0]
# 获取插件信息
plugin_info = self.plugin_manager.get_plugin_info(plugin_id)
if not plugin_info:
# 可能是未加载的插件,从发现列表中查找
discovered_plugins = self.plugin_manager.discover_plugins()
for p in discovered_plugins:
if p.id == plugin_id:
plugin_info = p
break
if plugin_info:
# 显示详情
detail = f"ID: {plugin_info.id}\n"
detail += f"名称: {plugin_info.name}\n"
detail += f"类型: {plugin_info.type.value}\n"
detail += f"版本: {plugin_info.version}\n"
detail += f"作者: {plugin_info.author}\n"
detail += f"描述: {plugin_info.description}\n"
detail += f"依赖: {', '.join(plugin_info.dependencies)}\n"
detail += f"路径: {plugin_info.path}\n"
self.detail_text.delete(1.0, tk.END)
self.detail_text.insert(1.0, detail)
def load_selected(self):
"""加载选中的插件"""
selection = self.tree.selection()
if not selection:
return
item = selection[0]
values = self.tree.item(item, "values")
plugin_id = values[0]
# 查找插件信息
discovered_plugins = self.plugin_manager.discover_plugins()
plugin_info = None
for p in discovered_plugins:
if p.id == plugin_id:
plugin_info = p
break
if plugin_info:
if self.plugin_manager.load_plugin(plugin_info):
self.refresh_plugins()
def enable_selected(self):
"""启用选中的插件"""
selection = self.tree.selection()
if not selection:
return
item = selection[0]
values = self.tree.item(item, "values")
plugin_id = values[0]
if self.plugin_manager.enable_plugin(plugin_id):
self.refresh_plugins()
def disable_selected(self):
"""禁用选中的插件"""
selection = self.tree.selection()
if not selection:
return
item = selection[0]
values = self.tree.item(item, "values")
plugin_id = values[0]
if self.plugin_manager.disable_plugin(plugin_id):
self.refresh_plugins()
def unload_selected(self):
"""卸载选中的插件"""
selection = self.tree.selection()
if not selection:
return
item = selection[0]
values = self.tree.item(item, "values")
plugin_id = values[0]
if self.plugin_manager.unload_plugin(plugin_id):
self.refresh_plugins()
6. 高级主题引擎与皮肤系统
6.1 完整的主题引擎实现
python
"""
theme_engine.py
高级主题引擎与皮肤系统
"""
import tkinter as tk
from tkinter import ttk
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass, field
from enum import Enum
import json
import colorsys
import os
from pathlib import Path
class ThemeType(Enum):
"""主题类型"""
LIGHT = "light" # 浅色主题
DARK = "dark" # 深色主题
HIGH_CONTRAST = "high_contrast" # 高对比度
CUSTOM = "custom" # 自定义主题
@dataclass
class ColorScheme:
"""颜色方案"""
primary: str = "#3498DB"
secondary: str = "#2ECC71"
accent: str = "#E74C3C"
background: str = "#ECF0F1"
surface: str = "#FFFFFF"
error: str = "#E74C3C"
warning: str = "#F39C12"
success: str = "#27AE60"
info: str = "#3498DB"
text_primary: str = "#2C3E50"
text_secondary: str = "#7F8C8D"
text_disabled: str = "#BDC3C7"
border: str = "#BDC3C7"
divider: str = "#ECF0F1"
hover: str = "#F8F9FA"
selected: str = "#E3F2FD"
pressed: str = "#BBDEFB"
def to_dict(self) -> Dict[str, str]:
"""转换为字典"""
return {k: v for k, v in self.__dict__.items()
if not k.startswith('_')}
@classmethod
def from_dict(cls, data: Dict[str, str]) -> 'ColorScheme':
"""从字典创建"""
return cls(**{k: v for k, v in data.items()
if k in cls.__annotations__})
@dataclass
class Typography:
"""排版设置"""
font_family: str = "微软雅黑, Segoe UI, Arial"
font_size_small: int = 10
font_size_normal: int = 12
font_size_large: int = 14
font_size_title: int = 18
font_weight_normal: str = "normal"
font_weight_bold: str = "bold"
line_height: float = 1.5
@dataclass
class Spacing:
"""间距设置"""
xs: int = 4
sm: int = 8
md: int = 12
lg: int = 16
xl: int = 24
xxl: int = 32
@dataclass
class Shape:
"""形状设置"""
border_radius_sm: int = 4
border_radius_md: int = 8
border_radius_lg: int = 12
border_width: int = 1
@dataclass
class Shadow:
"""阴影设置"""
sm: str = "0 1px 3px rgba(0,0,0,0.12)"
md: str = "0 4px 6px rgba(0,0,0,0.1)"
lg: str = "0 10px 15px rgba(0,0,0,0.1)"
xl: str = "0 20px 25px rgba(0,0,0,0.1)"
@dataclass
class Theme:
"""主题"""
id: str
name: str
type: ThemeType
description: str = ""
colors: ColorScheme = field(default_factory=ColorScheme)
typography: Typography = field(default_factory=Typography)
spacing: Spacing = field(default_factory=Spacing)
shape: Shape = field(default_factory=Shape)
shadow: Shadow = field(default_factory=Shadow)
version: str = "1.0.0"
def to_dict(self) -> Dict[str, Any]:
"""转换为字典"""
return {
"id": self.id,
"name": self.name,
"type": self.type.value,
"description": self.description,
"version": self.version,
"colors": self.colors.to_dict(),
"typography": self.typography.__dict__,
"spacing": self.spacing.__dict__,
"shape": self.shape.__dict__,
"shadow": self.shadow.__dict__
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Theme':
"""从字典创建"""
theme = cls(
id=data["id"],
name=data["name"],
type=ThemeType(data["type"]),
description=data.get("description", ""),
version=data.get("version", "1.0.0")
)
if "colors" in data:
theme.colors = ColorScheme.from_dict(data["colors"])
if "typography" in data:
theme.typography = Typography(**data["typography"])
if "spacing" in data:
theme.spacing = Spacing(**data["spacing"])
if "shape" in data:
theme.shape = Shape(**data["shape"])
if "shadow" in data:
theme.shadow = Shadow(**data["shadow"])
return theme
class ThemeEngine:
"""主题引擎"""
def __init__(self):
self.themes: Dict[str, Theme] = {}
self.current_theme: Optional[Theme] = None
self.style = ttk.Style()
# 注册的主题变化回调
self.theme_change_callbacks: List[Callable] = []
# 加载内置主题
self.load_builtin_themes()
def load_builtin_themes(self):
"""加载内置主题"""
# 浅色主题
light_theme = Theme(
id="light",
name="浅色主题",
type=ThemeType.LIGHT,
description="适合日间使用的明亮主题",
colors=ColorScheme(
primary="#3498DB",
secondary="#2ECC71",
accent="#E74C3C",
background="#F8F9FA",
surface="#FFFFFF",
text_primary="#212529",
text_secondary="#6C757D",
border="#DEE2E6"
)
)
self.register_theme(light_theme)
# 深色主题
dark_theme = Theme(
id="dark",
name="深色主题",
type=ThemeType.DARK,
description="适合夜间使用的深色主题",
colors=ColorScheme(
primary="#3498DB",
secondary="#2ECC71",
accent="#E74C3C",
background="#1C2833",
surface="#2C3E50",
text_primary="#ECF0F1",
text_secondary="#BDC3C7",
border="#566573"
)
)
self.register_theme(dark_theme)
# 高对比度主题
hc_theme = Theme(
id="high_contrast",
name="高对比度主题",
type=ThemeType.HIGH_CONTRAST,
description="高可访问性高对比度主题",
colors=ColorScheme(
primary="#0000FF",
secondary="#008000",
accent="#FF0000",
background="#000000",
surface="#2A2A2A",
text_primary="#FFFFFF",
text_secondary="#CCCCCC",
border="#FFFFFF"
)
)
self.register_theme(hc_theme)
def register_theme(self, theme: Theme):
"""注册主题"""
self.themes[theme.id] = theme
def load_theme_from_file(self, filepath: str) -> Optional[Theme]:
"""从文件加载主题"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
theme = Theme.from_dict(data)
self.register_theme(theme)
return theme
except Exception as e:
print(f"加载主题文件失败 {filepath}: {e}")
return None
def save_theme_to_file(self, theme: Theme, filepath: str):
"""保存主题到文件"""
try:
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(theme.to_dict(), f, indent=2, ensure_ascii=False)
return True
except Exception as e:
print(f"保存主题文件失败 {filepath}: {e}")
return False
def apply_theme(self, theme_id: str) -> bool:
"""应用主题"""
if theme_id not in self.themes:
print(f"主题不存在: {theme_id}")
return False
theme = self.themes[theme_id]
self.current_theme = theme
# 应用tkinter样式
self._apply_tkinter_theme(theme)
# 触发主题变化回调
for callback in self.theme_change_callbacks:
try:
callback(theme)
except Exception as e:
print(f"主题变化回调错误: {e}")
return True
def _apply_tkinter_theme(self, theme: Theme):
"""应用tkinter样式"""
colors = theme.colors
# 配置基础样式
self.style.theme_use('clam')
# 配置全局样式
self.style.configure('.',
background=colors.background,
foreground=colors.text_primary,
fieldbackground=colors.surface,
troughcolor=colors.divider,
selectbackground=colors.selected,
selectforeground=colors.text_primary,
borderwidth=theme.shape.border_width,
focusthickness=3,
focuscolor=''
)
# 配置Frame
self.style.configure('TFrame',
background=colors.background
)
# 配置Label
self.style.configure('TLabel',
background=colors.background,
foreground=colors.text_primary,
font=(theme.typography.font_family, theme.typography.font_size_normal)
)
# 配置LabelFrame
self.style.configure('TLabelframe',
background=colors.background,
foreground=colors.text_primary,
bordercolor=colors.border,
relief='solid'
)
self.style.configure('TLabelframe.Label',
background=colors.background,
foreground=colors.text_primary
)
# 配置Button
self._configure_button_styles(theme)
# 配置Entry
self._configure_entry_styles(theme)
# 配置Combobox
self._configure_combobox_styles(theme)
# 配置Treeview
self._configure_treeview_styles(theme)
# 配置Progressbar
self._configure_progressbar_styles(theme)
# 配置Notebook
self._configure_notebook_styles(theme)
# 配置Scrollbar
self._configure_scrollbar_styles(theme)
def _configure_button_styles(self, theme: Theme):
"""配置按钮样式"""
colors = theme.colors
# 基础按钮
self.style.configure('TButton',
padding=[theme.spacing.md, theme.spacing.sm],
background=colors.primary,
foreground='white',
borderwidth=0,
focusthickness=3,
focuscolor=colors.primary
)
self.style.map('TButton',
background=[
('pressed', colors.pressed),
('active', colors.hover)
],
foreground=[
('pressed', colors.text_primary),
('active', colors.text_primary)
]
)
# 次级按钮
self.style.configure('Secondary.TButton',
background=colors.secondary,
foreground='white'
)
# 轮廓按钮
self.style.configure('Outline.TButton',
background='transparent',
foreground=colors.primary,
relief='solid',
borderwidth=theme.shape.border_width
)
self.style.map('Outline.TButton',
background=[
('pressed', colors.pressed),
('active', colors.hover)
]
)
# 文字按钮
self.style.configure('Text.TButton',
background='transparent',
foreground=colors.primary,
borderwidth=0,
padding=[theme.spacing.xs, theme.spacing.xs]
)
def _configure_entry_styles(self, theme: Theme):
"""配置输入框样式"""
colors = theme.colors
self.style.configure('TEntry',
fieldbackground=colors.surface,
foreground=colors.text_primary,
borderwidth=theme.shape.border_width,
relief='solid',
padding=[theme.spacing.sm, theme.spacing.xs],
insertcolor=colors.text_primary
)
self.style.map('TEntry',
fieldbackground=[
('readonly', colors.background),
('disabled', colors.divider)
],
foreground=[
('readonly', colors.text_secondary),
('disabled', colors.text_disabled)
]
)
def _configure_combobox_styles(self, theme: Theme):
"""配置组合框样式"""
colors = theme.colors
self.style.configure('TCombobox',
fieldbackground=colors.surface,
foreground=colors.text_primary,
borderwidth=theme.shape.border_width,
relief='solid',
padding=[theme.spacing.sm, theme.spacing.xs],
arrowsize=12
)
self.style.map('TCombobox',
fieldbackground=[
('readonly', colors.surface),
('disabled', colors.divider)
],
foreground=[
('readonly', colors.text_primary),
('disabled', colors.text_disabled)
]
)
def _configure_treeview_styles(self, theme: Theme):
"""配置Treeview样式"""
colors = theme.colors
self.style.configure('Treeview',
background=colors.surface,
foreground=colors.text_primary,
fieldbackground=colors.surface,
rowheight=25,
borderwidth=0
)
self.style.map('Treeview',
background=[('selected', colors.selected)],
foreground=[('selected', colors.text_primary)]
)
self.style.configure('Treeview.Heading',
background=colors.background,
foreground=colors.text_primary,
borderwidth=0,
relief='flat',
padding=[theme.spacing.sm, theme.spacing.xs]
)
def _configure_progressbar_styles(self, theme: Theme):
"""配置进度条样式"""
colors = theme.colors
self.style.configure('Horizontal.TProgressbar',
background=colors.primary,
troughcolor=colors.divider,
borderwidth=0,
lightcolor=colors.primary,
darkcolor=colors.primary
)
def _configure_notebook_styles(self, theme: Theme):
"""配置Notebook样式"""
colors = theme.colors
self.style.configure('TNotebook',
background=colors.background,
borderwidth=0,
tabmargins=[0, 0, 0, 0]
)
self.style.configure('TNotebook.Tab',
background=colors.divider,
foreground=colors.text_secondary,
borderwidth=0,
padding=[theme.spacing.lg, theme.spacing.sm],
font=(theme.typography.font_family, theme.typography.font_size_normal)
)
self.style.map('TNotebook.Tab',
background=[
('selected', colors.background),
('active', colors.hover)
],
foreground=[
('selected', colors.text_primary),
('active', colors.text_primary)
]
)
def _configure_scrollbar_styles(self, theme: Theme):
"""配置滚动条样式"""
colors = theme.colors
self.style.configure('Vertical.TScrollbar',
background=colors.divider,
troughcolor=colors.background,
borderwidth=0,
arrowsize=12
)
self.style.map('Vertical.TScrollbar',
background=[('active', colors.border)]
)
self.style.configure('Horizontal.TScrollbar',
background=colors.divider,
troughcolor=colors.background,
borderwidth=0,
arrowsize=12
)
def register_theme_change_callback(self, callback: Callable):
"""注册主题变化回调"""
self.theme_change_callbacks.append(callback)
def get_current_theme(self) -> Optional[Theme]:
"""获取当前主题"""
return self.current_theme
def get_all_themes(self) -> List[Theme]:
"""获取所有主题"""
return list(self.themes.values())
def create_custom_theme(self, base_theme_id: str,
name: str,
color_overrides: Dict[str, str]) -> Theme:
"""创建自定义主题"""
if base_theme_id not in self.themes:
raise ValueError(f"基础主题不存在: {base_theme_id}")
base_theme = self.themes[base_theme_id]
# 创建新主题
new_theme = Theme(
id=f"custom_{int(time.time())}",
name=name,
type=ThemeType.CUSTOM,
description=f"基于 {base_theme.name} 的自定义主题"
)
# 复制基础主题
new_theme.colors = ColorScheme(**base_theme.colors.__dict__)
new_theme.typography = Typography(**base_theme.typography.__dict__)
new_theme.spacing = Spacing(**base_theme.spacing.__dict__)
new_theme.shape = Shape(**base_theme.shape.__dict__)
new_theme.shadow = Shadow(**base_theme.shadow.__dict__)
# 应用颜色覆盖
for key, value in color_overrides.items():
if hasattr(new_theme.colors, key):
setattr(new_theme.colors, key, value)
# 注册新主题
self.register_theme(new_theme)
return new_theme
class ThemeEditor:
"""主题编辑器"""
def __init__(self, parent: tk.Widget, theme_engine: ThemeEngine):
self.parent = parent
self.theme_engine = theme_engine
self.current_theme: Optional[Theme] = None
self.create_ui()
def create_ui(self):
"""创建UI界面"""
# 主框架
main_frame = ttk.Frame(self.parent)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 标题
title = ttk.Label(main_frame,
text="主题编辑器",
font=("微软雅黑", 16, "bold"))
title.pack(pady=(0, 10))
# 控制面板
control_frame = ttk.Frame(main_frame)
control_frame.pack(fill=tk.X, pady=(0, 10))
# 主题选择
ttk.Label(control_frame, text="选择主题:").pack(side=tk.LEFT, padx=5)
self.theme_var = tk.StringVar()
self.theme_combo = ttk.Combobox(control_frame,
textvariable=self.theme_var,
state="readonly")
self.theme_combo.pack(side=tk.LEFT, padx=5)
self.theme_combo.bind("<<ComboboxSelected>>", self.on_theme_selected)
# 创建自定义主题按钮
ttk.Button(control_frame, text="创建自定义主题",
command=self.create_custom_theme).pack(side=tk.LEFT, padx=5)
# 保存主题按钮
ttk.Button(control_frame, text="保存主题",
command=self.save_theme).pack(side=tk.LEFT, padx=5)
# 应用主题按钮
ttk.Button(control_frame, text="应用主题",
command=self.apply_current_theme).pack(side=tk.LEFT, padx=5)
# 颜色编辑器
self.color_editor = ColorEditor(main_frame, self)
self.color_editor.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
# 预览区域
preview_frame = ttk.LabelFrame(main_frame, text="主题预览", padding=10)
preview_frame.pack(fill=tk.BOTH, expand=True)
self.create_preview(preview_frame)
# 初始化主题列表
self.update_theme_list()
def update_theme_list(self):
"""更新主题列表"""
themes = self.theme_engine.get_all_themes()
theme_names = [theme.name for theme in themes]
self.theme_combo['values'] = theme_names
if theme_names:
self.theme_combo.set(theme_names[0])
self.on_theme_selected()
def on_theme_selected(self, event=None):
"""主题选择事件"""
theme_name = self.theme_var.get()
if not theme_name:
return
# 查找主题
themes = self.theme_engine.get_all_themes()
for theme in themes:
if theme.name == theme_name:
self.current_theme = theme
self.color_editor.load_theme(theme)
break
def create_custom_theme(self):
"""创建自定义主题"""
# 获取当前主题作为基础
if not self.current_theme:
return
# 创建新主题名称
new_name = f"{self.current_theme.name} (自定义)"
# 创建自定义主题
try:
new_theme = self.theme_engine.create_custom_theme(
self.current_theme.id,
new_name,
{}
)
# 更新UI
self.update_theme_list()
self.theme_combo.set(new_name)
self.on_theme_selected()
except Exception as e:
print(f"创建自定义主题失败: {e}")
def save_theme(self):
"""保存主题"""
if not self.current_theme:
return
# 在实际应用中,这里会弹出文件对话框
print(f"保存主题: {self.current_theme.name}")
def apply_current_theme(self):
"""应用当前主题"""
if self.current_theme:
self.theme_engine.apply_theme(self.current_theme.id)
def create_preview(self, parent: tk.Widget):
"""创建预览区域"""
# 使用框架展示各种控件
preview_frame = ttk.Frame(parent)
preview_frame.pack(fill=tk.BOTH, expand=True)
# 行1: 标签和按钮
row1 = ttk.Frame(preview_frame)
row1.pack(fill=tk.X, pady=5)
ttk.Label(row1, text="标签:").pack(side=tk.LEFT, padx=5)
ttk.Button(row1, text="主按钮").pack(side=tk.LEFT, padx=5)
ttk.Button(row1, text="次级按钮", style="Secondary.TButton").pack(side=tk.LEFT, padx=5)
# 行2: 输入控件
row2 = ttk.Frame(preview_frame)
row2.pack(fill=tk.X, pady=5)
ttk.Label(row2, text="输入:").pack(side=tk.LEFT, padx=5)
ttk.Entry(row2, width=20).pack(side=tk.LEFT, padx=5)
ttk.Combobox(row2, values=["选项1", "选项2", "选项3"], width=15).pack(side=tk.LEFT, padx=5)
# 行3: 进度条
row3 = ttk.Frame(preview_frame)
row3.pack(fill=tk.X, pady=5)
ttk.Label(row3, text="进度:").pack(side=tk.LEFT, padx=5)
progress = ttk.Progressbar(row3, length=200, mode='determinate')
progress.pack(side=tk.LEFT, padx=5)
progress['value'] = 50
# 行4: 复选框和单选按钮
row4 = ttk.Frame(preview_frame)
row4.pack(fill=tk.X, pady=5)
ttk.Checkbutton(row4, text="复选框").pack(side=tk.LEFT, padx=5)
ttk.Radiobutton(row4, text="单选按钮1", value=1).pack(side=tk.LEFT, padx=5)
ttk.Radiobutton(row4, text="单选按钮2", value=2).pack(side=tk.LEFT, padx=5)
# 行5: 缩放条
row5 = ttk.Frame(preview_frame)
row5.pack(fill=tk.X, pady=5)
ttk.Label(row5, text="缩放:").pack(side=tk.LEFT, padx=5)
ttk.Scale(row5, from_=0, to=100, length=200).pack(side=tk.LEFT, padx=5)
class ColorEditor(ttk.Frame):
"""颜色编辑器"""
def __init__(self, parent: tk.Widget, theme_editor: ThemeEditor):
super().__init__(parent)
self.theme_editor = theme_editor
self.color_vars: Dict[str, tk.StringVar] = {}
self.color_buttons: Dict[str, tk.Button] = {}
self.create_ui()
def create_ui(self):
"""创建UI界面"""
# 颜色网格
self.color_grid = ttk.Frame(self)
self.color_grid.pack(fill=tk.BOTH, expand=True)
def load_theme(self, theme: Theme):
"""加载主题"""
# 清除现有颜色项
for widget in self.color_grid.winfo_children():
widget.destroy()
self.color_vars.clear()
self.color_buttons.clear()
# 获取颜色字典
color_dict = theme.colors.to_dict()
# 按类别分组颜色
color_categories = {
"主色": ["primary", "secondary", "accent"],
"背景色": ["background", "surface"],
"文本色": ["text_primary", "text_secondary", "text_disabled"],
"状态色": ["error", "warning", "success", "info"],
"边框色": ["border", "divider"],
"交互色": ["hover", "selected", "pressed"]
}
row = 0
for category, color_keys in color_categories.items():
# 分类标签
ttk.Label(self.color_grid,
text=category,
font=("微软雅黑", 10, "bold")).grid(
row=row, column=0, columnspan=3, sticky=tk.W, pady=(10, 5)
)
row += 1
# 颜色项
for i, key in enumerate(color_keys):
if key in color_dict:
col = i % 3
# 颜色标签
label = ttk.Label(self.color_grid, text=key.replace('_', ' ').title())
label.grid(row=row, column=col*2, sticky=tk.W, padx=5, pady=2)
# 颜色值输入
color_var = tk.StringVar(value=color_dict[key])
self.color_vars[key] = color_var
entry = ttk.Entry(self.color_grid, textvariable=color_var, width=10)
entry.grid(row=row, column=col*2+1, sticky=tk.W, padx=5, pady=2)
# 颜色选择按钮
btn = tk.Button(self.color_grid,
bg=color_dict[key],
width=3,
command=lambda k=key: self.choose_color(k))
btn.grid(row=row, column=col*2+2, sticky=tk.W, padx=5, pady=2)
self.color_buttons[key] = btn
# 绑定输入变化事件
color_var.trace('w', lambda *args, k=key: self.on_color_changed(k))
if col == 2: # 每行3个
row += 1
if row % 3 != 0: # 确保下一行从新行开始
row += 1
def choose_color(self, color_key: str):
"""选择颜色"""
from tkinter import colorchooser
# 弹出颜色选择器
color_code = colorchooser.askcolor(
initialcolor=self.color_vars[color_key].get()
)
if color_code[1]: # 用户选择了颜色
self.color_vars[color_key].set(color_code[1])
def on_color_changed(self, color_key: str):
"""颜色变化事件"""
color_value = self.color_vars[color_key].get()
# 更新按钮颜色
if color_key in self.color_buttons:
self.color_buttons[color_key].config(bg=color_value)
# 更新主题
if self.theme_editor.current_theme:
setattr(self.theme_editor.current_theme.colors, color_key, color_value)
7. 分布式系统架构概念
7.1 分布式通信框架
python
"""
distributed_framework.py
分布式系统架构
"""
import socket
import threading
import queue
import time
import json
import pickle
from typing import Dict, List, Any, Optional, Tuple, Callable
from dataclasses import dataclass, field
from enum import Enum
import struct
import zlib
import hashlib
import ssl
class NodeType(Enum):
"""节点类型"""
MASTER = "master" # 主节点
WORKER = "worker" # 工作节点
DISPLAY = "display" # 显示节点
STORAGE = "storage" # 存储节点
GATEWAY = "gateway" # 网关节点
class MessageType(Enum):
"""消息类型"""
HEARTBEAT = "heartbeat" # 心跳
DATA = "data" # 数据
COMMAND = "command" # 命令
STATUS = "status" # 状态
CONFIG = "config" # 配置
ALERT = "alert" # 告警
SYNC = "sync" # 同步
@dataclass
class NetworkMessage:
"""网络消息"""
msg_id: str
type: MessageType
source: str
target: str
timestamp: float = field(default_factory=time.time)
data: Any = None
metadata: Dict[str, Any] = field(default_factory=dict)
def to_bytes(self) -> bytes:
"""转换为字节流"""
# 使用pickle序列化
data = {
'msg_id': self.msg_id,
'type': self.type.value,
'source': self.source,
'target': self.target,
'timestamp': self.timestamp,
'data': self.data,
'metadata': self.metadata
}
# 压缩数据
pickled = pickle.dumps(data)
compressed = zlib.compress(pickled)
# 添加消息头
header = struct.pack('!I', len(compressed))
return header + compressed
@classmethod
def from_bytes(cls, data: bytes) -> 'NetworkMessage':
"""从字节流创建"""
# 解压数据
decompressed = zlib.decompress(data)
obj = pickle.loads(decompressed)
return cls(
msg_id=obj['msg_id'],
type=MessageType(obj['type']),
source=obj['source'],
target=obj['target'],
timestamp=obj['timestamp'],
data=obj['data'],
metadata=obj['metadata']
)
class NetworkNode:
"""网络节点"""
def __init__(self, node_id: str, node_type: NodeType, host: str = '0.0.0.0', port: int = 0):
self.node_id = node_id
self.node_type = node_type
self.host = host
self.port = port
# 网络连接
self.socket: Optional[socket.socket] = None
self.connections: Dict[str, socket.socket] = {}
# 消息处理
self.incoming_queue = queue.Queue()
self.outgoing_queue = queue.Queue()
self.message_handlers: Dict[MessageType, List[Callable]] = {}
# 节点状态
self.running = False
self.connected_nodes: Dict[str, Dict] = {}
# 线程
self.receive_thread: Optional[threading.Thread] = None
self.send_thread: Optional[threading.Thread] = None
self.process_thread: Optional[threading.Thread] = None
def start(self):
"""启动节点"""
self.running = True
# 创建socket
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定端口
self.socket.bind((self.host, self.port))
self.port = self.socket.getsockname()[1] # 获取实际端口
# 监听连接
self.socket.listen(5)
self.socket.setblocking(False)
# 启动线程
self.receive_thread = threading.Thread(target=self._accept_connections, daemon=True)
self.send_thread = threading.Thread(target=self._send_loop, daemon=True)
self.process_thread = threading.Thread(target=self._process_loop, daemon=True)
self.receive_thread.start()
self.send_thread.start()
self.process_thread.start()
def stop(self):
"""停止节点"""
self.running = False
# 关闭连接
for conn in self.connections.values():
try:
conn.close()
except:
pass
if self.socket:
self.socket.close()
def connect_to(self, host: str, port: int) -> bool:
"""连接到其他节点"""
try:
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.connect((host, port))
# 发送连接信息
conn_info = {
'node_id': self.node_id,
'node_type': self.node_type.value,
'host': self.host,
'port': self.port
}
self._send_raw(conn, conn_info)
# 接收对方信息
response = self._receive_raw(conn)
if response and 'node_id' in response:
node_id = response['node_id']
self.connections[node_id] = conn
self.connected_nodes[node_id] = response
# 启动接收线程
threading.Thread(
target=self._receive_from_connection,
args=(node_id, conn),
daemon=True
).start()
return True
except Exception as e:
print(f"连接失败 {host}:{port}: {e}")
return False
def send_message(self, message: NetworkMessage):
"""发送消息"""
self.outgoing_queue.put(message)
def broadcast_message(self, message: NetworkMessage, exclude: List[str] = None):
"""广播消息"""
exclude = exclude or []
for node_id in self.connections:
if node_id not in exclude:
msg_copy = NetworkMessage(
msg_id=message.msg_id + f"_{node_id}",
type=message.type,
source=message.source,
target=node_id,
timestamp=message.timestamp,
data=message.data,
metadata=message.metadata
)
self.send_message(msg_copy)
def register_message_handler(self, msg_type: MessageType, handler: Callable):
"""注册消息处理函数"""
if msg_type not in self.message_handlers:
self.message_handlers[msg_type] = []
self.message_handlers[msg_type].append(handler)
def _accept_connections(self):
"""接受连接"""
while self.running:
try:
# 非阻塞接受连接
readable, _, _ = select.select([self.socket], [], [], 0.1)
if self.socket in readable:
conn, addr = self.socket.accept()
# 接收连接信息
conn_info = self._receive_raw(conn)
if conn_info and 'node_id' in conn_info:
node_id = conn_info['node_id']
# 发送本节点信息
self_info = {
'node_id': self.node_id,
'node_type': self.node_type.value,
'host': self.host,
'port': self.port
}
self._send_raw(conn, self_info)
# 存储连接
self.connections[node_id] = conn
self.connected_nodes[node_id] = conn_info
# 启动接收线程
threading.Thread(
target=self._receive_from_connection,
args=(node_id, conn),
daemon=True
).start()
except Exception as e:
if self.running: # 只在运行时打印错误
print(f"接受连接错误: {e}")
time.sleep(0.1)
def _receive_from_connection(self, node_id: str, conn: socket.socket):
"""从连接接收数据"""
buffer = b""
expected_length = 0
while self.running and node_id in self.connections:
try:
# 接收数据
data = conn.recv(4096)
if not data: # 连接关闭
break
buffer += data
# 处理完整消息
while len(buffer) >= 4:
if expected_length == 0:
# 读取消息头
if len(buffer) >= 4:
header = buffer[:4]
expected_length = struct.unpack('!I', header)[0]
buffer = buffer[4:]
else:
break
if len(buffer) >= expected_length:
# 提取完整消息
message_data = buffer[:expected_length]
buffer = buffer[expected_length:]
try:
message = NetworkMessage.from_bytes(message_data)
self.incoming_queue.put((node_id, message))
except Exception as e:
print(f"解析消息失败: {e}")
expected_length = 0
else:
break
except (ConnectionResetError, ConnectionAbortedError, TimeoutError):
break
except Exception as e:
print(f"接收数据错误: {e}")
break
# 连接断开
self._handle_disconnection(node_id)
def _send_loop(self):
"""发送循环"""
while self.running:
try:
# 获取待发送消息
message = self.outgoing_queue.get(timeout=0.1)
# 发送到目标节点
if message.target in self.connections:
conn = self.connections[message.target]
self._send_message_to_connection(conn, message)
except queue.Empty:
continue
except Exception as e:
print(f"发送消息错误: {e}")
def _process_loop(self):
"""处理循环"""
while self.running:
try:
# 获取接收到的消息
source_node, message = self.incoming_queue.get(timeout=0.1)
# 调用消息处理函数
if message.type in self.message_handlers:
for handler in self.message_handlers[message.type]:
try:
handler(source_node, message)
except Exception as e:
print(f"消息处理错误: {e}")
except queue.Empty:
continue
except Exception as e:
print(f"处理消息错误: {e}")
def _send_raw(self, conn: socket.socket, data: Any):
"""发送原始数据"""
try:
# 序列化数据
pickled = pickle.dumps(data)
compressed = zlib.compress(pickled)
# 发送长度前缀
header = struct.pack('!I', len(compressed))
conn.sendall(header + compressed)
except Exception as e:
print(f"发送原始数据错误: {e}")
def _receive_raw(self, conn: socket.socket) -> Optional[Any]:
"""接收原始数据"""
try:
# 接收长度前缀
header = conn.recv(4)
if len(header) < 4:
return None
data_length = struct.unpack('!I', header)[0]
# 接收数据
received = 0
chunks = []
while received < data_length:
chunk = conn.recv(min(data_length - received, 4096))
if not chunk:
return None
chunks.append(chunk)
received += len(chunk)
data = b''.join(chunks)
# 解压和反序列化
decompressed = zlib.decompress(data)
return pickle.loads(decompressed)
except Exception as e:
print(f"接收原始数据错误: {e}")
return None
def _send_message_to_connection(self, conn: socket.socket, message: NetworkMessage):
"""发送消息到连接"""
try:
data = message.to_bytes()
conn.sendall(data)
except Exception as e:
print(f"发送消息到连接错误: {e}")
def _handle_disconnection(self, node_id: str):
"""处理连接断开"""
if node_id in self.connections:
try:
self.connections[node_id].close()
except:
pass
del self.connections[node_id]
if node_id in self.connected_nodes:
del self.connected_nodes[node_id]
print(f"节点 {node_id} 已断开连接")
def get_connected_nodes(self) -> Dict[str, Dict]:
"""获取已连接的节点"""
return self.connected_nodes.copy()
class DistributedSystemManager:
"""分布式系统管理器"""
def __init__(self, master_host: str = 'localhost', master_port: int = 8888):
self.master_host = master_host
self.master_port = master_port
self.master_node: Optional[NetworkNode] = None
self.worker_nodes: Dict[str, NetworkNode] = {}
self.node_registry: Dict[str, Dict] = {}
def start_master(self) -> bool:
"""启动主节点"""
try:
self.master_node = NetworkNode(
node_id="master",
node_type=NodeType.MASTER,
host=self.master_host,
port=self.master_port
)
# 注册消息处理器
self.master_node.register_message_handler(
MessageType.HEARTBEAT, self.handle_heartbeat
)
self.master_node.register_message_handler(
MessageType.DATA, self.handle_data
)
self.master_node.register_message_handler(
MessageType.STATUS, self.handle_status
)
# 启动主节点
self.master_node.start()
# 启动心跳监控
threading.Thread(target=self._heartbeat_monitor, daemon=True).start()
return True
except Exception as e:
print(f"启动主节点失败: {e}")
return False
def start_worker(self, worker_id: str, host: str = 'localhost', port: int = 0) -> bool:
"""启动工作节点"""
try:
worker_node = NetworkNode(
node_id=worker_id,
node_type=NodeType.WORKER,
host=host,
port=port
)
# 注册消息处理器
worker_node.register_message_handler(
MessageType.COMMAND, self.handle_command
)
worker_node.register_message_handler(
MessageType.CONFIG, self.handle_config
)
# 启动工作节点
worker_node.start()
# 连接到主节点
if worker_node.connect_to(self.master_host, self.master_port):
self.worker_nodes[worker_id] = worker_node
# 启动心跳发送
threading.Thread(
target=self._send_heartbeats,
args=(worker_node,),
daemon=True
).start()
return True
else:
print(f"工作节点 {worker_id} 连接主节点失败")
return False
except Exception as e:
print(f"启动工作节点失败: {e}")
return False
def handle_heartbeat(self, source_node: str, message: NetworkMessage):
"""处理心跳消息"""
node_id = source_node
heartbeat_data = message.data or {}
# 更新节点注册表
if node_id not in self.node_registry:
self.node_registry[node_id] = {
'node_id': node_id,
'last_heartbeat': time.time(),
'status': 'online',
'data': heartbeat_data
}
else:
self.node_registry[node_id]['last_heartbeat'] = time.time()
self.node_registry[node_id]['data'].update(heartbeat_data)
def handle_data(self, source_node: str, message: NetworkMessage):
"""处理数据消息"""
# 在实际系统中,这里会处理数据并可能转发给其他节点
print(f"收到来自 {source_node} 的数据: {message.data}")
def handle_status(self, source_node: str, message: NetworkMessage):
"""处理状态消息"""
node_id = source_node
status_data = message.data or {}
if node_id in self.node_registry:
self.node_registry[node_id]['status'] = status_data.get('status', 'unknown')
def handle_command(self, source_node: str, message: NetworkMessage):
"""处理命令消息"""
# 工作节点处理来自主节点的命令
command = message.data
print(f"节点 {source_node} 收到命令: {command}")
def handle_config(self, source_node: str, message: NetworkMessage):
"""处理配置消息"""
# 工作节点处理来自主节点的配置
config = message.data
print(f"节点 {source_node} 收到配置: {config}")
def _heartbeat_monitor(self):
"""心跳监控"""
while self.master_node and self.master_node.running:
current_time = time.time()
# 检查所有节点的最后心跳时间
for node_id, node_info in list(self.node_registry.items()):
last_heartbeat = node_info.get('last_heartbeat', 0)
if current_time - last_heartbeat > 10: # 10秒超时
# 标记为离线
self.node_registry[node_id]['status'] = 'offline'
# 发送节点离线通知
offline_message = NetworkMessage(
msg_id=f"offline_{node_id}_{int(time.time())}",
type=MessageType.ALERT,
source="master",
target="all",
data={
'node_id': node_id,
'status': 'offline',
'timestamp': current_time
}
)
self.master_node.broadcast_message(offline_message)
time.sleep(5) # 每5秒检查一次
def _send_heartbeats(self, worker_node: NetworkNode):
"""发送心跳"""
while worker_node and worker_node.running:
try:
heartbeat_message = NetworkMessage(
msg_id=f"heartbeat_{worker_node.node_id}_{int(time.time())}",
type=MessageType.HEARTBEAT,
source=worker_node.node_id,
target="master",
data={
'cpu_usage': 0, # 这里可以添加实际系统指标
'memory_usage': 0,
'queue_size': 0,
'timestamp': time.time()
}
)
worker_node.send_message(heartbeat_message)
except Exception as e:
print(f"发送心跳失败: {e}")
time.sleep(3) # 每3秒发送一次心跳
def send_command_to_worker(self, worker_id: str, command: Dict[str, Any]):
"""发送命令到工作节点"""
if self.master_node and worker_id in self.worker_nodes:
command_message = NetworkMessage(
msg_id=f"command_{int(time.time())}",
type=MessageType.COMMAND,
source="master",
target=worker_id,
data=command
)
self.master_node.send_message(command_message)
def broadcast_command(self, command: Dict[str, Any]):
"""广播命令到所有工作节点"""
if self.master_node:
for worker_id in self.worker_nodes:
self.send_command_to_worker(worker_id, command)
def get_system_status(self) -> Dict[str, Any]:
"""获取系统状态"""
online_nodes = 0
offline_nodes = 0
for node_info in self.node_registry.values():
if node_info.get('status') == 'online':
online_nodes += 1
else:
offline_nodes += 1
return {
'total_nodes': len(self.node_registry),
'online_nodes': online_nodes,
'offline_nodes': offline_nodes,
'nodes': self.node_registry
}
8. 系统集成与测试
8.1 完整集成系统实现
python
"""
ewcc_integrated_system.py
综合电子战指挥控制台完整系统
"""
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading
import time
import json
import os
from typing import Dict, List, Any, Optional, Tuple
from datetime import datetime
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
import queue
from view_manager import ViewManager, ViewType, ViewConfig, BaseView
from data_pipeline import DataPipeline, DataPipelineManager
from plugin_system import PluginManager, BasePlugin, PluginInfo
from theme_engine import ThemeEngine, Theme, ThemeType
from distributed_framework import DistributedSystemManager
class EWCCIntegratedSystem:
"""综合电子战指挥控制台完整系统"""
def __init__(self):
self.root = tk.Tk()
self.root.title("综合电子战指挥控制台")
self.root.geometry("1600x900")
# 设置图标
try:
self.root.iconbitmap("ewcc_icon.ico")
except:
pass
# 系统组件
self.view_manager = None
self.plugin_manager = None
self.theme_engine = None
self.data_pipeline_manager = None
self.distributed_manager = None
# 系统状态
self.system_running = False
self.current_mode = "standalone" # standalone, distributed
# 数据缓存
self.data_cache = {}
self.alert_queue = queue.Queue()
# 构建UI
self.setup_system()
self.create_main_ui()
# 窗口居中
self.center_window()
def setup_system(self):
"""设置系统"""
# 创建主题引擎
self.theme_engine = ThemeEngine()
# 创建视图管理器
self.view_manager = ViewManager(self.root)
# 创建插件管理器
self.plugin_manager = PluginManager(self)
self.plugin_manager.add_plugin_dir("./plugins")
# 创建数据流水线管理器
self.data_pipeline_manager = DataPipelineManager()
# 创建分布式管理器
self.distributed_manager = DistributedSystemManager()
def create_main_ui(self):
"""创建主UI界面"""
# 应用主题
self.theme_engine.apply_theme("dark")
# 主菜单
self.create_menu_bar()
# 主容器
main_container = ttk.Frame(self.root)
main_container.pack(fill=tk.BOTH, expand=True, padx=2, pady=2)
# 标题栏
self.create_title_bar(main_container)
# 主内容区域
content_frame = ttk.Frame(main_container)
content_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# 左侧工具栏
self.create_toolbar(content_frame)
# 中央视图区域
self.create_view_area(content_frame)
# 右侧状态栏
self.create_status_bar(content_frame)
# 系统托盘
self.create_system_tray()
def create_menu_bar(self):
"""创建菜单栏"""
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
# 文件菜单
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="新建工程", command=self.new_project)
file_menu.add_command(label="打开工程", command=self.open_project)
file_menu.add_command(label="保存工程", command=self.save_project)
file_menu.add_separator()
file_menu.add_command(label="导入数据", command=self.import_data)
file_menu.add_command(label="导出数据", command=self.export_data)
file_menu.add_separator()
file_menu.add_command(label="系统设置", command=self.system_settings)
file_menu.add_command(label="退出", command=self.quit_system)
# 视图菜单
view_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="视图", menu=view_menu)
# 视图子菜单
view_submenu = tk.Menu(view_menu, tearoff=0)
view_menu.add_cascade(label="显示视图", menu=view_submenu)
view_types = [
("战场态势", "battlefield"),
("频谱监测", "spectrum"),
("资源管理", "resource"),
("日志信息", "log"),
("告警显示", "alert"),
("分析视图", "analysis")
]
for name, view_type in view_types:
view_submenu.add_command(
label=name,
command=lambda vt=view_type: self.show_view(vt)
)
# 布局子菜单
layout_menu = tk.Menu(view_menu, tearoff=0)
view_menu.add_cascade(label="布局", menu=layout_menu)
layout_menu.add_command(label="默认布局",
command=lambda: self.apply_layout("default"))
layout_menu.add_command(label="分析布局",
command=lambda: self.apply_layout("analysis"))
layout_menu.add_command(label="监控布局",
command=lambda: self.apply_layout("monitor"))
layout_menu.add_separator()
layout_menu.add_command(label="保存当前布局",
command=self.save_current_layout)
layout_menu.add_command(label="管理布局",
command=self.manage_layouts)
# 主题子菜单
theme_menu = tk.Menu(view_menu, tearoff=0)
view_menu.add_cascade(label="主题", menu=theme_menu)
for theme in self.theme_engine.get_all_themes():
theme_menu.add_command(
label=theme.name,
command=lambda t=theme.id: self.theme_engine.apply_theme(t)
)
theme_menu.add_separator()
theme_menu.add_command(label="主题编辑器",
command=self.open_theme_editor)
# 工具菜单
tool_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="工具", menu=tool_menu)
tool_menu.add_command(label="插件管理器",
command=self.open_plugin_manager)
tool_menu.add_command(label="数据流水线",
command=self.open_data_pipeline)
tool_menu.add_command(label="系统监控",
command=self.open_system_monitor)
tool_menu.add_command(label="性能分析",
command=self.open_performance_analyzer)
# 系统菜单
system_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="系统", menu=system_menu)
system_menu.add_command(label="启动系统",
command=self.start_system)
system_menu.add_command(label="停止系统",
command=self.stop_system)
system_menu.add_separator()
system_menu.add_command(label="连接集群",
command=self.connect_to_cluster)
system_menu.add_command(label="断开集群",
command=self.disconnect_from_cluster)
system_menu.add_separator()
system_menu.add_command(label="系统日志",
command=self.show_system_log)
system_menu.add_command(label="关于",
command=self.show_about)
# 帮助菜单
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="帮助", menu=help_menu)
help_menu.add_command(label="用户手册",
command=self.show_user_manual)
help_menu.add_command(label="快捷键",
command=self.show_shortcuts)
help_menu.add_separator()
help_menu.add_command(label="检查更新",
command=self.check_for_updates)
help_menu.add_command(label="技术支持",
command=self.show_technical_support)
def create_title_bar(self, parent):
"""创建标题栏"""
title_frame = ttk.Frame(parent)
title_frame.pack(fill=tk.X, pady=(0, 5))
# 系统标题
title_label = ttk.Label(
title_frame,
text="综合电子战指挥控制台",
font=("微软雅黑", 20, "bold"),
foreground="#3498DB"
)
title_label.pack(side=tk.LEFT, padx=10)
# 系统状态
self.system_status_var = tk.StringVar(value="就绪")
status_label = ttk.Label(
title_frame,
textvariable=self.system_status_var,
font=("微软雅黑", 10),
foreground="#2ECC71"
)
status_label.pack(side=tk.RIGHT, padx=10)
def create_toolbar(self, parent):
"""创建工具栏"""
toolbar_frame = ttk.Frame(parent, width=60)
toolbar_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 5))
toolbar_frame.pack_propagate(False)
# 工具栏按钮
tools = [
("🏠", "主页", self.show_home),
("🗺️", "态势", lambda: self.show_view("battlefield")),
("📡", "频谱", lambda: self.show_view("spectrum")),
("📊", "分析", lambda: self.show_view("analysis")),
("🔧", "工具", self.open_tools),
("⚙️", "设置", self.system_settings),
("🔌", "插件", self.open_plugin_manager),
("📈", "监控", self.open_system_monitor)
]
for icon, tooltip, command in tools:
btn = ttk.Button(
toolbar_frame,
text=icon,
command=command,
width=4
)
btn.pack(pady=2)
# 添加工具提示
self.create_tooltip(btn, tooltip)
def create_view_area(self, parent):
"""创建视图区域"""
# 视图容器
self.view_container = ttk.Frame(parent)
self.view_container.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 创建默认视图
self.create_default_views()
def create_default_views(self):
"""创建默认视图"""
# 战场态势视图
battlefield_config = ViewConfig(
id="battlefield",
type=ViewType.BATTLEFIELD,
title="战场态势",
size=(600, 400)
)
self.view_manager.create_view(battlefield_config)
# 频谱监测视图
spectrum_config = ViewConfig(
id="spectrum",
type=ViewType.SPECTRUM,
title="频谱监测",
size=(400, 300)
)
self.view_manager.create_view(spectrum_config)
# 资源管理视图
resource_config = ViewConfig(
id="resource",
type=ViewType.RESOURCE,
title="资源管理",
size=(400, 300)
)
self.view_manager.create_view(resource_config)
# 应用默认布局
self.view_manager.apply_layout("default")
def create_status_bar(self, parent):
"""创建状态栏"""
status_frame = ttk.Frame(parent, width=300)
status_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=(5, 0))
status_frame.pack_propagate(False)
# 系统状态
status_title = ttk.Label(
status_frame,
text="系统状态",
font=("微软雅黑", 11, "bold")
)
status_title.pack(pady=(5, 10))
# 状态指标
self.status_vars = {}
status_items = [
("系统", "就绪", "system"),
("CPU", "0%", "cpu"),
("内存", "0%", "memory"),
("网络", "正常", "network"),
("数据流", "0条/秒", "data_rate"),
("告警", "0", "alerts")
]
for label, value, key in status_items:
frame = ttk.Frame(status_frame)
frame.pack(fill=tk.X, padx=5, pady=2)
ttk.Label(frame, text=label + ":", width=8).pack(side=tk.LEFT)
var = tk.StringVar(value=value)
ttk.Label(frame, textvariable=var,
font=("Consolas", 9)).pack(side=tk.RIGHT)
self.status_vars[key] = var
# 节点状态
node_frame = ttk.LabelFrame(status_frame, text="节点状态", padding=5)
node_frame.pack(fill=tk.X, padx=5, pady=(10, 5))
self.node_tree = ttk.Treeview(node_frame,
columns=("状态",),
show="tree",
height=8)
self.node_tree.pack(fill=tk.BOTH, expand=True)
# 告警列表
alert_frame = ttk.LabelFrame(status_frame, text="最近告警", padding=5)
alert_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.alert_listbox = tk.Listbox(
alert_frame,
bg="#1C2833",
fg="#ECF0F1",
font=("Consolas", 8),
height=6
)
scrollbar = ttk.Scrollbar(alert_frame, orient=tk.VERTICAL,
command=self.alert_listbox.yview)
self.alert_listbox.configure(yscrollcommand=scrollbar.set)
self.alert_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def create_system_tray(self):
"""创建系统托盘"""
# 在实际应用中,这里会创建系统托盘图标
pass
def create_tooltip(self, widget, text):
"""创建工具提示"""
def enter(event):
x, y, _, _ = widget.bbox("insert")
x += widget.winfo_rootx() + 25
y += widget.winfo_rooty() + 25
self.tooltip = tk.Toplevel(widget)
self.tooltip.wm_overrideredirect(True)
self.tooltip.wm_geometry(f"+{x}+{y}")
label = ttk.Label(self.tooltip, text=text,
background="#ffffe0", relief="solid", borderwidth=1)
label.pack()
def leave(event):
if hasattr(self, 'tooltip'):
self.tooltip.destroy()
widget.bind("<Enter>", enter)
widget.bind("<Leave>", leave)
def center_window(self):
"""窗口居中"""
self.root.update_idletasks()
width = self.root.winfo_width()
height = self.root.winfo_height()
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
x = (screen_width - width) // 2
y = (screen_height - height) // 2
self.root.geometry(f'{width}x{height}+{x}+{y}')
def show_view(self, view_type: str):
"""显示视图"""
view_id = view_type
if view_id not in self.view_manager.views:
# 创建视图
config = ViewConfig(
id=view_id,
type=ViewType(view_type),
title=view_type,
size=(400, 300)
)
self.view_manager.create_view(config)
# 显示视图
view = self.view_manager.get_view(view_id)
if view:
view.show()
def apply_layout(self, layout_name: str):
"""应用布局"""
self.view_manager.apply_layout(layout_name)
def save_current_layout(self):
"""保存当前布局"""
layout_name = f"layout_{int(time.time())}"
self.view_manager.save_layout(layout_name)
messagebox.showinfo("成功", f"布局已保存为: {layout_name}")
def manage_layouts(self):
"""管理布局"""
# 在实际应用中,这里会打开布局管理器
pass
def open_theme_editor(self):
"""打开主题编辑器"""
from theme_engine import ThemeEditor
editor_window = tk.Toplevel(self.root)
editor_window.title("主题编辑器")
editor_window.geometry("800x600")
ThemeEditor(editor_window, self.theme_engine)
def open_plugin_manager(self):
"""打开插件管理器"""
from plugin_system import PluginBrowser
plugin_window = tk.Toplevel(self.root)
plugin_window.title("插件管理器")
plugin_window.geometry("1000x700")
PluginBrowser(plugin_window, self.plugin_manager)
def open_data_pipeline(self):
"""打开数据流水线"""
from data_pipeline import DataPipelineManagerUI
pipeline_window = tk.Toplevel(self.root)
pipeline_window.title("数据流水线管理器")
pipeline_window.geometry("1200x800")
DataPipelineManagerUI(pipeline_window, self.data_pipeline_manager)
def open_system_monitor(self):
"""打开系统监控"""
monitor_window = tk.Toplevel(self.root)
monitor_window.title("系统监控")
monitor_window.geometry("800x600")
self.create_system_monitor_ui(monitor_window)
def open_performance_analyzer(self):
"""打开性能分析器"""
analyzer_window = tk.Toplevel(self.root)
analyzer_window.title("性能分析器")
analyzer_window.geometry("1000x700")
self.create_performance_analyzer_ui(analyzer_window)
def open_tools(self):
"""打开工具面板"""
tools_window = tk.Toplevel(self.root)
tools_window.title("工具面板")
tools_window.geometry("600x400")
self.create_tools_panel(tools_window)
def create_system_monitor_ui(self, parent):
"""创建系统监控UI"""
notebook = ttk.Notebook(parent)
notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 系统资源标签页
resource_tab = ttk.Frame(notebook)
self.create_resource_monitor(resource_tab)
notebook.add(resource_tab, text="系统资源")
# 数据流标签页
dataflow_tab = ttk.Frame(notebook)
self.create_dataflow_monitor(dataflow_tab)
notebook.add(dataflow_tab, text="数据流")
# 节点状态标签页
node_tab = ttk.Frame(notebook)
self.create_node_monitor(node_tab)
notebook.add(node_tab, text="节点状态")
def create_resource_monitor(self, parent):
"""创建资源监控"""
# 创建图表
fig = plt.Figure(figsize=(8, 6), dpi=100)
canvas = FigureCanvasTkAgg(fig, parent)
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
# 创建子图
ax_cpu = fig.add_subplot(221)
ax_memory = fig.add_subplot(222)
ax_network = fig.add_subplot(223)
ax_disk = fig.add_subplot(224)
# 设置图表
ax_cpu.set_title("CPU使用率")
ax_cpu.set_ylabel("百分比%")
ax_cpu.grid(True, alpha=0.3)
ax_memory.set_title("内存使用率")
ax_memory.set_ylabel("百分比%")
ax_memory.grid(True, alpha=0.3)
ax_network.set_title("网络流量")
ax_network.set_ylabel("MB/s")
ax_network.grid(True, alpha=0.3)
ax_disk.set_title("磁盘IO")
ax_disk.set_ylabel("MB/s")
ax_disk.grid(True, alpha=0.3)
fig.tight_layout()
def create_dataflow_monitor(self, parent):
"""创建数据流监控"""
# 创建Treeview显示数据流水线状态
columns = ("流水线", "状态", "处理速率", "队列大小", "延迟")
tree = ttk.Treeview(parent, columns=columns, show="headings", height=10)
for col in columns:
tree.heading(col, text=col)
tree.column(col, width=100)
scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=tree.yview)
tree.configure(yscrollcommand=scrollbar.set)
tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def create_node_monitor(self, parent):
"""创建节点监控"""
# 创建节点状态显示
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
ttk.Label(frame, text="节点监控", font=("微软雅黑", 12, "bold")).pack(pady=(0, 10))
# 节点状态表格
columns = ("节点ID", "类型", "状态", "CPU", "内存", "最后活跃")
tree = ttk.Treeview(frame, columns=columns, show="headings", height=8)
for col in columns:
tree.heading(col, text=col)
tree.column(col, width=80)
scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=tree.yview)
tree.configure(yscrollcommand=scrollbar.set)
tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def create_performance_analyzer_ui(self, parent):
"""创建性能分析器UI"""
notebook = ttk.Notebook(parent)
notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 性能指标标签页
metrics_tab = ttk.Frame(notebook)
self.create_performance_metrics(metrics_tab)
notebook.add(metrics_tab, text="性能指标")
# 瓶颈分析标签页
bottleneck_tab = ttk.Frame(notebook)
self.create_bottleneck_analysis(bottleneck_tab)
notebook.add(bottleneck_tab, text="瓶颈分析")
# 优化建议标签页
optimization_tab = ttk.Frame(notebook)
self.create_optimization_suggestions(optimization_tab)
notebook.add(optimization_tab, text="优化建议")
def create_performance_metrics(self, parent):
"""创建性能指标显示"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 创建性能指标图表
fig = plt.Figure(figsize=(10, 8), dpi=100)
canvas = FigureCanvasTkAgg(fig, frame)
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
# 创建多个子图
axes = fig.subplots(2, 2)
# 设置图表
axes[0, 0].set_title("响应时间分布")
axes[0, 0].set_ylabel("毫秒")
axes[0, 0].grid(True, alpha=0.3)
axes[0, 1].set_title("吞吐量趋势")
axes[0, 1].set_ylabel("请求/秒")
axes[0, 1].grid(True, alpha=0.3)
axes[1, 0].set_title("资源使用率")
axes[1, 0].set_ylabel("百分比%")
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].set_title("错误率")
axes[1, 1].set_ylabel("百分比%")
axes[1, 1].grid(True, alpha=0.3)
fig.tight_layout()
def create_bottleneck_analysis(self, parent):
"""创建瓶颈分析"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
ttk.Label(frame, text="瓶颈分析", font=("微软雅黑", 12, "bold")).pack(pady=(0, 10))
# 瓶颈分析文本
text = tk.Text(frame, height=20, width=60, font=("Consolas", 10))
scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=text.yview)
text.configure(yscrollcommand=scrollbar.set)
text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 示例分析结果
analysis = """瓶颈分析报告:
1. CPU瓶颈:
- 进程: data_processor
- CPU使用率: 85%
- 建议: 优化算法或增加CPU核心
2. 内存瓶颈:
- 进程: cache_manager
- 内存使用: 3.2GB
- 建议: 增加内存或优化缓存策略
3. 磁盘IO瓶颈:
- 文件: data_log.db
- IO等待: 45%
- 建议: 使用SSD或优化写入策略
4. 网络瓶颈:
- 接口: eth0
- 带宽使用: 80%
- 建议: 增加带宽或压缩数据
"""
text.insert(1.0, analysis)
def create_optimization_suggestions(self, parent):
"""创建优化建议"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
ttk.Label(frame, text="优化建议", font=("微软雅黑", 12, "bold")).pack(pady=(0, 10))
# 优化建议列表
suggestions = [
("CPU优化", "将计算密集型任务分布到多个核心", "高"),
("内存优化", "使用内存池减少内存分配开销", "中"),
("磁盘优化", "使用异步IO提高磁盘读写效率", "高"),
("网络优化", "启用数据压缩减少网络流量", "低"),
("缓存优化", "增加缓存命中率减少数据库访问", "中"),
("算法优化", "使用更高效的算法减少计算量", "高")
]
for i, (title, description, priority) in enumerate(suggestions):
suggestion_frame = ttk.Frame(frame)
suggestion_frame.pack(fill=tk.X, pady=2)
ttk.Label(suggestion_frame, text=title, width=15).pack(side=tk.LEFT)
ttk.Label(suggestion_frame, text=description, width=40).pack(side=tk.LEFT, padx=5)
# 优先级标签
colors = {"高": "#E74C3C", "中": "#F39C12", "低": "#2ECC71"}
label = ttk.Label(suggestion_frame, text=priority,
foreground=colors.get(priority, "black"))
label.pack(side=tk.RIGHT, padx=5)
def create_tools_panel(self, parent):
"""创建工具面板"""
notebook = ttk.Notebook(parent)
notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 数据工具标签页
data_tools_tab = ttk.Frame(notebook)
self.create_data_tools(data_tools_tab)
notebook.add(data_tools_tab, text="数据工具")
# 分析工具标签页
analysis_tools_tab = ttk.Frame(notebook)
self.create_analysis_tools(analysis_tools_tab)
notebook.add(analysis_tools_tab, text="分析工具")
# 系统工具标签页
system_tools_tab = ttk.Frame(notebook)
self.create_system_tools(system_tools_tab)
notebook.add(system_tools_tab, text="系统工具")
def create_data_tools(self, parent):
"""创建数据工具"""
tools = [
("数据导入", self.import_data),
("数据导出", self.export_data),
("数据清洗", self.clean_data),
("数据转换", self.transform_data),
("数据合并", self.merge_data),
("数据抽样", self.sample_data)
]
for i, (name, command) in enumerate(tools):
btn = ttk.Button(parent, text=name, command=command, width=20)
btn.grid(row=i//2, column=i%2, padx=10, pady=10, sticky=tk.NSEW)
# 配置网格权重
for i in range(2):
parent.grid_columnconfigure(i, weight=1)
for i in range((len(tools) + 1) // 2):
parent.grid_rowconfigure(i, weight=1)
def create_analysis_tools(self, parent):
"""创建分析工具"""
tools = [
("统计分析", self.statistical_analysis),
("频谱分析", self.spectrum_analysis),
("趋势分析", self.trend_analysis),
("相关性分析", self.correlation_analysis),
("聚类分析", self.cluster_analysis),
("异常检测", self.anomaly_detection)
]
for i, (name, command) in enumerate(tools):
btn = ttk.Button(parent, text=name, command=command, width=20)
btn.grid(row=i//2, column=i%2, padx=10, pady=10, sticky=tk.NSEW)
def create_system_tools(self, parent):
"""创建系统工具"""
tools = [
("系统诊断", self.system_diagnosis),
("性能测试", self.performance_test),
("压力测试", self.stress_test),
("日志分析", self.log_analysis),
("配置备份", self.config_backup),
("系统恢复", self.system_recovery)
]
for i, (name, command) in enumerate(tools):
btn = ttk.Button(parent, text=name, command=command, width=20)
btn.grid(row=i//2, column=i%2, padx=10, pady=10, sticky=tk.NSEW)
def new_project(self):
"""新建工程"""
response = messagebox.askyesno("确认", "创建新工程将丢失当前未保存的更改,是否继续?")
if response:
# 重置系统状态
self.reset_system()
def open_project(self):
"""打开工程"""
filename = filedialog.askopenfilename(
title="选择工程文件",
filetypes=[("EWCC工程文件", "*.ewcc"), ("所有文件", "*.*")]
)
if filename:
try:
with open(filename, 'r', encoding='utf-8') as f:
project_data = json.load(f)
# 加载工程配置
self.load_project(project_data)
messagebox.showinfo("成功", f"工程已加载: {filename}")
except Exception as e:
messagebox.showerror("错误", f"加载工程失败: {str(e)}")
def save_project(self):
"""保存工程"""
filename = filedialog.asksaveasfilename(
title="保存工程文件",
defaultextension=".ewcc",
filetypes=[("EWCC工程文件", "*.ewcc"), ("所有文件", "*.*")]
)
if filename:
try:
project_data = self.save_project_data()
with open(filename, 'w', encoding='utf-8') as f:
json.dump(project_data, f, indent=2, ensure_ascii=False)
messagebox.showinfo("成功", f"工程已保存: {filename}")
except Exception as e:
messagebox.showerror("错误", f"保存工程失败: {str(e)}")
def load_project(self, project_data: Dict[str, Any]):
"""加载工程数据"""
# 在实际应用中,这里会加载工程的所有配置和数据
pass
def save_project_data(self) -> Dict[str, Any]:
"""保存工程数据"""
return {
"name": "综合电子战指挥控制台工程",
"version": "1.0.0",
"timestamp": time.time(),
"config": {
"theme": self.theme_engine.current_theme.id if self.theme_engine.current_theme else "default",
"layout": self.view_manager.current_layout,
"views": [view.config.id for view in self.view_manager.get_all_views()]
}
}
def import_data(self):
"""导入数据"""
filename = filedialog.askopenfilename(
title="选择数据文件",
filetypes=[
("CSV文件", "*.csv"),
("JSON文件", "*.json"),
("Excel文件", "*.xlsx"),
("所有文件", "*.*")
]
)
if filename:
try:
# 在实际应用中,这里会解析和导入数据
print(f"导入数据: {filename}")
except Exception as e:
messagebox.showerror("错误", f"导入数据失败: {str(e)}")
def export_data(self):
"""导出数据"""
filename = filedialog.asksaveasfilename(
title="导出数据",
defaultextension=".csv",
filetypes=[
("CSV文件", "*.csv"),
("JSON文件", "*.json"),
("Excel文件", "*.xlsx"),
("所有文件", "*.*")
]
)
if filename:
try:
# 在实际应用中,这里会导出数据
print(f"导出数据: {filename}")
except Exception as e:
messagebox.showerror("错误", f"导出数据失败: {str(e)}")
def system_settings(self):
"""系统设置"""
settings_window = tk.Toplevel(self.root)
settings_window.title("系统设置")
settings_window.geometry("600x400")
notebook = ttk.Notebook(settings_window)
notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 常规设置
general_tab = ttk.Frame(notebook)
self.create_general_settings(general_tab)
notebook.add(general_tab, text="常规")
# 显示设置
display_tab = ttk.Frame(notebook)
self.create_display_settings(display_tab)
notebook.add(display_tab, text="显示")
# 数据设置
data_tab = ttk.Frame(notebook)
self.create_data_settings(data_tab)
notebook.add(data_tab, text="数据")
# 网络设置
network_tab = ttk.Frame(notebook)
self.create_network_settings(network_tab)
notebook.add(network_tab, text="网络")
def create_general_settings(self, parent):
"""创建常规设置"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
settings = [
("启动时加载上次工程", tk.BooleanVar(value=True)),
("自动保存间隔(分钟)", tk.IntVar(value=10)),
("最大历史记录数", tk.IntVar(value=100)),
("语言", tk.StringVar(value="中文")),
("时区", tk.StringVar(value="Asia/Shanghai"))
]
for i, (label, var) in enumerate(settings):
ttk.Label(frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=5)
if isinstance(var, tk.BooleanVar):
ttk.Checkbutton(frame, variable=var).grid(row=i, column=1, sticky=tk.W, pady=5)
elif isinstance(var, tk.IntVar):
ttk.Entry(frame, textvariable=var, width=10).grid(row=i, column=1, sticky=tk.W, pady=5)
elif isinstance(var, tk.StringVar):
ttk.Entry(frame, textvariable=var, width=20).grid(row=i, column=1, sticky=tk.W, pady=5)
def create_display_settings(self, parent):
"""创建显示设置"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
settings = [
("默认主题", tk.StringVar(value="dark")),
("字体大小", tk.IntVar(value=12)),
("动画效果", tk.BooleanVar(value=True)),
("硬件加速", tk.BooleanVar(value=True)),
("高DPI缩放", tk.BooleanVar(value=True))
]
for i, (label, var) in enumerate(settings):
ttk.Label(frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=5)
if isinstance(var, tk.BooleanVar):
ttk.Checkbutton(frame, variable=var).grid(row=i, column=1, sticky=tk.W, pady=5)
elif isinstance(var, tk.IntVar):
ttk.Entry(frame, textvariable=var, width=10).grid(row=i, column=1, sticky=tk.W, pady=5)
elif isinstance(var, tk.StringVar):
ttk.Entry(frame, textvariable=var, width=20).grid(row=i, column=1, sticky=tk.W, pady=5)
def create_data_settings(self, parent):
"""创建数据设置"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
settings = [
("数据缓存大小(MB)", tk.IntVar(value=100)),
("自动备份间隔(小时)", tk.IntVar(value=24)),
("数据压缩", tk.BooleanVar(value=True)),
("数据加密", tk.BooleanVar(value=False)),
("最大连接数", tk.IntVar(value=10))
]
for i, (label, var) in enumerate(settings):
ttk.Label(frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=5)
if isinstance(var, tk.BooleanVar):
ttk.Checkbutton(frame, variable=var).grid(row=i, column=1, sticky=tk.W, pady=5)
elif isinstance(var, tk.IntVar):
ttk.Entry(frame, textvariable=var, width=10).grid(row=i, column=1, sticky=tk.W, pady=5)
def create_network_settings(self, parent):
"""创建网络设置"""
frame = ttk.Frame(parent)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
settings = [
("服务器地址", tk.StringVar(value="localhost")),
("服务器端口", tk.IntVar(value=8888)),
("连接超时(秒)", tk.IntVar(value=30)),
("启用SSL", tk.BooleanVar(value=False)),
("代理服务器", tk.StringVar(value=""))
]
for i, (label, var) in enumerate(settings):
ttk.Label(frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=5)
if isinstance(var, tk.BooleanVar):
ttk.Checkbutton(frame, variable=var).grid(row=i, column=1, sticky=tk.W, pady=5)
elif isinstance(var, tk.IntVar):
ttk.Entry(frame, textvariable=var, width=10).grid(row=i, column=1, sticky=tk.W, pady=5)
elif isinstance(var, tk.StringVar):
ttk.Entry(frame, textvariable=var, width=20).grid(row=i, column=1, sticky=tk.W, pady=5)
def start_system(self):
"""启动系统"""
if self.system_running:
return
self.system_running = True
self.system_status_var.set("运行中")
# 启动数据流水线
self.start_data_pipelines()
# 启动插件
self.start_plugins()
# 启动系统监控
self.start_system_monitor()
messagebox.showinfo("系统启动", "综合电子战指挥控制台已启动")
def stop_system(self):
"""停止系统"""
if not self.system_running:
return
response = messagebox.askyesno("确认", "确定要停止系统吗?")
if response:
self.system_running = False
self.system_status_var.set("已停止")
# 停止数据流水线
self.stop_data_pipelines()
# 停止插件
self.stop_plugins()
# 停止系统监控
self.stop_system_monitor()
messagebox.showinfo("系统停止", "系统已安全停止")
def connect_to_cluster(self):
"""连接到集群"""
if self.current_mode == "distributed":
messagebox.showinfo("提示", "已连接到集群")
return
# 在实际应用中,这里会连接到分布式集群
self.current_mode = "distributed"
self.distributed_manager.start_master()
messagebox.showinfo("集群连接", "已连接到分布式集群")
def disconnect_from_cluster(self):
"""断开集群连接"""
if self.current_mode == "standalone":
messagebox.showinfo("提示", "未连接到集群")
return
self.current_mode = "standalone"
# 在实际应用中,这里会断开集群连接
messagebox.showinfo("集群断开", "已断开集群连接")
def show_system_log(self):
"""显示系统日志"""
log_window = tk.Toplevel(self.root)
log_window.title("系统日志")
log_window.geometry("800x500")
text = tk.Text(log_window, font=("Consolas", 10))
scrollbar = ttk.Scrollbar(log_window, orient=tk.VERTICAL, command=text.yview)
text.configure(yscrollcommand=scrollbar.set)
text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 示例日志
logs = """[INFO] 2024-01-10 10:00:00 系统启动
[INFO] 2024-01-10 10:00:01 加载主题: dark
[INFO] 2024-01-10 10:00:02 创建视图: 战场态势
[INFO] 2024-01-10 10:00:03 创建视图: 频谱监测
[INFO] 2024-01-10 10:00:04 数据流水线启动
[INFO] 2024-01-10 10:00:05 插件管理器加载
[INFO] 2024-01-10 10:00:06 系统准备就绪
"""
text.insert(1.0, logs)
def show_about(self):
"""显示关于信息"""
about_text = """综合电子战指挥控制台
版本: 3.0.0
功能特点:
- 多视图协同显示
- 实时数据处理
- 插件化架构
- 分布式支持
- 主题皮肤系统
技术栈:
- Python 3.8+
- tkinter/ttk GUI
- matplotlib 可视化
- 多线程/多进程
作者: Python GUI研究团队
日期: 2024年
许可证: MIT
"""
messagebox.showinfo("关于", about_text)
def show_user_manual(self):
"""显示用户手册"""
# 在实际应用中,这里会打开用户手册
messagebox.showinfo("用户手册", "用户手册功能开发中...")
def show_shortcuts(self):
"""显示快捷键"""
shortcuts = """系统快捷键:
Ctrl+N: 新建工程
Ctrl+O: 打开工程
Ctrl+S: 保存工程
Ctrl+Q: 退出系统
视图快捷键:
F1: 战场态势
F2: 频谱监测
F3: 资源管理
F4: 日志信息
F5: 告警显示
F6: 分析视图
操作快捷键:
Ctrl+R: 刷新数据
Ctrl+F: 搜索
Ctrl+P: 打印
Ctrl+H: 帮助
Esc: 取消/返回
"""
messagebox.showinfo("快捷键", shortcuts)
def check_for_updates(self):
"""检查更新"""
# 在实际应用中,这里会检查软件更新
messagebox.showinfo("检查更新", "当前已是最新版本")
def show_technical_support(self):
"""显示技术支持"""
support_text = """技术支持:
官方网站: https://ewcc.example.com
技术支持邮箱: support@ewcc.example.com
文档中心: https://docs.ewcc.example.com
社区论坛: https://forum.ewcc.example.com
问题反馈:
请通过邮件或论坛反馈问题,我们会尽快处理。
"""
messagebox.showinfo("技术支持", support_text)
def start_data_pipelines(self):
"""启动数据流水线"""
# 启动遥测数据流水线
self.data_pipeline_manager.create_telemetry_pipeline(
"telemetry_pipeline",
self.get_view("spectrum").canvas if hasattr(self, 'get_view') else None
)
self.data_pipeline_manager.start_pipeline("telemetry_pipeline")
# 启动频谱数据流水线
self.data_pipeline_manager.create_spectrum_pipeline(
"spectrum_pipeline",
self.get_view("spectrum").canvas if hasattr(self, 'get_view') else None
)
self.data_pipeline_manager.start_pipeline("spectrum_pipeline")
def stop_data_pipelines(self):
"""停止数据流水线"""
self.data_pipeline_manager.stop_pipeline("telemetry_pipeline")
self.data_pipeline_manager.stop_pipeline("spectrum_pipeline")
def start_plugins(self):
"""启动插件"""
# 发现并加载插件
self.plugin_manager.discover_plugins()
# 加载所有插件
for plugin_info in self.plugin_manager.discover_plugins():
if self.plugin_manager.load_plugin(plugin_info):
self.plugin_manager.enable_plugin(plugin_info.id)
def stop_plugins(self):
"""停止插件"""
for plugin in self.plugin_manager.get_all_plugins():
self.plugin_manager.disable_plugin(plugin.info.id)
def start_system_monitor(self):
"""启动系统监控"""
# 启动系统状态更新线程
self.monitor_thread = threading.Thread(
target=self._update_system_status,
daemon=True
)
self.monitor_thread.start()
def stop_system_monitor(self):
"""停止系统监控"""
pass
def _update_system_status(self):
"""更新系统状态"""
import psutil
while self.system_running:
try:
# 获取系统状态
cpu_percent = psutil.cpu_percent(interval=1)
memory_percent = psutil.virtual_memory().percent
# 更新状态变量
self.root.after(0, self.status_vars['cpu'].set, f"{cpu_percent:.1f}%")
self.root.after(0, self.status_vars['memory'].set, f"{memory_percent:.1f}%")
# 更新数据流状态
if hasattr(self.data_pipeline_manager, 'get_pipeline_stats'):
stats = self.data_pipeline_manager.get_pipeline_stats("telemetry_pipeline")
if stats:
data_rate = stats.get('throughput', 0)
self.root.after(0, self.status_vars['data_rate'].set,
f"{data_rate:.1f}条/秒")
except Exception as e:
print(f"更新系统状态错误: {e}")
time.sleep(2) # 每2秒更新一次
def reset_system(self):
"""重置系统"""
# 停止系统
self.stop_system()
# 清除数据
self.data_cache.clear()
# 重置视图
for view in self.view_manager.get_all_views():
view.destroy()
self.view_manager.views.clear()
# 重新创建默认视图
self.create_default_views()
# 更新状态
self.system_status_var.set("就绪")
def quit_system(self):
"""退出系统"""
response = messagebox.askyesno("确认", "确定要退出系统吗?")
if response:
# 停止系统
self.stop_system()
# 保存配置
self.save_project()
# 退出应用
self.root.quit()
# 以下是一些示例工具方法
def clean_data(self):
"""数据清洗"""
messagebox.showinfo("数据清洗", "数据清洗功能开发中...")
def transform_data(self):
"""数据转换"""
messagebox.showinfo("数据转换", "数据转换功能开发中...")
def merge_data(self):
"""数据合并"""
messagebox.showinfo("数据合并", "数据合并功能开发中...")
def sample_data(self):
"""数据抽样"""
messagebox.showinfo("数据抽样", "数据抽样功能开发中...")
def statistical_analysis(self):
"""统计分析"""
messagebox.showinfo("统计分析", "统计分析功能开发中...")
def spectrum_analysis(self):
"""频谱分析"""
messagebox.showinfo("频谱分析", "频谱分析功能开发中...")
def trend_analysis(self):
"""趋势分析"""
messagebox.showinfo("趋势分析", "趋势分析功能开发中...")
def correlation_analysis(self):
"""相关性分析"""
messagebox.showinfo("相关性分析", "相关性分析功能开发中...")
def cluster_analysis(self):
"""聚类分析"""
messagebox.showinfo("聚类分析", "聚类分析功能开发中...")
def anomaly_detection(self):
"""异常检测"""
messagebox.showinfo("异常检测", "异常检测功能开发中...")
def system_diagnosis(self):
"""系统诊断"""
messagebox.showinfo("系统诊断", "系统诊断功能开发中...")
def performance_test(self):
"""性能测试"""
messagebox.showinfo("性能测试", "性能测试功能开发中...")
def stress_test(self):
"""压力测试"""
messagebox.showinfo("压力测试", "压力测试功能开发中...")
def log_analysis(self):
"""日志分析"""
messagebox.showinfo("日志分析", "日志分析功能开发中...")
def config_backup(self):
"""配置备份"""
messagebox.showinfo("配置备份", "配置备份功能开发中...")
def system_recovery(self):
"""系统恢复"""
messagebox.showinfo("系统恢复", "系统恢复功能开发中...")
def get_view(self, view_id: str):
"""获取视图"""
return self.view_manager.get_view(view_id)
def run(self):
"""运行系统"""
self.root.mainloop()
# 主程序入口
if __name__ == "__main__":
app = EWCCIntegratedSystem()
app.run()
9. 性能优化与部署
9.1 性能优化策略
python
"""
performance_optimization.py
性能优化与部署策略
"""
import time
import tracemalloc
import cProfile
import pstats
from typing import Dict, List, Any, Optional, Callable
from functools import wraps
import threading
import queue
import numpy as np
from dataclasses import dataclass, field
from enum import Enum
import gc
class OptimizationLevel(Enum):
"""优化级别"""
NONE = "none" # 无优化
BASIC = "basic" # 基础优化
ADVANCED = "advanced" # 高级优化
AGGRESSIVE = "aggressive" # 激进优化
@dataclass
class PerformanceMetrics:
"""性能指标"""
execution_time: float = 0.0
memory_usage: float = 0.0
cpu_usage: float = 0.0
call_count: int = 0
cache_hits: int = 0
cache_misses: int = 0
queue_size: int = 0
class PerformanceOptimizer:
"""性能优化器"""
def __init__(self, optimization_level: OptimizationLevel = OptimizationLevel.BASIC):
self.optimization_level = optimization_level
self.metrics: Dict[str, PerformanceMetrics] = {}
self.caches: Dict[str, Any] = {}
self.profiler = cProfile.Profile()
self.enabled = False
def start(self):
"""启动优化器"""
self.enabled = True
tracemalloc.start()
def stop(self):
"""停止优化器"""
self.enabled = False
tracemalloc.stop()
def profile_function(self, func: Callable):
"""性能分析装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
if not self.enabled:
return func(*args, **kwargs)
# 开始性能分析
start_time = time.perf_counter()
start_memory = tracemalloc.get_traced_memory()[0]
# 执行函数
result = func(*args, **kwargs)
# 计算性能指标
end_time = time.perf_counter()
end_memory = tracemalloc.get_traced_memory()[0]
# 记录指标
func_name = func.__name__
if func_name not in self.metrics:
self.metrics[func_name] = PerformanceMetrics()
metrics = self.metrics[func_name]
metrics.execution_time += end_time - start_time
metrics.memory_usage = max(metrics.memory_usage, end_memory - start_memory)
metrics.call_count += 1
return result
return wrapper
def cache_result(self, func: Callable):
"""结果缓存装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
if self.optimization_level == OptimizationLevel.NONE:
return func(*args, **kwargs)
# 创建缓存键
cache_key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
# 检查缓存
if cache_key in self.caches:
self.metrics.setdefault(func.__name__, PerformanceMetrics()).cache_hits += 1
return self.caches[cache_key]
# 计算并缓存结果
result = func(*args, **kwargs)
self.caches[cache_key] = result
self.metrics.setdefault(func.__name__, PerformanceMetrics()).cache_misses += 1
# 清理旧缓存(如果缓存太大)
if len(self.caches) > 1000:
self._cleanup_cache()
return result
return wrapper
def _cleanup_cache(self):
"""清理缓存"""
# 保留最近使用的缓存项
if len(self.caches) > 1000:
# 简单的LRU策略:删除一半旧缓存
keys_to_remove = list(self.caches.keys())[:500]
for key in keys_to_remove:
del self.caches[key]
def lazy_loading(self, func: Callable):
"""懒加载装饰器"""
cached_result = None
initialized = False
@wraps(func)
def wrapper(*args, **kwargs):
nonlocal cached_result, initialized
if not initialized:
cached_result = func(*args, **kwargs)
initialized = True
return cached_result
return wrapper
def batch_processing(self, batch_size: int = 100):
"""批处理装饰器"""
def decorator(func: Callable):
buffer = []
@wraps(func)
def wrapper(item):
buffer.append(item)
if len(buffer) >= batch_size:
result = func(buffer)
buffer.clear()
return result
return None
return wrapper
return decorator
def get_performance_report(self) -> Dict[str, Any]:
"""获取性能报告"""
report = {
"total_functions": len(self.metrics),
"total_calls": sum(m.call_count for m in self.metrics.values()),
"total_time": sum(m.execution_time for m in self.metrics.values()),
"cache_efficiency": 0,
"function_metrics": {}
}
# 计算缓存效率
total_hits = sum(m.cache_hits for m in self.metrics.values())
total_misses = sum(m.cache_misses for m in self.metrics.values())
if total_hits + total_misses > 0:
report["cache_efficiency"] = total_hits / (total_hits + total_misses)
# 添加函数级别的指标
for func_name, metrics in self.metrics.items():
report["function_metrics"][func_name] = {
"call_count": metrics.call_count,
"total_time": metrics.execution_time,
"avg_time": metrics.execution_time / metrics.call_count if metrics.call_count > 0 else 0,
"memory_usage": metrics.memory_usage,
"cache_hits": metrics.cache_hits,
"cache_misses": metrics.cache_misses
}
return report
def optimize_memory(self):
"""优化内存使用"""
if self.optimization_level == OptimizationLevel.NONE:
return
# 强制垃圾回收
gc.collect()
if self.optimization_level in [OptimizationLevel.ADVANCED, OptimizationLevel.AGGRESSIVE]:
# 禁用循环引用垃圾回收(仅在高级优化时使用)
gc.disable()
def restore_memory_settings(self):
"""恢复内存设置"""
if self.optimization_level in [OptimizationLevel.ADVANCED, OptimizationLevel.AGGRESSIVE]:
gc.enable()
def run_profiling(self, func: Callable, *args, **kwargs):
"""运行性能分析"""
self.profiler.enable()
result = func(*args, **kwargs)
self.profiler.disable()
# 生成分析报告
stats = pstats.Stats(self.profiler)
stats.sort_stats('cumulative')
return result, stats
class ThreadPoolOptimizer:
"""线程池优化器"""
def __init__(self, max_workers: int = None, optimization_level: OptimizationLevel = OptimizationLevel.BASIC):
import concurrent.futures
self.max_workers = max_workers or (threading.cpu_count() * 2)
self.optimization_level = optimization_level
self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers)
self.task_queue = queue.Queue()
self.running = False
def start(self):
"""启动线程池"""
self.running = True
# 启动任务处理线程
for _ in range(self.max_workers):
threading.Thread(target=self._worker, daemon=True).start()
def stop(self):
"""停止线程池"""
self.running = False
self.executor.shutdown(wait=True)
def submit_task(self, func, *args, **kwargs):
"""提交任务"""
future = self.executor.submit(func, *args, **kwargs)
return future
def map_tasks(self, func, iterable, chunk_size=1):
"""映射任务"""
return self.executor.map(func, iterable, chunksize=chunk_size)
def _worker(self):
"""工作线程"""
while self.running:
try:
task = self.task_queue.get(timeout=0.1)
if task is None:
break
func, args, kwargs = task
try:
func(*args, **kwargs)
except Exception as e:
print(f"任务执行错误: {e}")
finally:
self.task_queue.task_done()
except queue.Empty:
continue
def get_queue_size(self):
"""获取队列大小"""
return self.task_queue.qsize()
def get_active_workers(self):
"""获取活动工作线程数"""
return self.executor._work_queue.qsize() # 注意:这是内部属性,实际中需要更安全的方法
class MemoryOptimizer:
"""内存优化器"""
def __init__(self, optimization_level: OptimizationLevel = OptimizationLevel.BASIC):
self.optimization_level = optimization_level
self.memory_pool = {}
self.object_cache = {}
def optimize_data_structure(self, data):
"""优化数据结构"""
if self.optimization_level == OptimizationLevel.NONE:
return data
if isinstance(data, np.ndarray):
return self._optimize_ndarray(data)
elif isinstance(data, list):
return self._optimize_list(data)
elif isinstance(data, dict):
return self._optimize_dict(data)
else:
return data
def _optimize_ndarray(self, array: np.ndarray) -> np.ndarray:
"""优化NumPy数组"""
if self.optimization_level in [OptimizationLevel.ADVANCED, OptimizationLevel.AGGRESSIVE]:
# 使用更节省内存的数据类型
if array.dtype == np.float64:
return array.astype(np.float32)
elif array.dtype == np.int64:
return array.astype(np.int32)
return array
def _optimize_list(self, lst: list) -> list:
"""优化列表"""
if self.optimization_level == OptimizationLevel.AGGRESSIVE:
# 转换为元组(如果可能)
if len(lst) < 1000: # 小列表
return tuple(lst)
return lst
def _optimize_dict(self, dct: dict) -> dict:
"""优化字典"""
if self.optimization_level == OptimizationLevel.AGGRESSIVE:
# 使用更紧凑的字典实现
from collections import OrderedDict
return OrderedDict(dct)
return dct
def create_memory_pool(self, pool_id: str, size: int, dtype=np.float32):
"""创建内存池"""
if pool_id not in self.memory_pool:
self.memory_pool[pool_id] = np.zeros(size, dtype=dtype)
def get_from_pool(self, pool_id: str, size: int) -> np.ndarray:
"""从内存池获取内存"""
if pool_id in self.memory_pool:
pool = self.memory_pool[pool_id]
if len(pool) >= size:
return pool[:size]
# 如果内存池不足,创建新的
self.create_memory_pool(pool_id, size)
return self.memory_pool[pool_id][:size]
def cache_object(self, key: str, obj: Any, max_size: int = 1000):
"""缓存对象"""
if len(self.object_cache) >= max_size:
# 使用LRU策略清理缓存
oldest_key = next(iter(self.object_cache))
del self.object_cache[oldest_key]
self.object_cache[key] = obj
def get_cached_object(self, key: str) -> Optional[Any]:
"""获取缓存对象"""
return self.object_cache.get(key)
def clear_cache(self):
"""清空缓存"""
self.object_cache.clear()
self.memory_pool.clear()
class DiskCacheOptimizer:
"""磁盘缓存优化器"""
def __init__(self, cache_dir: str = "./cache", max_size_mb: int = 1000):
self.cache_dir = cache_dir
self.max_size_mb = max_size_mb
os.makedirs(cache_dir, exist_ok=True)
def cache_to_disk(self, key: str, data: Any, compress: bool = True):
"""缓存到磁盘"""
import pickle
import gzip
# 创建缓存文件名
cache_file = os.path.join(self.cache_dir, f"{hash(key)}.cache")
# 序列化数据
if compress:
with gzip.open(cache_file, 'wb') as f:
pickle.dump(data, f)
else:
with open(cache_file, 'wb') as f:
pickle.dump(data, f)
# 检查缓存大小
self._manage_cache_size()
def load_from_disk(self, key: str) -> Optional[Any]:
"""从磁盘加载"""
import pickle
import gzip
cache_file = os.path.join(self.cache_dir, f"{hash(key)}.cache")
if not os.path.exists(cache_file):
return None
try:
# 尝试gzip解压
with gzip.open(cache_file, 'rb') as f:
return pickle.load(f)
except:
# 如果不是gzip压缩,尝试普通加载
try:
with open(cache_file, 'rb') as f:
return pickle.load(f)
except:
return None
def _manage_cache_size(self):
"""管理缓存大小"""
import shutil
# 计算当前缓存大小
total_size = 0
cache_files = []
for filename in os.listdir(self.cache_dir):
if filename.endswith('.cache'):
filepath = os.path.join(self.cache_dir, filename)
size = os.path.getsize(filepath)
total_size += size
cache_files.append((filepath, os.path.getmtime(filepath)))
# 转换为MB
total_size_mb = total_size / (1024 * 1024)
# 如果超过最大大小,删除最旧的文件
if total_size_mb > self.max_size_mb:
# 按修改时间排序
cache_files.sort(key=lambda x: x[1])
for filepath, _ in cache_files:
if total_size_mb <= self.max_size_mb * 0.8: # 保留80%的空间
break
# 删除文件
os.remove(filepath)
total_size_mb -= os.path.getsize(filepath) / (1024 * 1024)
def clear_disk_cache(self):
"""清空磁盘缓存"""
import shutil
if os.path.exists(self.cache_dir):
shutil.rmtree(self.cache_dir)
os.makedirs(self.cache_dir, exist_ok=True)
class DeploymentOptimizer:
"""部署优化器"""
def __init__(self, app_name: str, version: str = "1.0.0"):
self.app_name = app_name
self.version = version
self.build_config = {}
def create_installer(self, output_dir: str = "./dist"):
"""创建安装程序"""
import subprocess
import sys
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
# 使用PyInstaller创建可执行文件
pyinstaller_cmd = [
sys.executable, "-m", "PyInstaller",
"--name", f"{self.app_name}_{self.version}",
"--onefile",
"--windowed",
"--add-data", "plugins:plugins",
"--add-data", "themes:themes",
"--add-data", "config:config",
"--icon", "ewcc_icon.ico",
"ewcc_integrated_system.py"
]
try:
subprocess.run(pyinstaller_cmd, check=True)
print(f"安装程序创建成功: {output_dir}")
except subprocess.CalledProcessError as e:
print(f"创建安装程序失败: {e}")
def create_docker_image(self, dockerfile_path: str = "./Dockerfile"):
"""创建Docker镜像"""
import subprocess
# 构建Docker镜像
docker_cmd = [
"docker", "build",
"-t", f"{self.app_name.lower()}:{self.version}",
"-f", dockerfile_path,
"."
]
try:
subprocess.run(docker_cmd, check=True)
print(f"Docker镜像创建成功: {self.app_name}:{self.version}")
except subprocess.CalledProcessError as e:
print(f"创建Docker镜像失败: {e}")
except FileNotFoundError:
print("Docker未安装或未在PATH中")
def generate_requirements(self, output_file: str = "requirements.txt"):
"""生成requirements.txt文件"""
import pkg_resources
# 获取已安装的包
installed_packages = pkg_resources.working_set
with open(output_file, 'w', encoding='utf-8') as f:
for package in installed_packages:
f.write(f"{package.key}=={package.version}\n")
print(f"requirements.txt已生成: {output_file}")
def optimize_for_production(self):
"""生产环境优化"""
# 禁用调试功能
import sys
if hasattr(sys, 'gettrace') and sys.gettrace() is not None:
print("警告: 在调试模式下运行,性能可能受影响")
# 设置生产环境变量
os.environ['PYTHONOPTIMIZE'] = '1'
os.environ['PYTHONDONTWRITEBYTECODE'] = '1'
# 优化Python设置
import sys
sys.setrecursionlimit(10000)
def create_config_file(self, config: Dict[str, Any], output_file: str = "config.yaml"):
"""创建配置文件"""
import yaml
with open(output_file, 'w', encoding='utf-8') as f:
yaml.dump(config, f, default_flow_style=False)
print(f"配置文件已生成: {output_file}")
def performance_test(self, test_cases: List[Callable]):
"""性能测试"""
results = {}
for test_case in test_cases:
test_name = test_case.__name__
# 测量执行时间
start_time = time.perf_counter()
test_case()
execution_time = time.perf_counter() - start_time
# 测量内存使用
import tracemalloc
tracemalloc.start()
test_case()
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
results[test_name] = {
'execution_time': execution_time,
'memory_peak': peak / 1024 / 1024, # MB
'memory_current': current / 1024 / 1024 # MB
}
return results
# 使用示例
if __name__ == "__main__":
# 创建性能优化器
optimizer = PerformanceOptimizer(OptimizationLevel.ADVANCED)
optimizer.start()
# 示例函数
@optimizer.profile_function
@optimizer.cache_result
def expensive_computation(n: int) -> int:
time.sleep(0.1) # 模拟耗时计算
return n * n
# 测试缓存
print(expensive_computation(5)) # 计算并缓存
print(expensive_computation(5)) # 从缓存获取
# 获取性能报告
report = optimizer.get_performance_report()
print(f"缓存效率: {report['cache_efficiency']:.2%}")
optimizer.stop()
10. 总结与展望
10.1 本博客的技术成果总结
通过本博客,我们构建了一个完整的综合电子战指挥控制台系统,展示了tkinter/ttk在构建复杂专业系统方面的强大能力:

10.2 关键技术突破
-
复杂布局管理:
-
实现了可拖拽、可停靠、可最大化的多视图系统
-
支持多种布局模式和预设配置
-
视图间协同和数据同步机制
-
-
高性能数据处理:
-
构建了完整的数据流水线框架
-
支持多级数据处理和可视化
-
实现了高效的数据缓存和复用机制
-
-
可扩展架构:
-
完整的插件化系统设计
-
动态加载和热插拔支持
-
统一的插件接口规范
-
-
现代化UI系统:
-
主题引擎支持动态换肤
-
完整的控件样式系统
-
响应式布局和自适应设计
-
-
分布式系统支持:
-
网络通信框架
-
节点发现和管理
-
负载均衡和故障转移
-
10.3 实际应用价值
-
军事指挥系统:
-
电子战态势监控
-
频谱管理和分析
-
资源调度和优化
-
-
工业控制系统:
-
实时数据监控
-
分布式控制
-
故障诊断和预警
-
-
科研仿真平台:
-
算法验证和测试
-
数据分析和可视化
-
多学科协同研究
-
-
教育培训系统:
-
模拟训练环境
-
交互式教学
-
技能评估和考核
-
10.4 未来发展方向

10.5 结语
本博客通过构建综合电子战指挥控制台系统,全面展示了tkinter/ttk在构建复杂专业系统方面的能力。我们不仅实现了复杂的业务逻辑和算法,还构建了美观、易用、高性能的用户界面。
核心价值:
-
证明了Python标准库能够胜任专业级复杂系统的开发
-
展示了现代化GUI设计的最佳实践
-
提供了完整的系统架构和设计模式参考
-
开源了所有代码,便于学习和二次开发
技术启示:
-
不要低估标准库:tkinter/ttk经过合理设计和优化,完全可以构建专业级应用
-
架构决定成败:良好的系统架构是复杂项目成功的关键
-
性能是设计出来的:从架构层面考虑性能,而不是事后优化
-
可扩展性是必须的:为未来的功能扩展预留接口