吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 324|回复: 0
收起左侧

[Python 原创] 基于Python写的Mac系统用的极简系统信息状态栏

[复制链接]
liangzaihuigui 发表于 2026-3-26 10:18
效果如图,极简风格,没有多余功能,
【测试环境】硬件:macmini m4 。系统:macos 26.3

【所需依赖库】
1. psutil - 版本 7.2.2
   - 用途:跨平台的进程和系统监控库
   - 安装命令: pip3 install psutil
2. pyobjc - 版本 12.1
   - 用途:Python和Objective-C之间的互操作性模块,用于macOS开发
   - 安装命令: pip3 install pyobjc
[Python] 纯文本查看 复制代码
pip3 install psutil --break-system-packages
pip3 install pyobjc --break-system-packages



【完整代码】
[Python] 纯文本查看 复制代码
"""
macOS 菜单栏系统监控工具(轻量版)
只显示核心信息,低内存占用
"""

import sys
import time
import psutil
import objc
import os
from AppKit import (
    NSApplication, NSStatusBar, NSMenu, NSMenuItem, NSRunningApplication,
    NSApplicationActivationPolicyRegular, NSApplicationActivationPolicyAccessory,
    NSApplicationActivationPolicyProhibited
)
from Foundation import NSObject, NSTimer

def format_speed(speed_bytes):
    """格式化网络速度为 KB/s 或 MB/s"""
    speed_kb = speed_bytes / 1024
    if speed_kb < 10:
        return f"{speed_kb:.1f} KB/s"
    if speed_kb < 1000:
        return f"{speed_kb:.0f} KB/s"
    return f"{speed_kb / 1024:.1f} MB/s"

def get_network_speed(last_net_io):
    """获取当前网络速度(KB/s)"""
    net_io = psutil.net_io_counters()
    if last_net_io:
        sent_speed = net_io.bytes_sent - last_net_io[0]
        recv_speed = net_io.bytes_recv - last_net_io[1]
    else:
        sent_speed = 0
        recv_speed = 0
    return sent_speed, recv_speed, (net_io.bytes_sent, net_io.bytes_recv)

def get_cpu_usage():
    """获取CPU使用率"""
    return psutil.cpu_percent()

def get_memory_usage():
    """获取内存使用率"""
    mem = psutil.virtual_memory()
    return mem.percent

class StatusBarAppDelegate(NSObject):
    def init(self):
        self = objc.super(StatusBarAppDelegate, self).init()
        if self is None:
            return None
        
        self.last_net_io = None
        self.update_interval = 1
        self.show_dock_icon = False  # 默认隐藏Dock栏图标
        # 默认全部显示
        self.show_cpu = True
        self.show_ram = True
        self.show_upload = True
        self.show_download = True
        self.config_file = os.path.expanduser("~/Library/Preferences/com.systemmonitor.plist")
        
        # 尝试读取配置
        self.load_config()
        
        # 设置应用程序激活策略(隐藏Dock图标)
        self.set_activation_policy()
        
        self.status_bar = NSStatusBar.systemStatusBar()
        self.status_item = self.status_bar.statusItemWithLength_(-1)
        self.status_item.setHighlightMode_(True)
        
        self.menu = NSMenu.alloc().init()
        self.status_item.setMenu_(self.menu)
        
        self.update_timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
            1, self, 'updateData:', None, True
        )
        
        self.create_menu()
        self.updateData_(None)
        
        return self
    
    def load_config(self):
        """加载用户配置"""
        try:
            if os.path.exists(self.config_file):
                import plistlib
                with open(self.config_file, 'rb') as f:
                    config = plistlib.load(f)
                self.show_dock_icon = config.get('show_dock_icon', False)
                # 加载显示选项配置
                self.show_cpu = config.get('show_cpu', True)
                self.show_ram = config.get('show_ram', True)
                self.show_upload = config.get('show_upload', True)
                self.show_download = config.get('show_download', True)
        except Exception as e:
            print(f"Error loading config: {e}")
            self.show_dock_icon = False
            # 默认全部显示
            self.show_cpu = True
            self.show_ram = True
            self.show_upload = True
            self.show_download = True
    
    def save_config(self):
        """保存用户配置"""
        try:
            import plistlib
            config = {
                'show_dock_icon': self.show_dock_icon,
                'show_cpu': self.show_cpu,
                'show_ram': self.show_ram,
                'show_upload': self.show_upload,
                'show_download': self.show_download
            }
            os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
            with open(self.config_file, 'wb') as f:
                plistlib.dump(config, f)
        except Exception as e:
            print(f"Error saving config: {e}")
    
    def set_activation_policy(self):
        """设置应用程序激活策略"""
        if self.show_dock_icon:
            NSApplication.sharedApplication().setActivationPolicy_(NSApplicationActivationPolicyRegular)
        else:
            NSApplication.sharedApplication().setActivationPolicy_(NSApplicationActivationPolicyProhibited)
    
    def create_menu(self):
        """创建菜单栏"""
        refresh_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            "刷新", "refreshData:", ""
        )
        refresh_item.setTarget_(self)
        
        self.interval_menu = NSMenu.alloc().init()
        
        for interval in [1, 2, 5]:
            title = f"&#10003; {interval}秒" if interval == self.update_interval else f"{interval}秒"
            item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
                title, "setUpdateInterval:", ""
            )
            item.setTarget_(self)
            item.setRepresentedObject_(interval)
            self.interval_menu.addItem_(item)
        
        interval_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            "刷新间隔", "", ""
        )
        interval_item.setSubmenu_(self.interval_menu)
        
        # 添加显示/隐藏Dock栏图标选项
        dock_icon_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            "显示Dock栏图标" if not self.show_dock_icon else "隐藏Dock栏图标", 
            "toggleDockIcon:", 
            ""
        )
        dock_icon_item.setTarget_(self)
        
        # 添加显示选项
        show_cpu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            f"{'&#10003; ' if self.show_cpu else ''}显示CPU", "toggleShowCPU:", ""
        )
        show_cpu_item.setTarget_(self)
        
        show_ram_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            f"{'&#10003; ' if self.show_ram else ''}显示RAM", "toggleShowRAM:", ""
        )
        show_ram_item.setTarget_(self)
        
        show_upload_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            f"{'&#10003; ' if self.show_upload else ''}显示上传", "toggleShowUpload:", ""
        )
        show_upload_item.setTarget_(self)
        
        show_download_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            f"{'&#10003; ' if self.show_download else ''}显示下载", "toggleShowDownload:", ""
        )
        show_download_item.setTarget_(self)
        
        quit_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            "退出", "quitApp:", ""
        )
        quit_item.setTarget_(self)
        
        self.menu.addItem_(refresh_item)
        self.menu.addItem_(interval_item)
        self.menu.addItem_(dock_icon_item)
        self.menu.addItem_(NSMenuItem.separatorItem())
        self.menu.addItem_(show_cpu_item)
        self.menu.addItem_(show_ram_item)
        self.menu.addItem_(show_upload_item)
        self.menu.addItem_(show_download_item)
        self.menu.addItem_(NSMenuItem.separatorItem())
        self.menu.addItem_(quit_item)
    
    def toggleDockIcon_(self, sender):
        """切换Dock栏图标显示/隐藏"""
        # 保存当前状态
        old_show_dock_icon = self.show_dock_icon
        # 切换状态
        self.show_dock_icon = not self.show_dock_icon
        self.set_activation_policy()
        self.save_config()
        
        # 使用旧状态查找菜单项,使用新状态设置标题
        old_title = "显示Dock栏图标" if not old_show_dock_icon else "隐藏Dock栏图标"
        new_title = "显示Dock栏图标" if not self.show_dock_icon else "隐藏Dock栏图标"
        
        index = self.menu.indexOfItemWithTitle_(old_title)
        if index != -1:
            self.menu.itemAtIndex_(index).setTitle_(new_title)
    
    def updateData_(self, timer):
        """更新数据"""
        cpu = get_cpu_usage()
        mem = get_memory_usage()
        sent_speed, recv_speed, self.last_net_io = get_network_speed(self.last_net_io)
        
        cpu_str = f"{cpu:.0f}%"
        mem_str = f"{mem:.0f}%"
        up_str = format_speed(sent_speed).replace("KB/s", "K").replace("MB/s", "M")
        down_str = format_speed(recv_speed).replace("KB/s", "K").replace("MB/s", "M")
        
        # 根据显示选项构建状态栏文本
        status_parts = []
        if self.show_cpu:
            status_parts.append(f"C:{cpu_str}")
        if self.show_ram:
            status_parts.append(f"R:{mem_str}")
        if self.show_upload:
            status_parts.append(f"↑:{up_str}")
        if self.show_download:
            status_parts.append(f"↓:{down_str}")
        
        # 如果所有选项都被隐藏,显示默认文本
        if not status_parts:
            status_text = "系统监控"
        else:
            status_text = " ".join(status_parts)
        
        self.status_item.setTitle_(status_text)
    
    def refreshData_(self, sender):
        """刷新数据"""
        self.updateData_(None)
    
    def setUpdateInterval_(self, sender):
        """设置更新间隔"""
        new_interval = sender.representedObject()
        if self.update_interval != new_interval:
            self.update_interval = new_interval
            self.update_timer.invalidate()
            self.update_timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
                self.update_interval, self, 'updateData:', None, True
            )
            
            # 更新菜单项显示,添加&#10003;标记
            for i in range(self.interval_menu.numberOfItems()):
                item = self.interval_menu.itemAtIndex_(i)
                interval = item.representedObject()
                title = f"&#10003; {interval}秒" if interval == self.update_interval else f"{interval}秒"
                item.setTitle_(title)
    
    def toggleShowCPU_(self, sender):
        """切换CPU显示"""
        self.show_cpu = not self.show_cpu
        self.save_config()
        # 更新菜单项标题
        title = f"{'&#10003; ' if self.show_cpu else ''}显示CPU"
        index = self.menu.indexOfItemWithTitle_(f"{'&#10003; ' if not self.show_cpu else ''}显示CPU")
        if index != -1:
            self.menu.itemAtIndex_(index).setTitle_(title)
        # 更新状态栏
        self.updateData_(None)
    
    def toggleShowRAM_(self, sender):
        """切换RAM显示"""
        self.show_ram = not self.show_ram
        self.save_config()
        # 更新菜单项标题
        title = f"{'&#10003; ' if self.show_ram else ''}显示RAM"
        index = self.menu.indexOfItemWithTitle_(f"{'&#10003; ' if not self.show_ram else ''}显示RAM")
        if index != -1:
            self.menu.itemAtIndex_(index).setTitle_(title)
        # 更新状态栏
        self.updateData_(None)
    
    def toggleShowUpload_(self, sender):
        """切换上传显示"""
        self.show_upload = not self.show_upload
        self.save_config()
        # 更新菜单项标题
        title = f"{'&#10003; ' if self.show_upload else ''}显示上传"
        index = self.menu.indexOfItemWithTitle_(f"{'&#10003; ' if not self.show_upload else ''}显示上传")
        if index != -1:
            self.menu.itemAtIndex_(index).setTitle_(title)
        # 更新状态栏
        self.updateData_(None)
    
    def toggleShowDownload_(self, sender):
        """切换下载显示"""
        self.show_download = not self.show_download
        self.save_config()
        # 更新菜单项标题
        title = f"{'&#10003; ' if self.show_download else ''}显示下载"
        index = self.menu.indexOfItemWithTitle_(f"{'&#10003; ' if not self.show_download else ''}显示下载")
        if index != -1:
            self.menu.itemAtIndex_(index).setTitle_(title)
        # 更新状态栏
        self.updateData_(None)
    
    def quitApp_(self, sender):
        """退出应用"""
        self.update_timer.invalidate()
        NSApplication.sharedApplication().terminate_(self)

def main():
    app = NSApplication.sharedApplication()
    delegate = StatusBarAppDelegate.alloc().init()
    app.setDelegate_(delegate)
    
    try:
        app.run()
    except KeyboardInterrupt:
        app.terminate_(delegate)

if __name__ == "__main__":
    main()

xw_20260326101151.png

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-29 10:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表