import tkinter as tk
from tkinter import ttk, messagebox
import threading
import time
import datetime
import paramiko
import threading
class RemoteAdminUI:
def __init__(self, root):
"""初始化主应用程序窗口和所有UI组件"""
self.root = root
self.root.title("Linux远程管理工具")
self.root.geometry("1200x700")
self.ssh_client = None # 添加这行
self.is_connecting = False # 添加这行
# 状态变量
self.is_connected = False
self.current_module = "file_browser"
self.ui_initialized = False # 添加UI初始化标志
# 先初始化module_frames字典
self.module_frames = {}
# 设置样式
self.setup_styles()
# 构建UI
self.setup_ui()
# 标记UI初始化完成
self.ui_initialized = True
# 启动模拟连接状态更新
self.update_connection_status()
def setup_styles(self):
"""配置应用程序的视觉样式"""
style = ttk.Style()
style.theme_use('clam')
# 自定义颜色
self.bg_color = "#f0f0f0"
self.sidebar_color = "#2c3e50"
self.topbar_color = "#3498db"
self.status_connected = "#2ecc71"
self.status_disconnected = "#e74c3c"
# 配置组件样式
style.configure("Sidebar.TButton",
foreground="white",
background=self.sidebar_color,
padding=10,
font=('Segoe UI', 10))
style.configure("Topbar.TFrame", background=self.topbar_color)
style.configure("Status.TFrame", background="#ecf0f1")
def ssh_connect_thread(self, host, port):
"""SSH连接线程"""
try:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(
hostname=host,
port=port,
username=self.user_entry.get(),
password=self.pass_entry.get(),
timeout=10
)
self.ssh_client = client
self.is_connected = True
# 更新UI
self.root.after(0, self.on_connected)
except Exception as e:
self.root.after(0, self.on_connection_failed, str(e))
def on_connection_failed(self, error_msg):
"""连接失败处理"""
self.connect_btn.config(state="normal")
self.log(f"连接失败: {error_msg}", "ERROR")
self.status_bar.config(text="连接失败")
messagebox.showerror("连接错误", f"无法连接到服务器:\n{error_msg}")
def disconnect_ssh(self):
"""断开SSH连接"""
if self.ssh_client:
self.ssh_client.close()
self.ssh_client = None
self.is_connected = False
self.connect_btn.config(text="连接")
self.update_status_indicator(False)
self.log("已断开连接", "INFO")
self.status_bar.config(text="已断开连接")
self.connect_btn.config(state="normal")
def setup_ui(self):
"""设置应用程序的主要UI布局和组件"""
# 创建主容器
self.main_container = ttk.Frame(self.root)
self.main_container.pack(fill=tk.BOTH, expand=True)
# 1. 顶部连接状态栏
self.setup_topbar()
# 2. 主内容区域(侧边栏 + 工作区)
self.content_frame = ttk.Frame(self.main_container)
self.content_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=(0, 5))
# 2.1 侧边导航面板
self.setup_sidebar()
# 2.2 主工作区
self.setup_workspace()
# 3. 底部面板
self.setup_bottombar()
# 设置默认模块(在UI完全初始化后)
self.root.after(100, lambda: self.switch_module("file_browser"))
def setup_topbar(self):
"""创建顶部连接状态栏"""
topbar = ttk.Frame(self.main_container, style="Topbar.TFrame", height=50)
topbar.pack(fill=tk.X, padx=5, pady=5)
topbar.pack_propagate(False) # 固定高度
# 左侧:连接信息
conn_frame = ttk.Frame(topbar)
conn_frame.pack(side=tk.LEFT, padx=15)
ttk.Label(conn_frame, text="服务器:",
foreground="white",
background=self.topbar_color,
font=('Segoe UI', 10)).pack(side=tk.LEFT)
self.server_entry = ttk.Entry(conn_frame, width=25)
self.server_entry.insert(0, "192.168.1.100:22")
self.server_entry.pack(side=tk.LEFT, padx=(5, 0))
self.user_entry = ttk.Entry(conn_frame, width=12)
self.user_entry.insert(0, "root")
self.user_entry.pack(side=tk.LEFT, padx=5)
self.pass_entry = ttk.Entry(conn_frame, width=12, show="*")
self.pass_entry.insert(0, "password")
self.pass_entry.pack(side=tk.LEFT, padx=5)
# 连接按钮
self.connect_btn = ttk.Button(conn_frame, text="连接",
command=self.toggle_connection)
self.connect_btn.pack(side=tk.LEFT, padx=(10, 0))
# 右侧:状态指示器
status_frame = ttk.Frame(topbar)
status_frame.pack(side=tk.RIGHT, padx=15)
ttk.Label(status_frame, text="状态:",
foreground="white",
background=self.topbar_color,
font=('Segoe UI', 10)).pack(side=tk.LEFT)
# 状态指示灯画布
self.status_canvas = tk.Canvas(status_frame, width=20, height=20,
bg=self.topbar_color, highlightthickness=0)
self.status_canvas.pack(side=tk.LEFT, padx=5)
self.status_indicator = self.status_canvas.create_oval(2, 2, 18, 18,
fill=self.status_disconnected)
self.status_label = ttk.Label(status_frame, text="未连接",
foreground="white",
background=self.topbar_color)
self.status_label.pack(side=tk.LEFT)
def setup_sidebar(self):
"""创建左侧导航面板"""
sidebar = ttk.Frame(self.content_frame, width=200)
sidebar.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 5))
sidebar.pack_propagate(False) # 固定宽度
# 标题
title_frame = ttk.Frame(sidebar)
title_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Label(title_frame, text="功能模块",
font=('Segoe UI', 12, 'bold')).pack(pady=10)
# 导航按钮
self.nav_buttons = {}
nav_items = [
("📁 文件管理", "file_browser"),
("⚙️ 进程管理", "process_mgr"),
("📊 系统监控", "system_monitor"),
("🔄 文件传输", "file_transfer"),
("🔧 快速命令", "quick_cmd"),
("⚡ 系统配置", "system_config")
]
for text, module_id in nav_items:
btn = ttk.Button(sidebar, text=text,
style="Sidebar.TButton",
command=lambda m=module_id: self.switch_module(m))
btn.pack(fill=tk.X, pady=2)
self.nav_buttons[module_id] = btn
# 当前选中模块的指示器
self.active_indicator = tk.Frame(sidebar, height=2, bg=self.topbar_color)
# 注意:这里不立即调用switch_module
def setup_workspace(self):
"""创建主工作区(Tab页签)"""
workspace = ttk.Frame(self.content_frame)
workspace.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 创建Notebook(标签页)
self.notebook = ttk.Notebook(workspace)
self.notebook.pack(fill=tk.BOTH, expand=True)
# 创建各个模块的页面
# 文件浏览器页面
self.module_frames["file_browser"] = self.create_file_browser()
self.notebook.add(self.module_frames["file_browser"], text="文件管理")
# 进程管理页面
self.module_frames["process_mgr"] = self.create_process_manager()
self.notebook.add(self.module_frames["process_mgr"], text="进程管理")
# 系统监控页面
self.module_frames["system_monitor"] = self.create_system_monitor()
self.notebook.add(self.module_frames["system_monitor"], text="系统监控")
# 其他页面(占位)
for module_id in ["file_transfer", "quick_cmd", "system_config"]:
frame = ttk.Frame(self.notebook)
ttk.Label(frame, text=f"{module_id.replace('_', ' ').title()} 模块开发中...",
font=('Segoe UI', 16)).pack(expand=True)
self.module_frames[module_id] = frame
display_name = module_id.split('_')[0].title()
if module_id == "file_transfer":
display_name = "文件传输"
elif module_id == "quick_cmd":
display_name = "快速命令"
elif module_id == "system_config":
display_name = "系统配置"
self.notebook.add(frame, text=display_name)
# 绑定标签页切换事件
self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)
def create_file_browser(self):
"""创建文件浏览器界面"""
frame = ttk.Frame(self.notebook)
# 工具栏
toolbar = ttk.Frame(frame)
toolbar.pack(fill=tk.X, pady=(0, 10))
ttk.Button(toolbar, text="刷新", command=self.refresh_files).pack(side=tk.LEFT, padx=2)
ttk.Button(toolbar, text="上传", command=self.upload_file).pack(side=tk.LEFT, padx=2)
ttk.Button(toolbar, text="下载", command=self.download_file).pack(side=tk.LEFT, padx=2)
ttk.Button(toolbar, text="新建文件夹", command=self.create_folder).pack(side=tk.LEFT, padx=2)
# 路径导航
path_frame = ttk.Frame(frame)
path_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Label(path_frame, text="当前路径:").pack(side=tk.LEFT)
self.path_entry = ttk.Entry(path_frame)
self.path_entry.insert(0, "/home/user")
self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
ttk.Button(path_frame, text="转到", command=self.navigate_path).pack(side=tk.LEFT)
# 文件列表
list_frame = ttk.Frame(frame)
list_frame.pack(fill=tk.BOTH, expand=True)
# 列定义
columns = ("name", "size", "type", "permissions", "modified")
self.tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=20)
# 设置列标题
self.tree.heading("name", text="文件名")
self.tree.heading("size", text="大小")
self.tree.heading("type", text="类型")
self.tree.heading("permissions", text="权限")
self.tree.heading("modified", text="修改时间")
# 设置列宽
self.tree.column("name", width=250)
self.tree.column("size", width=100)
self.tree.column("type", width=80)
self.tree.column("permissions", width=100)
self.tree.column("modified", width=150)
# 滚动条
vsb = ttk.Scrollbar(list_frame, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=vsb.set)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
vsb.pack(side=tk.RIGHT, fill=tk.Y)
# 绑定双击事件
self.tree.bind("<Double-1>", self.on_file_double_click)
# 模拟数据
self.populate_sample_files()
return frame
def create_process_manager(self):
"""创建进程管理界面"""
frame = ttk.Frame(self.notebook)
# 控制栏
control_frame = ttk.Frame(frame)
control_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Button(control_frame, text="刷新进程", command=self.refresh_processes).pack(side=tk.LEFT, padx=2)
self.kill_btn = ttk.Button(control_frame, text="结束进程",
command=self.kill_process, state="disabled")
self.kill_btn.pack(side=tk.LEFT, padx=2)
ttk.Label(control_frame, text="筛选:").pack(side=tk.LEFT, padx=(20, 5))
self.filter_combo = ttk.Combobox(control_frame, values=["全部", "我的进程", "高CPU", "高内存"], width=12)
self.filter_combo.set("全部")
self.filter_combo.pack(side=tk.LEFT)
# 进程列表
list_frame = ttk.Frame(frame)
list_frame.pack(fill=tk.BOTH, expand=True)
# 创建Treeview
columns = ("select", "pid", "user", "cpu", "memory", "command")
self.process_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=20)
# 设置列
self.process_tree.heading("select", text="✓")
self.process_tree.heading("pid", text="PID")
self.process_tree.heading("user", text="用户")
self.process_tree.heading("cpu", text="CPU%")
self.process_tree.heading("memory", text="内存%")
self.process_tree.heading("command", text="命令")
self.process_tree.column("select", width=30, stretch=False)
self.process_tree.column("pid", width=80)
self.process_tree.column("user", width=80)
self.process_tree.column("cpu", width=80)
self.process_tree.column("memory", width=80)
self.process_tree.column("command", width=300)
# 滚动条
vsb = ttk.Scrollbar(list_frame, orient="vertical", command=self.process_tree.yview)
self.process_tree.configure(yscrollcommand=vsb.set)
self.process_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
vsb.pack(side=tk.RIGHT, fill=tk.Y)
# 绑定选择事件
self.process_tree.bind("<<TreeviewSelect>>", self.on_process_select)
# 填充示例数据
self.populate_sample_processes()
return frame
def create_system_monitor(self):
"""创建系统监控界面"""
frame = ttk.Frame(self.notebook)
# 使用PanedWindow实现可调整分割
paned = ttk.PanedWindow(frame, orient=tk.HORIZONTAL)
paned.pack(fill=tk.BOTH, expand=True)
# 左侧:系统信息
left_frame = ttk.Frame(paned)
ttk.Label(left_frame, text="系统信息", font=('Segoe UI', 11, 'bold')).pack(pady=(0, 10))
# 信息表格
info_frame = ttk.Frame(left_frame)
info_frame.pack(fill=tk.BOTH, expand=True)
self.info_text = tk.Text(info_frame, height=15, width=30, bg="white", relief=tk.FLAT)
self.info_text.pack(fill=tk.BOTH, expand=True)
# 填充示例系统信息
self.update_system_info()
paned.add(left_frame, weight=1)
# 右侧:实时监控
right_frame = ttk.Frame(paned)
ttk.Label(right_frame, text="实时监控", font=('Segoe UI', 11, 'bold')).pack(pady=(0, 10))
# 监控指标
metrics = ["CPU使用率", "内存使用", "磁盘I/O", "网络流量"]
for metric in metrics:
metric_frame = ttk.Frame(right_frame)
metric_frame.pack(fill=tk.X, pady=5)
ttk.Label(metric_frame, text=metric, width=15).pack(side=tk.LEFT)
progress = ttk.Progressbar(metric_frame, length=150, mode='determinate')
progress.pack(side=tk.LEFT, padx=5)
progress['value'] = 30 # 示例值
ttk.Label(metric_frame, text="30%", width=5).pack(side=tk.LEFT)
paned.add(right_frame, weight=1)
# 底部控制栏
control_frame = ttk.Frame(frame)
control_frame.pack(fill=tk.X, pady=(10, 0))
ttk.Button(control_frame, text="刷新数据", command=self.refresh_monitor).pack(side=tk.LEFT)
self.monitor_var = tk.BooleanVar(value=True)
ttk.Checkbutton(control_frame, text="自动刷新",
variable=self.monitor_var,
command=self.toggle_auto_refresh).pack(side=tk.LEFT, padx=20)
return frame
def setup_bottombar(self):
"""创建底部面板(命令输入和日志)"""
bottombar = ttk.Frame(self.main_container, style="Status.TFrame", height=200)
bottombar.pack(fill=tk.X, padx=5, pady=(0, 5))
bottombar.pack_propagate(False)
# 使用PanedWindow可调整分割
paned = ttk.PanedWindow(bottombar, orient=tk.VERTICAL)
paned.pack(fill=tk.BOTH, expand=True)
# 命令输入部分
cmd_frame = ttk.Frame(paned)
ttk.Label(cmd_frame, text="快速命令:").pack(side=tk.LEFT, padx=(5, 0))
self.cmd_entry = ttk.Entry(cmd_frame)
self.cmd_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5, pady=5)
self.cmd_entry.bind("<Return>", self.execute_command)
ttk.Button(cmd_frame, text="执行", command=self.execute_command).pack(side=tk.LEFT, padx=(0, 5))
paned.add(cmd_frame)
# 日志输出部分
log_frame = ttk.Frame(paned)
# 日志工具栏
log_toolbar = ttk.Frame(log_frame)
log_toolbar.pack(fill=tk.X)
ttk.Label(log_toolbar, text="执行日志").pack(side=tk.LEFT)
ttk.Button(log_toolbar, text="清空", command=self.clear_log).pack(side=tk.RIGHT, padx=5)
ttk.Button(log_toolbar, text="保存", command=self.save_log).pack(side=tk.RIGHT, padx=5)
# 日志文本框
log_text_frame = ttk.Frame(log_frame)
log_text_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=(0, 5))
self.log_text = tk.Text(log_text_frame, bg="white", relief=tk.SUNKEN, height=8)
log_scroll = ttk.Scrollbar(log_text_frame, command=self.log_text.yview)
self.log_text.configure(yscrollcommand=log_scroll.set)
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
log_scroll.pack(side=tk.RIGHT, fill=tk.Y)
paned.add(log_frame)
# 状态栏
self.status_bar = ttk.Label(self.main_container, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.pack(fill=tk.X, padx=5, pady=(0, 5))
# ==================== 事件处理方法 ====================
def toggle_connection(self):
if not self.is_connected:
# === 替换开始 ===
server_info = self.server_entry.get().split(':')
host = server_info[0]
port = int(server_info[1]) if len(server_info) > 1 else 22
self.log(f"正在连接到 {host}:{port}...", "INFO")
self.status_bar.config(text="正在连接...")
self.connect_btn.config(state="disabled")
# 在新线程中连接
threading.Thread(target=self.ssh_connect_thread,
args=(host, port), daemon=True).start()
# === 替换结束 ===
else:
# 断开连接逻辑
self.disconnect_ssh()
def simulate_connection(self):
"""模拟连接过程(实际使用时替换为真正的SSH连接)"""
time.sleep(1) # 模拟网络延迟
self.is_connected = True
self.root.after(0, self.on_connected)
def on_connected(self):
"""连接成功后的回调"""
self.connect_btn.config(text="断开")
self.update_status_indicator(True)
self.log("连接成功!", "SUCCESS")
self.status_bar.config(text=f"已连接到 {self.server_entry.get()}")
messagebox.showinfo("连接成功", "已成功连接到服务器")
def on_connected(self):
"""连接成功后的回调"""
self.connect_btn.config(text="断开", state="normal")
self.update_status_indicator(True)
self.log("连接成功!", "SUCCESS")
self.status_bar.config(text=f"已连接到 {self.server_entry.get()}")
# 连接成功后自动刷新进程
self.refresh_processes()
def update_status_indicator(self, connected):
"""更新状态指示灯"""
color = self.status_connected if connected else self.status_disconnected
text = "已连接" if connected else "未连接"
self.status_canvas.itemconfig(self.status_indicator, fill=color)
self.status_label.config(text=text)
def switch_module(self, module_id):
"""切换功能模块"""
self.current_module = module_id
# 更新Notebook选中标签
tab_names = list(self.module_frames.keys())
if module_id in tab_names:
tab_index = tab_names.index(module_id)
self.notebook.select(tab_index)
# 更新导航按钮状态(在实际中可以通过样式变化实现)
if self.ui_initialized: # 只在UI初始化完成后记录日志
self.log(f"切换到 {module_id.replace('_', ' ')} 模块", "INFO")
def on_tab_changed(self, event):
"""标签页切换事件"""
current_tab = self.notebook.index(self.notebook.select())
module_ids = list(self.module_frames.keys())
if current_tab < len(module_ids):
self.current_module = module_ids[current_tab]
def on_file_double_click(self, event):
"""文件列表双击事件"""
selection = self.tree.selection()
if selection: # 确保有选中项
item = selection[0]
values = self.tree.item(item, 'values')
if values and len(values) > 2 and values[2] == "目录":
self.log(f"进入目录: {values[0]}", "INFO")
# 实际应用中这里会刷新文件列表
def on_process_select(self, event):
"""进程列表选择事件"""
selection = self.process_tree.selection()
if selection:
self.kill_btn.config(state="normal")
else:
self.kill_btn.config(state="disabled")
def execute_command(self, event=None):
"""执行快速命令"""
cmd = self.cmd_entry.get().strip()
if cmd:
self.log(f"执行命令: {cmd}", "COMMAND")
# 模拟命令执行
self.log(f"输出: 命令 '{cmd}' 执行完成", "OUTPUT")
self.cmd_entry.delete(0, tk.END)
self.status_bar.config(text=f"命令执行完成: {cmd[:30]}..." if len(cmd) > 30 else f"命令执行完成: {cmd}")
def log(self, message, level="INFO"):
"""添加日志消息"""
if not hasattr(self, 'log_text') or self.log_text is None:
return # 如果log_text还没初始化,直接返回
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
# 颜色映射
colors = {
"INFO": "black",
"ERROR": "red",
"SUCCESS": "green",
"COMMAND": "blue",
"OUTPUT": "gray",
"WARNING": "orange"
}
color = colors.get(level, "black")
# 插入带格式的文本
self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
# 获取刚插入文本的起始和结束位置
start_index = self.log_text.index(f"end-2l linestart")
end_index = self.log_text.index("end-1c")
# 为刚插入的文本添加标签
self.log_text.tag_add(level, start_index, end_index)
self.log_text.tag_config(level, foreground=color)
self.log_text.see(tk.END) # 自动滚动到底部
def clear_log(self):
"""清空日志"""
if hasattr(self, 'log_text'):
self.log_text.delete(1.0, tk.END)
def save_log(self):
"""保存日志到文件"""
# 这里简化处理,实际应用中可以添加文件对话框
self.log("日志保存功能开发中...", "INFO")
# ==================== 示例数据填充方法 ====================
def populate_sample_files(self):
"""填充示例文件数据"""
sample_files = [
("/home/user", "目录", "drwxr-xr-x", "2023-10-01 10:30"),
("document.pdf", "PDF文件", "-rw-r--r--", "2023-10-02 14:25", "2.3MB"),
("script.py", "Python脚本", "-rwxr-xr-x", "2023-10-03 09:15", "15KB"),
("data.json", "JSON文件", "-rw-r--r--", "2023-10-04 16:40", "45KB"),
("backup.tar.gz", "压缩文件", "-rw-r--r--", "2023-10-05 11:20", "120MB"),
]
self.tree.delete(*self.tree.get_children()) # 清空现有数据
for file in sample_files:
self.tree.insert("", tk.END, values=file)
def populate_sample_processes(self):
"""填充示例进程数据"""
sample_processes = [
("✓", "1234", "root", "12.3%", "4.5%", "nginx -g daemon off;"),
("✓", "5678", "www-data", "1.2%", "2.1%", "python /app/main.py"),
("✓", "9012", "user", "0.5%", "1.8%", "ssh-agent"),
("✓", "3456", "mysql", "3.2%", "15.3%", "mysqld"),
("✓", "7890", "root", "0.1%", "0.3%", "systemd-logind"),
]
self.process_tree.delete(*self.process_tree.get_children()) # 清空现有数据
for proc in sample_processes:
self.process_tree.insert("", tk.END, values=proc)
def update_system_info(self):
"""更新系统信息显示"""
info = """主机名: ubuntu-server
系统: Ubuntu 22.04 LTS
内核: 5.15.0-78-generic
CPU: Intel Xeon 8核心
内存: 16GB (已用 8.2GB)
磁盘: 500GB (已用 45%)
IP地址: 192.168.1.100
运行时间: 15天 3小时
负载: 0.12, 0.08, 0.05"""
self.info_text.delete(1.0, tk.END)
self.info_text.insert(1.0, info)
def update_connection_status(self):
"""定时更新连接状态(模拟)"""
# 在实际应用中,这里可以检查SSH连接是否仍然活跃
if self.is_connected:
self.root.after(5000, self.update_connection_status) # 每5秒检查一次
# ==================== 占位功能方法 ====================
def refresh_files(self):
"""刷新文件列表"""
self.log("刷新文件列表", "INFO")
# 实际应用中这里会重新获取文件列表
def upload_file(self):
"""上传文件"""
self.log("打开文件上传对话框", "INFO")
def download_file(self):
"""下载文件"""
self.log("下载选中的文件", "INFO")
def create_folder(self):
"""创建新文件夹"""
self.log("创建新文件夹", "INFO")
def navigate_path(self):
"""导航到指定路径"""
path = self.path_entry.get()
self.log(f"导航到路径: {path}", "INFO")
def refresh_processes(self):
"""刷新进程列表 - 真实SSH数据"""
if not self.is_connected or not self.ssh_client:
self.log("未连接到服务器", "ERROR")
return
try:
# 执行ps命令获取进程信息
stdin, stdout, stderr = self.ssh_client.exec_command(
"ps aux --sort=-%cpu | head -20"
)
output = stdout.read().decode('utf-8', errors='ignore')
# 清空现有数据
self.process_tree.delete(*self.process_tree.get_children())
# 解析并添加进程数据
lines = output.strip().split('\n')
if len(lines) > 1: # 跳过表头
for line in lines[1:]: # 从第二行开始
parts = line.split(maxsplit=10)
if len(parts) >= 11:
# 格式: select, pid, user, cpu, memory, command
self.process_tree.insert("", tk.END, values=(
"✓", # 选择框
parts[1], # PID
parts[0], # 用户
f"{float(parts[2]):.1f}%", # CPU
f"{float(parts[3]):.1f}%", # 内存
parts[10][:50] # 命令(截断)
))
self.log("进程列表已刷新", "SUCCESS")
except Exception as e:
self.log(f"获取进程失败: {str(e)}", "ERROR")
def kill_process(self):
"""结束选中的进程"""
selection = self.process_tree.selection()
if selection:
item = self.process_tree.item(selection[0])
values = item['values']
if values and len(values) > 1:
pid = values[1]
self.log(f"结束进程 PID: {pid}", "WARNING")
def refresh_monitor(self):
"""刷新系统监控数据"""
self.log("刷新监控数据", "INFO")
def toggle_auto_refresh(self):
"""切换自动刷新"""
if self.monitor_var.get():
self.log("启用自动刷新", "INFO")
else:
self.log("禁用自动刷新", "INFO")
def main():
"""应用程序入口点"""
root = tk.Tk()
app = RemoteAdminUI(root)
root.mainloop()
if __name__ == "__main__":
main()