系统监控服务

查看 7|回复 1
作者:lgk   
这是一个基于Python的系统监控工具,提供GUI界面和Web监控页面,可以实时监控系统性能指标,
同时能够在 Windows 电脑开启一个 HTTP 服务,它既能够通过网页进行访问,也能够通过 api调用,点击即可复制各类 API 调用地址。

1.刷新接口,选择接口,然后启动服务,随后可以复制对应的地址


主界面.png (182.14 KB, 下载次数: 0)
下载附件
2025-10-6 22:43 上传

2.web界面展示


web主界面.jpg (214.08 KB, 下载次数: 0)
下载附件
2025-10-6 22:48 上传

3.纯文本请求内容展示


api请求纯文本.png (196.65 KB, 下载次数: 0)
下载附件
2025-10-6 22:48 上传

主要功能
1. 系统性能监控
CPU使用率:实时监控CPU负载
内存使用:显示内存使用量、总量和可用内存
磁盘使用:监控硬盘使用情况
网络统计:上传/下载速度、总流量统计
2. Web监控界面
美观的HTML页面:使用现代CSS设计
多格式输出:
HTML页面(主界面)
纯文本状态(Ubuntu风格)
JSON API接口
自动刷新:每5秒自动更新数据
IP地址显示:显示所有网络接口的IPv4/IPv6地址
复制功能:一键复制IP地址
3. GUI控制界面
网络接口选择:可选择绑定到特定网络接口
服务控制:启动/停止监控服务
实时信息显示:在GUI中显示系统状态
浏览器集成:一键在浏览器中打开监控页面
4. 系统托盘支持
后台运行:支持最小化到系统托盘
托盘菜单:显示/退出功能
自定义图标:支持外部图标文件或自动生成
特色功能
1. 网络接口自动检测
自动检测所有可用的网络接口
包括IPv4和IPv6地址
2. 跨平台兼容
Windows字体兼容处理
资源路径管理(支持打包环境)
系统托盘适配
3. 用户体验优化
5秒自动刷新
一键复制IP地址
优雅的进度条显示
响应式设计(支持移动设备)
4.以下是代码,有需要的大佬可以继续开发:
[Python] 纯文本查看 复制代码import tkinter as tk
from tkinter import ttk, messagebox
import threading
import webbrowser
import psutil
import time
import json
import socket
import tkinter.font as tkFont
import platform
from datetime import datetime, timedelta
import pystray
from PIL import Image, ImageDraw
import sys
import os
# 延迟导入Flask,避免命名冲突
def setup_flask():
    from flask import Flask, Response
    return Flask
class SystemMonitor:
    def __init__(self):
        Flask = setup_flask()
        self.app = Flask(__name__)
        self.setup_routes()
        self.server_thread = None
        self.server_running = False
        self.network_stats = {
            'bytes_sent': 0,
            'bytes_recv': 0,
            'last_update': time.time()
        }
        self.selected_interface = '0.0.0.0'  # 默认绑定所有接口
    def set_interface(self, interface):
        """设置要绑定的网络接口"""
        self.selected_interface = interface
    def setup_routes(self):
        @self.app.route('/')
        def index():
            return self.get_html_page()
        @self.app.route('/status')
        def status():
            return self.get_text_status()
        @self.app.route('/api/json')
        def api_json():
            return self.get_json_data()
    def get_system_info(self):
        """获取系统性能信息"""
        try:
            # CPU使用率
            cpu_percent = psutil.cpu_percent(interval=0.1)
            # 内存使用率
            memory = psutil.virtual_memory()
            memory_percent = memory.percent
            memory_used = memory.used / (1024 ** 3)  # GB
            memory_total = memory.total / (1024 ** 3)  # GB
            memory_available = memory.available / (1024 ** 3)  # GB
            # 硬盘使用率
            disk = psutil.disk_usage('/')
            disk_percent = disk.percent
            disk_used = disk.used / (1024 ** 3)  # GB
            disk_total = disk.total / (1024 ** 3)  # GB
            # 网络统计
            net_io = psutil.net_io_counters()
            current_time = time.time()
            time_diff = current_time - self.network_stats['last_update']
            # 计算网络速率 (Bytes/s)
            upload_speed = (net_io.bytes_sent - self.network_stats['bytes_sent']) / time_diff
            download_speed = (net_io.bytes_recv - self.network_stats['bytes_recv']) / time_diff
            # 更新网络统计
            self.network_stats.update({
                'bytes_sent': net_io.bytes_sent,
                'bytes_recv': net_io.bytes_recv,
                'last_update': current_time
            })
            # 转换为更友好的单位
            def format_speed(bytes_per_sec):
                if bytes_per_sec >= 1024 ** 2:  # MB/s
                    return f"{bytes_per_sec / (1024 ** 2):.2f} MB/s"
                elif bytes_per_sec >= 1024:  # KB/s
                    return f"{bytes_per_sec / 1024:.2f} KB/s"
                else:
                    return f"{bytes_per_sec:.2f} B/s"
            # 获取系统信息
            hostname = socket.gethostname()
            os_info = f"{platform.system()} {platform.release()}"
            boot_time = psutil.boot_time()
            uptime_seconds = time.time() - boot_time
            uptime_str = str(timedelta(seconds=uptime_seconds)).split('.')[0]
            # 获取CPU信息
            cpu_info = "Unknown"
            cpu_cores = psutil.cpu_count(logical=False)  # 物理核心
            cpu_threads = psutil.cpu_count(logical=True)  # 逻辑核心
            try:
                cpu_freq = psutil.cpu_freq()
                if cpu_freq:
                    cpu_info = f"{cpu_freq.max:.0f}MHz"
            except:
                pass
            # 获取网络接口信息
            net_if_addrs = psutil.net_if_addrs()
            net_if_stats = psutil.net_if_stats()
            network_interface = "Unknown"
            ip_address = "未获取"
            gateway = "未获取"
            # 获取所有IPv4和IPv6地址
            ipv4_addresses = []
            ipv6_addresses = []
            for interface, addrs in net_if_addrs.items():
                # 跳过回环接口
                if interface == 'lo':
                    continue
                # 检查接口状态
                if interface in net_if_stats and not net_if_stats[interface].isup:
                    continue
                for addr in addrs:
                    if addr.family == socket.AF_INET:  # IPv4
                        if not addr.address.startswith('127.'):
                            ipv4_addresses.append({
                                'interface': interface,
                                'address': addr.address,
                                'netmask': addr.netmask
                            })
                            # 设置默认网络接口
                            if network_interface == "Unknown":
                                network_interface = interface
                                ip_address = addr.address
                    elif addr.family == socket.AF_INET6:  # IPv6
                        # 跳过链路本地地址
                        if not addr.address.startswith('fe80:'):
                            ipv6_addresses.append({
                                'interface': interface,
                                'address': addr.address,
                                'netmask': addr.netmask
                            })
            # 获取磁盘分区信息
            disk_partitions = psutil.disk_partitions()
            disk_info = "Unknown"
            if disk_partitions:
                disk_info = disk_partitions[0].device
            return {
                'cpu_percent': cpu_percent,
                'memory_percent': memory_percent,
                'memory_used': memory_used,
                'memory_total': memory_total,
                'memory_available': memory_available,
                'disk_percent': disk_percent,
                'disk_used': disk_used,
                'disk_total': disk_total,
                'upload_speed': format_speed(upload_speed),
                'download_speed': format_speed(download_speed),
                'total_upload': net_io.bytes_sent / (1024 ** 2),  # MB
                'total_download': net_io.bytes_recv / (1024 ** 2),  # MB
                'timestamp': time.strftime('%Y/%m/%d %H:%M:%S'),
                'hostname': hostname,
                'os_info': os_info,
                'uptime': uptime_str,
                'cpu_info': cpu_info,
                'cpu_cores': cpu_cores,
                'cpu_threads': cpu_threads,
                'network_interface': network_interface,
                'ip_address': ip_address,
                'gateway': gateway,
                'disk_info': disk_info,
                'ipv4_addresses': ipv4_addresses,
                'ipv6_addresses': ipv6_addresses
            }
        except Exception as e:
            return {'error': str(e)}
    def get_network_interfaces(self):
        """获取所有可用的网络接口和IP地址"""
        interfaces = {}
        try:
            net_if_addrs = psutil.net_if_addrs()
            for interface, addrs in net_if_addrs.items():
                for addr in addrs:
                    if addr.family == socket.AF_INET and not addr.address.startswith('127.'):
                        interfaces[interface] = addr.address
                        break
        except Exception as e:
            print(f"获取网络接口失败: {e}")
        # 添加所有接口选项
        interfaces['所有接口 (0.0.0.0)'] = '0.0.0.0'
        interfaces['本地主机 (127.0.0.1)'] = '127.0.0.1'
        return interfaces
    def get_html_page(self):
        """生成优美的HTML监控页面"""
        info = self.get_system_info()
        if 'error' in info:
            return f"系统监控错误: {info['error']}
"
        # 生成IPv4地址列表HTML
        ipv4_list_html = ""
        if info['ipv4_addresses']:
            for addr_info in info['ipv4_addresses']:
                ipv4_list_html += f"""
               
                    {addr_info['interface']}
                    {addr_info['address']}
                    
                        复制
                    

               
                """
        else:
            ipv4_list_html = "未找到IPv4地址"
        # 生成IPv6地址列表HTML
        ipv6_list_html = ""
        if info['ipv6_addresses']:
            for addr_info in info['ipv6_addresses']:
                ipv6_list_html += f"""
               
                    {addr_info['interface']}
                    {addr_info['address']}
                    
                        复制
                    

               
                """
        else:
            ipv6_list_html = "未找到IPv6地址"
        html = f"""
        
        
        
            
            
            系统监控面板
            
                * {{
                    margin: 0;
                    padding: 0;
                    box-sizing: border-box;
                }}
                body {{
                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    min-height: 100vh;
                    padding: 20px;
                }}
                .container {{
                    max-width: 1400px;
                    margin: 0 auto;
                }}
                .header {{
                    text-align: center;
                    color: white;
                    margin-bottom: 30px;
                }}
                .header h1 {{
                    font-size: 2.5rem;
                    margin-bottom: 10px;
                    text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
                }}
                .header p {{
                    font-size: 1.1rem;
                    opacity: 0.9;
                }}
                .stats-grid {{
                    display: grid;
                    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
                    gap: 20px;
                    margin-bottom: 20px;
                }}
                .stat-card {{
                    background: rgba(255, 255, 255, 0.95);
                    border-radius: 15px;
                    padding: 25px;
                    box-shadow: 0 8px 32px rgba(0,0,0,0.1);
                    backdrop-filter: blur(10px);
                    border: 1px solid rgba(255,255,255,0.2);
                    transition: transform 0.3s ease, box-shadow 0.3s ease;
                }}
                .stat-card:hover {{
                    transform: translateY(-5px);
                    box-shadow: 0 12px 40px rgba(0,0,0,0.15);
                }}
                .stat-header {{
                    display: flex;
                    align-items: center;
                    margin-bottom: 15px;
                }}
                .stat-icon {{
                    font-size: 2rem;
                    margin-right: 15px;
                    color: #667eea;
                }}
                .stat-title {{
                    font-size: 1.3rem;
                    font-weight: 600;
                    color: #333;
                }}
                .stat-value {{
                    font-size: 2rem;
                    font-weight: bold;
                    color: #2c3e50;
                    margin-bottom: 10px;
                }}
                .progress-bar {{
                    width: 100%;
                    height: 10px;
                    background: #ecf0f1;
                    border-radius: 5px;
                    overflow: hidden;
                    margin: 10px 0;
                }}
                .progress-fill {{
                    height: 100%;
                    border-radius: 5px;
                    transition: width 0.3s ease;
                }}
                .cpu-progress {{ background: linear-gradient(90deg, #e74c3c, #e67e22); }}
                .memory-progress {{ background: linear-gradient(90deg, #3498db, #2980b9); }}
                .disk-progress {{ background: linear-gradient(90deg, #9b59b6, #8e44ad); }}
                .stat-details {{
                    font-size: 0.9rem;
                    color: #7f8c8d;
                    margin-top: 5px;
                }}
                .network-stats {{
                    display: grid;
                    grid-template-columns: 1fr 1fr;
                    gap: 15px;
                    margin-top: 10px;
                }}
                .network-stat {{
                    text-align: center;
                    padding: 10px;
                    background: rgba(52, 152, 219, 0.1);
                    border-radius: 8px;
                }}
                .network-label {{
                    font-size: 0.8rem;
                    color: #7f8c8d;
                    margin-bottom: 5px;
                }}
                .network-value {{
                    font-size: 1.1rem;
                    font-weight: bold;
                    color: #2c3e50;
                }}
                /* IP地址卡片样式 */
                .ip-addresses-container {{
                    display: grid;
                    grid-template-columns: 1fr 1fr;
                    gap: 20px;
                    margin-bottom: 20px;
                }}
                .ip-card {{
                    background: rgba(255, 255, 255, 0.95);
                    border-radius: 15px;
                    padding: 25px;
                    box-shadow: 0 8px 32px rgba(0,0,0,0.1);
                    backdrop-filter: blur(10px);
                    border: 1px solid rgba(255,255,255,0.2);
                }}
                .ip-card-header {{
                    display: flex;
                    align-items: center;
                    margin-bottom: 15px;
                }}
                .ip-card-icon {{
                    font-size: 1.5rem;
                    margin-right: 10px;
                }}
                .ip-card-title {{
                    font-size: 1.2rem;
                    font-weight: 600;
                    color: #333;
                }}
                .ip-address-list {{
                    max-height: 200px;
                    overflow-y: auto;
                }}
                .ip-address-item {{
                    display: flex;
                    align-items: center;
                    padding: 8px 0;
                    border-bottom: 1px solid rgba(0,0,0,0.1);
                }}
                .ip-interface {{
                    font-weight: 600;
                    min-width: 100px;
                    color: #3498db;
                }}
                .ip-address {{
                    flex-grow: 1;
                    font-family: monospace;
                    color: #2c3e50;
                    word-break: break-all;
                }}
                .copy-btn {{
                    background: #3498db;
                    border: none;
                    border-radius: 5px;
                    color: white;
                    cursor: pointer;
                    padding: 5px 10px;
                    margin-left: 10px;
                    transition: background 0.3s ease;
                    min-width: 60px;
                }}
                .copy-btn:hover {{
                    background: #2980b9;
                }}
                .copy-btn.copied {{
                    background: #27ae60;
                }}
                .copy-text {{
                    font-size: 0.8rem;
                }}
                .no-addresses {{
                    text-align: center;
                    color: #7f8c8d;
                    font-style: italic;
                    padding: 20px 0;
                }}
                .footer {{
                    text-align: center;
                    color: white;
                    margin-top: 30px;
                    opacity: 0.8;
                }}
                @media (max-width: 768px) {{
                    .stats-grid {{
                        grid-template-columns: 1fr;
                    }}
                    .network-stats {{
                        grid-template-columns: 1fr;
                    }}
                    .ip-addresses-container {{
                        grid-template-columns: 1fr;
                    }}
                }}
            
        
        
            
               
                    🖥️ 系统监控面板
                    实时系统性能监控 | 最后更新: {info['timestamp']}
               
               
                    
                    
                        
                            🌐
                            IPv4 地址
                        
                        
                            {ipv4_list_html}
                        
                    
                    
                    
                        
                            🌍
                            IPv6 地址
                        
                        
                            {ipv6_list_html}
                        
                    
               
               
                    
                    
                        
                            🚀
                            CPU 使用率
                        
                        {info['cpu_percent']}%
                        
                           
                        
                        核心数: {info['cpu_threads']} ({info['cpu_cores']}物理核心)
                    
                    
                    
                        
                            💾
                            内存使用率
                        
                        {info['memory_percent']}%
                        
                           
                        
                        已用: {info['memory_used']:.1f} GB / 总共: {info['memory_total']:.1f} GB
                    
                    
                    
                        
                            💽
                            硬盘使用率
                        
                        {info['disk_percent']}%
                        
                           
                        
                        已用: {info['disk_used']:.1f} GB / 总共: {info['disk_total']:.1f} GB
                    
                    
                    
                        
                            📡
                            网络统计
                        
                        
                           
                                上传速度
                                {info['upload_speed']}
                           
                           
                                下载速度
                                {info['download_speed']}
                           
                           
                                总上传
                                {info['total_upload']:.2f} MB
                           
                           
                                总下载
                                {info['total_download']:.2f} MB
                           
                        
                    
               
               
                    系统监控服务 | 自动刷新: 5秒
                    
               
            
        

        
        """
        return html
    def get_text_status(self):
        """返回纯文本状态信息 - 按照Ubuntu风格格式"""
        info = self.get_system_info()
        if 'error' in info:
            return f"Error: {info['error']}"
        # 构建Ubuntu风格的纯文本报告
        text = f"""{info['hostname']} 🖥️ 系统状态报告
【基础信息】📋
• 操作系统:{info['os_info']}
• 主机名称:{info['hostname']} | 当前时间:{info['timestamp']}
• 系统运行时长:{info['uptime']} ⏱️
【CPU & 内存】💻 + 🧠
• CPU型号:{info['cpu_info']} | 核心数:{info['cpu_threads']}核(物理:{info['cpu_cores']}核)
• CPU使用率:{info['cpu_percent']}%(用户态:未获取 | 系统态:未获取)
• 内存总容量:{info['memory_total']:.2f}GB | 已用:{info['memory_used']:.2f}GB({info['memory_percent']}%)
• 空闲内存:{info['memory_available']:.2f}GB | 缓存内存:{info['memory_total'] - info['memory_used'] - info['memory_available']:.2f}GB
【磁盘 & 网络】💽 + 🌐
• 磁盘分区:{info['disk_info']} | 使用率:{info['disk_percent']}%
• 网卡名称:{info['network_interface']} | IP地址(IPv4):{info['ip_address']} | 网关:{info['gateway']}
• 网络流量:发送 {info['total_upload']:.2f}MB 📤 | 接收 {info['total_download']:.2f}MB 📥
【系统负载】⚖️
• 实时负载:{info['cpu_percent']}% | 说明:Windows下为进程总使用率
"""
        return text
    def get_json_data(self):
        """返回JSON格式数据"""
        info = self.get_system_info()
        from flask import Response
        return Response(
            json.dumps(info, ensure_ascii=False, indent=2),
            mimetype='application/json'
        )
    def start_server(self, host='0.0.0.0', port=5000):
        """启动HTTP服务器"""
        try:
            self.server_running = True
            self.app.run(host=host, port=port, debug=False, use_reloader=False)
        except Exception as e:
            messagebox.showerror("错误", f"启动服务器失败: {str(e)}")
            self.server_running = False
class MonitorApp:
    def __init__(self, root):
        self.root = root
        self.monitor = SystemMonitor()
        self.tray_icon = None
        self.setup_ui()
        # 创建托盘图标
        self.create_tray_icon()
        # 重写关闭窗口的行为
        self.root.protocol('WM_DELETE_WINDOW', self.hide_to_tray)
    def get_resource_path(self, relative_path):
        """获取资源的绝对路径,兼容开发环境和打包后的环境"""
        try:
            # 打包后的环境
            base_path = sys._MEIPASS
        except Exception:
            # 开发环境
            base_path = os.path.abspath(".")
        return os.path.join(base_path, relative_path)
    def set_window_icon(self):
        """设置窗口图标,兼容打包环境"""
        icon_paths = [
            'monitor.ico',
            'icon.ico',
            'icons/monitor.ico',
            'icons/icon.ico'
        ]
        for path in icon_paths:
            try:
                # 尝试获取资源的绝对路径
                absolute_path = self.get_resource_path(path)
                if os.path.exists(absolute_path):
                    self.root.iconbitmap(absolute_path)
                    print(f"成功设置窗口图标: {absolute_path}")
                    return
            except Exception as e:
                print(f"设置窗口图标失败 {path}: {e}")
        print("警告: 未找到可用的图标文件")
    def create_tray_icon(self):
        """创建系统托盘图标"""
        try:
            # 尝试加载外部图标文件
            icon_paths = [
                'monitor.ico',
                'icon.ico',
                'icons/monitor.ico',
                'icons/icon.ico'
            ]
            image = None
            for path in icon_paths:
                try:
                    # 使用get_resource_path获取图标路径
                    absolute_path = self.get_resource_path(path)
                    if os.path.exists(absolute_path):
                        image = Image.open(absolute_path)
                        print(f"使用托盘图标文件: {absolute_path}")
                        break
                except Exception as e:
                    print(f"无法加载托盘图标文件 {path}: {e}")
            # 如果外部图标文件不存在,创建一个简单的图标
            if image is None:
                print("未找到外部图标文件,使用默认图标")
                image = Image.new('RGB', (64, 64), color='#3498db')
                draw = ImageDraw.Draw(image)
                # 绘制一个简单的显示器图标
                draw.rectangle([16, 12, 48, 36], fill='#2c3e50')  # 显示器屏幕
                draw.rectangle([20, 16, 44, 32], fill='#ecf0f1')  # 屏幕内容
                draw.rectangle([28, 36, 36, 42], fill='#2c3e50')  # 显示器支架
                draw.rectangle([24, 42, 40, 44], fill='#2c3e50')  # 显示器底座
            # 创建托盘图标菜单
            menu = pystray.Menu(
                pystray.MenuItem('显示', self.show_window),
                pystray.MenuItem('退出', self.quit_application)
            )
            # 创建托盘图标
            self.tray_icon = pystray.Icon("system_monitor", image, "系统监控", menu)
            # 启动托盘图标线程
            self.setup_tray_icon()
        except Exception as e:
            print(f"创建托盘图标失败: {e}")
            self.tray_icon = None
    def setup_tray_icon(self):
        """在单独的线程中运行托盘图标"""
        if self.tray_icon is None:
            print("警告: 托盘图标未创建,无法启动托盘功能")
            return
        def run_tray():
            try:
                self.tray_icon.run()
            except Exception as e:
                print(f"运行托盘图标时出错: {e}")
        tray_thread = threading.Thread(target=run_tray, daemon=True)
        tray_thread.start()
        print("托盘图标线程已启动")
    def hide_to_tray(self):
        """隐藏窗口到系统托盘"""
        self.root.withdraw()  # 隐藏窗口
        if self.tray_icon is not None:
            messagebox.showinfo("系统监控", "程序已最小化到系统托盘。\n右键点击托盘图标可显示或退出程序。")
        else:
            messagebox.showwarning("系统监控", "程序已最小化,但托盘功能未正常启动。\n请使用任务管理器结束进程。")
    def show_window(self, icon=None, item=None):
        """显示主窗口"""
        self.root.deiconify()  # 显示窗口
        self.root.lift()  # 将窗口置于前台
        self.root.focus_force()  # 强制获取焦点
    def quit_application(self, icon=None, item=None):
        """退出应用程序"""
        # 停止监控服务
        if hasattr(self, 'monitor') and self.monitor.server_running:
            self.monitor.server_running = False
        # 停止托盘图标
        if self.tray_icon:
            try:
                self.tray_icon.stop()
            except:
                pass
        # 退出程序
        self.root.quit()
        self.root.destroy()
        sys.exit(0)
    def set_safe_fonts(self):
        """设置安全的系统字体"""
        # 获取系统默认字体
        default_font = tkFont.nametofont("TkDefaultFont")
        # Windows安全字体列表
        safe_fonts = [
            "Microsoft YaHei UI",  # 微软雅黑 UI
            "Microsoft YaHei",  # 微软雅黑
            "SimHei",  # 黑体
            "SimSun",  # 宋体
            "TkDefaultFont",  # Tk默认字体
            "Arial",  # 英文字体
            "Helvetica"  # 英文字体
        ]
        # 尝试设置安全字体
        selected_font = "TkDefaultFont"  # 默认字体
        for font_name in safe_fonts:
            try:
                # 测试字体是否可用
                test_font = tkFont.Font(family=font_name, size=10)
                test_font.actual()  # 如果字体不存在会抛出异常
                # 设置Tk默认字体
                default_font.config(family=font_name, size=10)
                selected_font = font_name
                print(f"使用字体: {font_name}")
                break
            except tk.TclError:
                continue
        # 设置样式使用安全字体
        style = ttk.Style()
        style.configure('TFrame', background='#f0f0f0')
        style.configure('TLabel', background='#f0f0f0', font=(selected_font, 10))
        style.configure('Title.TLabel', font=(selected_font, 16, 'bold'))
        style.configure('TButton', font=(selected_font, 10))
        style.configure('TLabelframe', font=(selected_font, 10))
        style.configure('TLabelframe.Label', font=(selected_font, 10))
        return selected_font
    def setup_ui(self):
        self.root.title("系统监控服务")
        self.root.geometry("650x600")  # 增加窗口尺寸以适应新布局
        self.root.resizable(True, True)
        self.root.minsize(600, 550)  # 设置最小窗口大小
        # 设置应用程序图标 - 使用新的方法
        self.set_window_icon()
        # 设置安全字体
        selected_font = self.set_safe_fonts()
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        # 标题
        title_label = ttk.Label(main_frame, text="系统监控服务", style='Title.TLabel')
        title_label.pack(pady=(0, 20))
        # 网络接口选择
        interface_frame = ttk.LabelFrame(main_frame, text="网络接口选择", padding="10")
        interface_frame.pack(fill=tk.X, pady=10)
        # 网络接口选择内部框架
        interface_inner_frame = ttk.Frame(interface_frame)
        interface_inner_frame.pack(fill=tk.X, expand=True)
        ttk.Label(interface_inner_frame, text="选择服务绑定的网卡:").pack(side=tk.LEFT, padx=(0, 10))
        # 获取网络接口
        self.interfaces = self.monitor.get_network_interfaces()
        self.interface_var = tk.StringVar()
        # 创建下拉菜单
        self.interface_combo = ttk.Combobox(
            interface_inner_frame,
            textvariable=self.interface_var,
            values=list(self.interfaces.keys()),
            state="readonly",
            width=30
        )
        self.interface_combo.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
        # 刷新接口按钮
        refresh_btn = ttk.Button(
            interface_inner_frame,
            text="刷新接口",
            command=self.refresh_interfaces
        )
        refresh_btn.pack(side=tk.RIGHT)
        # 服务状态
        status_frame = ttk.Frame(main_frame)
        status_frame.pack(fill=tk.X, pady=10)
        ttk.Label(status_frame, text="服务状态:").pack(side=tk.LEFT)
        self.status_var = tk.StringVar(value="未启动")
        self.status_label = ttk.Label(status_frame, textvariable=self.status_var, foreground="red")
        self.status_label.pack(side=tk.LEFT, padx=(10, 0))
        # 控制按钮框架 - 使用网格布局实现居中
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=10)
        # 创建内部框架用于居中按钮
        button_inner_frame = ttk.Frame(button_frame)
        button_inner_frame.pack(expand=True)
        self.start_button = ttk.Button(
            button_inner_frame,
            text="启动监控服务",
            command=self.start_service
        )
        self.start_button.grid(row=0, column=0, padx=5, pady=5, sticky="ew")
        self.open_browser_button = ttk.Button(
            button_inner_frame,
            text="通过浏览器打开查看",
            command=self.open_browser,
            state='disabled'
        )
        self.open_browser_button.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        self.stop_button = ttk.Button(
            button_inner_frame,
            text="停止服务",
            command=self.stop_service,
            state='disabled'
        )
        self.stop_button.grid(row=0, column=2, padx=5, pady=5, sticky="ew")
        # 配置列权重使按钮均匀分布
        button_inner_frame.columnconfigure(0, weight=1)
        button_inner_frame.columnconfigure(1, weight=1)
        button_inner_frame.columnconfigure(2, weight=1)
        # API地址按钮框架 - 使用网格布局实现居中
        api_button_frame = ttk.Frame(main_frame)
        api_button_frame.pack(fill=tk.X, pady=10)
        # 创建内部框架用于居中按钮
        api_inner_frame = ttk.Frame(api_button_frame)
        api_inner_frame.pack(expand=True)
        self.copy_text_button = ttk.Button(
            api_inner_frame,
            text="复制纯文本状态地址",
            command=self.copy_text_status_url,
            state='disabled'
        )
        self.copy_text_button.grid(row=0, column=0, padx=5, pady=5, sticky="ew")
        self.copy_json_button = ttk.Button(
            api_inner_frame,
            text="复制JSON API接口地址",
            command=self.copy_json_api_url,
            state='disabled'
        )
        self.copy_json_button.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        # 配置列权重使按钮均匀分布
        api_inner_frame.columnconfigure(0, weight=1)
        api_inner_frame.columnconfigure(1, weight=1)
        # 实时监控信息
        info_frame = ttk.LabelFrame(main_frame, text="实时系统信息", padding="10")
        info_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        # 设置Text控件的字体
        self.info_text = tk.Text(info_frame, height=15, width=60, font=(selected_font, 9))
        scrollbar = ttk.Scrollbar(info_frame, orient=tk.VERTICAL, command=self.info_text.yview)
        self.info_text.configure(yscrollcommand=scrollbar.set)
        self.info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        # 绑定接口选择事件
        self.interface_combo.bind('>', self.on_interface_selected)
        # 绑定窗口大小变化事件
        self.root.bind('', self.on_window_resize)
        # 更新实时信息
        self.update_info()
    def on_window_resize(self, event):
        """窗口大小变化时的回调函数"""
        # 确保只处理主窗口的大小变化
        if event.widget == self.root:
            # 更新按钮布局
            self.update_button_layout()
    def update_button_layout(self):
        """更新按钮布局以适应窗口大小"""
        # 这个方法会在窗口大小变化时被调用
        # 可以在这里添加额外的布局调整逻辑
        pass
    def refresh_interfaces(self):
        """刷新网络接口列表"""
        self.interfaces = self.monitor.get_network_interfaces()
        current_value = self.interface_var.get()
        self.interface_combo['values'] = list(self.interfaces.keys())
        # 如果当前值仍然存在,保持选择;否则选择默认值
        if current_value in self.interfaces:
            self.interface_combo.set(current_value)
        elif '所有接口 (0.0.0.0)' in self.interfaces:
            self.interface_combo.set('所有接口 (0.0.0.0)')
            self.monitor.set_interface('0.0.0.0')
        messagebox.showinfo("成功", "网络接口列表已刷新")
    def on_interface_selected(self, event):
        """当选择网络接口时的回调函数"""
        selected_key = self.interface_var.get()
        if selected_key in self.interfaces:
            ip_address = self.interfaces[selected_key]
            self.monitor.set_interface(ip_address)
            print(f"已选择网络接口: {selected_key} -> {ip_address}")
    def copy_text_status_url(self):
        """复制纯文本状态请求地址到剪贴板"""
        try:
            selected_key = self.interface_var.get()
            if selected_key in self.interfaces:
                host = self.interfaces[selected_key]
                port = 5000
                if host == '0.0.0.0':
                    host = 'localhost'
                url = f"http://{host}:{port}/status"
                self.root.clipboard_clear()
                self.root.clipboard_append(url)
                self.root.update()  # 现在剪贴板内容将保持可用
                messagebox.showinfo("复制成功", f"已复制纯文本状态地址到剪贴板:\n{url}")
        except Exception as e:
            messagebox.showerror("错误", f"复制地址失败: {str(e)}")
    def copy_json_api_url(self):
        """复制JSON API接口地址到剪贴板"""
        try:
            selected_key = self.interface_var.get()
            if selected_key in self.interfaces:
                host = self.interfaces[selected_key]
                port = 5000
                if host == '0.0.0.0':
                    host = 'localhost'
                url = f"http://{host}:{port}/api/json"
                self.root.clipboard_clear()
                self.root.clipboard_append(url)
                self.root.update()  # 现在剪贴板内容将保持可用
                messagebox.showinfo("复制成功", f"已复制JSON API接口地址到剪贴板:\n{url}")
        except Exception as e:
            messagebox.showerror("错误", f"复制地址失败: {str(e)}")
    def start_service(self):
        """启动监控服务"""
        try:
            # 获取选择的接口
            selected_key = self.interface_var.get()
            if selected_key not in self.interfaces:
                messagebox.showerror("错误", "请选择有效的网络接口")
                return
            host = self.interfaces[selected_key]
            port = 5000
            # 在新线程中启动服务器
            self.server_thread = threading.Thread(
                target=self.monitor.start_server,
                args=(host, port),
                daemon=True
            )
            self.server_thread.start()
            # 更新UI状态
            self.status_var.set("运行中")
            self.status_label.configure(foreground="green")
            self.start_button.configure(state='disabled')
            self.open_browser_button.configure(state='normal')
            self.stop_button.configure(state='normal')
            self.copy_text_button.configure(state='normal')  # 启用复制按钮
            self.copy_json_button.configure(state='normal')  # 启用复制按钮
            self.interface_combo.configure(state='disabled')
            if host == '0.0.0.0':
                # 获取所有IP地址用于显示
                all_ips = []
                for iface, ip in self.interfaces.items():
                    if ip not in ['0.0.0.0', '127.0.0.1']:
                        all_ips.append(f"http://{ip}:{port}")
                if all_ips:
                    access_urls = "\n".join(all_ips)
                    messagebox.showinfo("成功", f"监控服务已启动\n服务绑定到所有接口\n访问地址:\n{access_urls}")
                else:
                    messagebox.showinfo("成功",
                                        f"监控服务已启动\n服务绑定到所有接口\n访问地址: http://localhost:{port}")
            else:
                messagebox.showinfo("成功", f"监控服务已启动\n访问地址: http://{host}:{port}")
        except Exception as e:
            messagebox.showerror("错误", f"启动服务失败: {str(e)}")
    def stop_service(self):
        """停止监控服务"""
        # 由于Flask开发服务器没有内置停止方法,我们只能通过标记来停止
        self.monitor.server_running = False
        self.status_var.set("未启动")
        self.status_label.configure(foreground="red")
        self.start_button.configure(state='normal')
        self.open_browser_button.configure(state='disabled')
        self.stop_button.configure(state='disabled')
        self.copy_text_button.configure(state='disabled')  # 禁用复制按钮
        self.copy_json_button.configure(state='disabled')  # 禁用复制按钮
        self.interface_combo.configure(state='readonly')
        messagebox.showinfo("成功", "监控服务已停止")
    def open_browser(self):
        """在浏览器中打开监控页面"""
        try:
            selected_key = self.interface_var.get()
            if selected_key in self.interfaces:
                host = self.interfaces[selected_key]
                port = 5000
                if host == '0.0.0.0':
                    # 如果绑定到所有接口,默认打开localhost
                    webbrowser.open(f"http://localhost:{port}")
                else:
                    webbrowser.open(f"http://{host}:{port}")
        except Exception as e:
            messagebox.showerror("错误", f"打开浏览器失败: {str(e)}")
    def update_info(self):
        """更新实时监控信息"""
        try:
            info = self.monitor.get_system_info()
            if 'error' not in info:
                text_info = self.monitor.get_text_status()
                self.info_text.delete(1.0, tk.END)
                self.info_text.insert(1.0, text_info)
        except:
            pass
        # 每2秒更新一次
        self.root.after(2000, self.update_info)
def main():
    root = tk.Tk()
    app = MonitorApp(root)
    root.mainloop()
if __name__ == "__main__":
    main()
文件下载地址:
https://lgkymj.lanzouq.com/imeeB37srhud
密码:52pj

接口, 地址

templarcc   

感谢楼主的分享~
您需要登录后才可以回帖 登录 | 立即注册

返回顶部