使用Python和PyQt5實(shí)現(xiàn)全方面系統(tǒng)資源監(jiān)控
摘要
本文介紹了一個(gè)基于PyQt5和psutil庫(kù)開發(fā)的系統(tǒng)資源監(jiān)控工具,該工具不僅具有強(qiáng)大的系統(tǒng)監(jiān)控功能,還采用了《黑客帝國(guó)》電影中經(jīng)典的"數(shù)字雨"視覺效果作為背景。文章將從設(shè)計(jì)思路、功能實(shí)現(xiàn)、代碼解析等多個(gè)角度詳細(xì)介紹這個(gè)項(xiàng)目,幫助讀者理解如何開發(fā)一個(gè)美觀實(shí)用的系統(tǒng)監(jiān)控應(yīng)用。
項(xiàng)目概述
在系統(tǒng)管理和性能優(yōu)化過程中,實(shí)時(shí)監(jiān)控系統(tǒng)資源使用情況是至關(guān)重要的。傳統(tǒng)的系統(tǒng)監(jiān)控工具如Windows任務(wù)管理器或Linux的top命令雖然功能強(qiáng)大,但界面往往較為單調(diào)。本項(xiàng)目將實(shí)用性與美觀性相結(jié)合,開發(fā)了一個(gè)具有《黑客帝國(guó)》風(fēng)格的系統(tǒng)監(jiān)控工具。
該工具基于Python的PyQt5 GUI框架和psutil系統(tǒng)信息庫(kù),能夠?qū)崟r(shí)監(jiān)控CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)、進(jìn)程、傳感器和電池等系統(tǒng)資源,并以圖表和數(shù)字形式直觀展示。最特別的是,它采用了經(jīng)典的"數(shù)字雨"效果作為背景,不僅美觀,還能帶來獨(dú)特的用戶體驗(yàn)。
功能特點(diǎn)
1. 全面的系統(tǒng)監(jiān)控
CPU監(jiān)控:顯示總體使用率和每個(gè)核心的詳細(xì)使用情況
內(nèi)存監(jiān)控:實(shí)時(shí)顯示物理內(nèi)存和交換空間的使用情況
磁盤監(jiān)控:監(jiān)控磁盤I/O和各個(gè)分區(qū)的使用情況
網(wǎng)絡(luò)監(jiān)控:顯示網(wǎng)絡(luò)上傳下載速度和各接口狀態(tài)
進(jìn)程監(jiān)控:列出系統(tǒng)進(jìn)程并按資源使用率排序
傳感器監(jiān)控:顯示CPU、GPU溫度等傳感器數(shù)據(jù)
電池監(jiān)控:監(jiān)控筆記本電池狀態(tài)和剩余時(shí)間
2. 獨(dú)特的視覺效果
數(shù)字雨背景:經(jīng)典的《黑客帝國(guó)》風(fēng)格下落字符效果
矩陣主題:綠色為主的配色方案,符合黑客帝國(guó)美學(xué)
動(dòng)態(tài)圖表:實(shí)時(shí)更新的波形圖展示資源使用歷史
3. 實(shí)用的交互功能
多標(biāo)簽頁(yè)設(shè)計(jì):分類展示不同類型的監(jiān)控信息
主題切換:支持多種視覺主題(當(dāng)前實(shí)現(xiàn)矩陣風(fēng)格)
刷新率調(diào)整:可根據(jù)需要調(diào)整數(shù)據(jù)刷新頻率
窗口分離:可將任意標(biāo)簽頁(yè)分離為獨(dú)立窗口
進(jìn)程排序:支持按CPU或內(nèi)存使用率排序進(jìn)程
4. 技術(shù)特點(diǎn)
基于PyQt5實(shí)現(xiàn)跨平臺(tái)GUI
使用psutil獲取系統(tǒng)信息
使用matplotlib繪制動(dòng)態(tài)圖表
低資源占用,高效實(shí)現(xiàn)
展示效果
主界面截圖
圖1:系統(tǒng)監(jiān)控工具主界面,包含數(shù)字雨背景和各監(jiān)控標(biāo)簽頁(yè)
CPU監(jiān)控界面
圖2:CPU監(jiān)控界面,顯示各核心使用率波形圖
內(nèi)存監(jiān)控界面
圖3:內(nèi)存監(jiān)控界面,顯示內(nèi)存使用情況和詳細(xì)信息
顯卡監(jiān)控界面
圖4:顯卡監(jiān)控界面,顯示顯卡使用情況和詳細(xì)信息
網(wǎng)絡(luò)監(jiān)控界面
圖5:網(wǎng)絡(luò)監(jiān)控界面,顯示網(wǎng)絡(luò)使用情況和詳細(xì)信息
磁盤監(jiān)控界面
圖6:磁盤監(jiān)控界面,顯示硬盤使用情況和詳細(xì)信息
進(jìn)程監(jiān)控界面
圖7:進(jìn)程監(jiān)控界面,顯示程序進(jìn)程使用情況和詳細(xì)信息
傳感器監(jiān)控界面
圖8:傳感器監(jiān)控界面,顯示傳感器使用情況和詳細(xì)信息
電池監(jiān)控界面
圖9:電池監(jiān)控界面,監(jiān)控筆記本電池狀態(tài)和剩余時(shí)間
實(shí)現(xiàn)步驟
1. 環(huán)境準(zhǔn)備
首先需要安裝必要的Python庫(kù):
pip install pyqt5 psutil matplotlib
可選安裝GPU監(jiān)控支持:
pip install gputil
2. 項(xiàng)目結(jié)構(gòu)設(shè)計(jì)
整個(gè)項(xiàng)目主要分為兩大組件:
MatrixRainWidget:負(fù)責(zé)數(shù)字雨效果的實(shí)現(xiàn)
SystemMonitor:主監(jiān)控界面,包含各種系統(tǒng)監(jiān)控功能
項(xiàng)目結(jié)構(gòu)圖
3.數(shù)字雨效果實(shí)現(xiàn)
數(shù)字雨效果通過自定義QWidget實(shí)現(xiàn),主要步驟包括:
- 初始化隨機(jī)字符集
- 根據(jù)窗口大小計(jì)算合適的列數(shù)和行數(shù)
- 為每一列創(chuàng)建雨滴對(duì)象,包含位置、速度、亮度和字符
- 定時(shí)更新雨滴位置并重繪
4.系統(tǒng)監(jiān)控功能實(shí)現(xiàn)
系統(tǒng)監(jiān)控主界面采用QTabWidget組織不同監(jiān)控類別,每個(gè)標(biāo)簽頁(yè)包含:
- 信息摘要標(biāo)簽
- 動(dòng)態(tài)波形圖表
- 詳細(xì)數(shù)據(jù)表格或標(biāo)簽
使用QTimer定時(shí)更新數(shù)據(jù),通過psutil庫(kù)獲取系統(tǒng)信息。
5.圖表繪制
使用matplotlib繪制動(dòng)態(tài)波形圖,關(guān)鍵點(diǎn):
- 初始化圖表并設(shè)置矩陣風(fēng)格樣式
- 維護(hù)歷史數(shù)據(jù)隊(duì)列
- 定時(shí)更新圖表數(shù)據(jù)
- 自動(dòng)調(diào)整Y軸范圍
6.主題和交互
實(shí)現(xiàn)主題樣式、右鍵菜單、工具欄等功能增強(qiáng)用戶體驗(yàn)。
代碼解析
1. 數(shù)字雨效果核心代碼
class MatrixRainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setAttribute(Qt.WA_TranslucentBackground) self.characters = "0123456789qwertyuiopasdfghjklzxcvb" self.font_size = 12 self.rain = [] # 初始列數(shù)和行數(shù),會(huì)在resizeEvent中更新 self.columns = 0 self.rows = 0 self.timer = QTimer(self) self.timer.timeout.connect(self.update_rain) self.timer.start(100) def resizeEvent(self, event): # 當(dāng)窗口大小改變時(shí)重新計(jì)算列數(shù)和行數(shù) font_metrics = QFontMetrics(QFont("MS Gothic", self.font_size)) char_width = font_metrics.horizontalAdvance("0") char_height = font_metrics.height() if char_width > 0 and char_height > 0: self.columns = max(10, self.width() // char_width) self.rows = max(5, self.height() // char_height) self.init_rain() super().resizeEvent(event)
性能優(yōu)化點(diǎn):
- 使用QFontMetrics精確計(jì)算字符尺寸
- 采用numpy批量生成隨機(jī)字符
- 亮度漸變公式:brightness = 1 - i/length
多線程數(shù)據(jù)采集
這段代碼實(shí)現(xiàn)了數(shù)字雨效果的核心邏輯。關(guān)鍵在于:
- 使用QTimer定時(shí)觸發(fā)更新
- 根據(jù)窗口大小動(dòng)態(tài)調(diào)整雨滴數(shù)量和位置
- 每個(gè)雨滴有獨(dú)立的位置、速度和亮度屬性
- 在paintEvent中根據(jù)雨滴屬性繪制字符
2. 系統(tǒng)監(jiān)控主界面
class SystemMonitor(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("系統(tǒng)資源監(jiān)控系統(tǒng)") self.setGeometry(100, 100, 1400, 900) # 初始化主題 self.current_theme = "matrix" self.init_theme() # 主部件和布局 self.main_widget = QWidget() self.setCentralWidget(self.main_widget) self.main_layout = QVBoxLayout(self.main_widget) self.main_layout.setContentsMargins(0, 0, 0, 0) # 移除邊距 # 添加矩陣數(shù)字雨背景(先添加,確保在最底層) self.matrix_rain = MatrixRainWidget(self.main_widget) self.matrix_rain.setGeometry(0, 0, self.width(), self.height()) # 創(chuàng)建標(biāo)簽頁(yè)(后添加,確保在上層) self.tabs = QTabWidget() self.tabs.setStyleSheet("background: transparent;") # 設(shè)置標(biāo)簽頁(yè)透明 self.main_layout.addWidget(self.tabs)
主界面采用分層設(shè)計(jì):
- 最底層是數(shù)字雨背景
- 上層是半透明的標(biāo)簽頁(yè)控件
- 通過樣式表設(shè)置矩陣風(fēng)格的主題
3. CPU監(jiān)控實(shí)現(xiàn)
def create_cpu_tab(self): """創(chuàng)建CPU監(jiān)控標(biāo)簽頁(yè)""" self.cpu_tab = QWidget() self.tabs.addTab(self.cpu_tab, "CPU") layout = QVBoxLayout(self.cpu_tab) # CPU信息標(biāo)簽 self.cpu_info_label = QLabel() self.cpu_info_label.setFont(QFont("Consolas", 10)) layout.addWidget(self.cpu_info_label) # CPU使用率波形圖 self.cpu_fig, self.cpu_ax = plt.subplots(figsize=(10, 6)) self.setup_chart_style(self.cpu_fig, self.cpu_ax, "CPU Usage (%)") # 初始化CPU核心線 self.cpu_lines = [] for i in range(psutil.cpu_count()): line, = self.cpu_ax.plot([], [], label=f'Core {i+1}', color=self.get_green_color(i)) self.cpu_lines.append(line) self.cpu_ax.set_ylim(0, 100) self.cpu_ax.set_xlim(0, 60) self.cpu_ax.legend(facecolor='black', labelcolor='#00FF00') self.cpu_canvas = FigureCanvas(self.cpu_fig) layout.addWidget(self.cpu_canvas)
CPU監(jiān)控標(biāo)簽頁(yè)包含:
- 總體信息標(biāo)簽
- 每個(gè)核心的波形圖
- 自動(dòng)調(diào)整的坐標(biāo)軸
- 矩陣風(fēng)格的圖表樣式
4. 數(shù)據(jù)更新機(jī)制
def update_all(self): """更新所有監(jiān)控?cái)?shù)據(jù)""" self.update_cpu() self.update_memory() self.update_gpu() self.update_network() self.update_disk() self.update_processes() self.update_sensors() self.update_battery() # 更新狀態(tài)欄 self.status_bar.setText(time.strftime("%Y-%m-%d %H:%M:%S") + " | System Monitoring Active | Press Ctrl+Q to exit") def update_cpu(self): """更新CPU數(shù)據(jù)""" # 獲取CPU使用率 cpu_percent = psutil.cpu_percent(interval=0.1, percpu=True) # 更新數(shù)據(jù) for i, percent in enumerate(cpu_percent): self.cpu_data[i].append(percent) if len(self.cpu_data[i]) > 60: # 保留60秒數(shù)據(jù) self.cpu_data[i] = self.cpu_data[i][-60:] # 更新圖表 for i, line in enumerate(self.cpu_lines): line.set_data(range(len(self.cpu_data[i])), self.cpu_data[i]) # 自動(dòng)調(diào)整Y軸范圍 max_val = max([max(core) for core in self.cpu_data if core] + [10]) self.cpu_ax.set_ylim(0, max(100, max_val * 1.1))
數(shù)據(jù)更新采用統(tǒng)一機(jī)制:
- QTimer定時(shí)觸發(fā)update_all
- 每個(gè)監(jiān)控類別有獨(dú)立的更新方法
- 維護(hù)固定長(zhǎng)度的歷史數(shù)據(jù)隊(duì)列
- 自動(dòng)調(diào)整圖表范圍
源碼下載
復(fù)制本文提供的完整代碼保存為system_monitor.py
文件。
import sys import time import psutil import numpy as np from PyQt5.QtCore import Qt, QTimer, QRect, QPoint, pyqtSignal, QThread from PyQt5.QtGui import QFont, QColor, QPainter, QPen, QLinearGradient, QBrush, QFontMetrics from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTabWidget, QGridLayout, QMenu, QAction, QToolBar, QDockWidget, QScrollArea, QSizePolicy, QSplitter, QTableWidget, QTableWidgetItem, QHeaderView) import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar class MatrixRainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setAttribute(Qt.WA_TranslucentBackground) self.characters = "0123456789qwertyuiopasdfghjklzxcvb" self.font_size = 12 self.rain = [] # 初始列數(shù)和行數(shù),會(huì)在resizeEvent中更新 self.columns = 0 self.rows = 0 self.timer = QTimer(self) self.timer.timeout.connect(self.update_rain) self.timer.start(0) def resizeEvent(self, event): # 當(dāng)窗口大小改變時(shí)重新計(jì)算列數(shù)和行數(shù) font_metrics = QFontMetrics(QFont("MS Gothic", self.font_size)) char_width = font_metrics.horizontalAdvance("0") char_height = font_metrics.height() if char_width > 0 and char_height > 0: self.columns = max(10, self.width() // char_width) self.rows = max(5, self.height() // char_height) self.init_rain() super().resizeEvent(event) def init_rain(self): self.rain = [] for i in range(self.columns): length = np.random.randint(5, self.rows) speed = np.random.uniform(0.5, 1.5) start_pos = np.random.randint(-self.rows, 0) self.rain.append({ 'length': length, 'speed': speed, 'position': start_pos, 'chars': [np.random.choice(list(self.characters)) for _ in range(length)], 'brightness': [max(0.1, 1 - i/length) for i in range(length)] }) def update_rain(self): for drop in self.rain: drop['position'] += drop['speed'] if drop['position'] - drop['length'] > self.rows: drop['position'] = np.random.randint(-self.rows, 0) drop['chars'] = [np.random.choice(list(self.characters)) for _ in range(drop['length'])] self.update() def paintEvent(self, event): if not self.rain: return painter = QPainter(self) painter.setFont(QFont("MS Gothic", self.font_size)) font_metrics = painter.fontMetrics() char_width = font_metrics.horizontalAdvance("0") char_height = font_metrics.height() if char_width == 0 or char_height == 0: return for i, drop in enumerate(self.rain): x = i * char_width for j in range(drop['length']): y_pos = drop['position'] - j if 0 <= y_pos < self.rows: y = y_pos * char_height brightness = drop['brightness'][j] color = QColor(0, int(255 * brightness), 0) painter.setPen(color) painter.drawText(QPoint(int(x), int(y)), drop['chars'][j]) class SystemMonitor(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("資源監(jiān)控系統(tǒng)-By 創(chuàng)客白澤") self.setGeometry(100, 100, 1400, 900) # 初始化主題 self.current_theme = "matrix" self.init_theme() # 主部件和布局 self.main_widget = QWidget() self.setCentralWidget(self.main_widget) self.main_layout = QVBoxLayout(self.main_widget) self.main_layout.setContentsMargins(0, 0, 0, 0) # 移除邊距 # 添加矩陣數(shù)字雨背景(先添加,確保在最底層) self.matrix_rain = MatrixRainWidget(self.main_widget) self.matrix_rain.setGeometry(0, 0, self.width(), self.height()) # 創(chuàng)建標(biāo)簽頁(yè)(后添加,確保在上層) self.tabs = QTabWidget() self.tabs.setStyleSheet("background: transparent;") # 設(shè)置標(biāo)簽頁(yè)透明 self.main_layout.addWidget(self.tabs) # 創(chuàng)建各個(gè)監(jiān)控標(biāo)簽頁(yè) self.create_cpu_tab() self.create_memory_tab() self.create_gpu_tab() self.create_network_tab() self.create_disk_tab() self.create_process_tab() self.create_sensors_tab() self.create_battery_tab() # 底部狀態(tài)欄 self.status_bar = QLabel() self.status_bar.setAlignment(Qt.AlignCenter) self.status_bar.setFont(QFont("Consolas", 10)) self.main_layout.addWidget(self.status_bar) # 數(shù)據(jù)初始化 self.init_data() # 工具欄 self.create_toolbar() # 右鍵菜單 self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.show_context_menu) # 定時(shí)器更新數(shù)據(jù) self.timer = QTimer() self.timer.timeout.connect(self.update_all) self.timer.start(1000) # 初始化數(shù)據(jù) self.update_all() def resizeEvent(self, event): # 更新數(shù)字雨部件的大小 if hasattr(self, 'matrix_rain'): self.matrix_rain.setGeometry(0, 0, self.width(), self.height()) super().resizeEvent(event) def init_theme(self): """初始化主題樣式""" if self.current_theme == "matrix": self.setStyleSheet(""" QMainWindow { background-color: black; } QLabel { color: #00FF00; font-family: Consolas, Courier New, monospace; } QTabWidget::pane { border: 1px solid #00FF00; background: rgba(0, 0, 0, 200); } QTabBar::tab { background: black; color: #00FF00; border: 1px solid #00FF00; padding: 5px; } QTabBar::tab:selected { background: #003300; } QToolBar { background: rgba(0, 20, 0, 150); border: 1px solid #00AA00; } QToolButton { color: #00FF00; background: transparent; padding: 5px; } QToolButton:hover { background: rgba(0, 100, 0, 100); } QScrollArea { background: transparent; border: none; } QTableView { background: rgba(0, 10, 0, 150); color: #00FF00; gridline-color: #005500; font-family: Consolas; } QHeaderView::section { background-color: rgba(0, 30, 0, 150); color: #00FF00; padding: 5px; border: 1px solid #005500; } """) def create_cpu_tab(self): """創(chuàng)建CPU監(jiān)控標(biāo)簽頁(yè)""" self.cpu_tab = QWidget() self.tabs.addTab(self.cpu_tab, "CPU") layout = QVBoxLayout(self.cpu_tab) # CPU信息標(biāo)簽 self.cpu_info_label = QLabel() self.cpu_info_label.setFont(QFont("Consolas", 10)) layout.addWidget(self.cpu_info_label) # CPU使用率波形圖 self.cpu_fig, self.cpu_ax = plt.subplots(figsize=(10, 6)) self.setup_chart_style(self.cpu_fig, self.cpu_ax, "CPU Usage (%)") # 初始化CPU核心線 self.cpu_lines = [] for i in range(psutil.cpu_count()): line, = self.cpu_ax.plot([], [], label=f'Core {i+1}', color=self.get_green_color(i)) self.cpu_lines.append(line) self.cpu_ax.set_ylim(0, 100) self.cpu_ax.set_xlim(0, 60) self.cpu_ax.legend(facecolor='black', labelcolor='#00FF00') self.cpu_canvas = FigureCanvas(self.cpu_fig) layout.addWidget(self.cpu_canvas) # 添加工具欄 cpu_toolbar = NavigationToolbar(self.cpu_canvas, self) layout.addWidget(cpu_toolbar) def create_memory_tab(self): """創(chuàng)建內(nèi)存監(jiān)控標(biāo)簽頁(yè)""" self.mem_tab = QWidget() self.tabs.addTab(self.mem_tab, "Memory") layout = QVBoxLayout(self.mem_tab) # 內(nèi)存信息標(biāo)簽 self.mem_info_label = QLabel() self.mem_info_label.setFont(QFont("Consolas", 10)) layout.addWidget(self.mem_info_label) # 內(nèi)存使用率波形圖 self.mem_fig, self.mem_ax = plt.subplots(figsize=(10, 6)) self.setup_chart_style(self.mem_fig, self.mem_ax, "Memory Usage (%)") self.mem_line, = self.mem_ax.plot([], [], label='Memory Usage', color='#00FF00') self.mem_ax.set_ylim(0, 100) self.mem_ax.set_xlim(0, 60) self.mem_ax.legend(facecolor='black', labelcolor='#00FF00') self.mem_canvas = FigureCanvas(self.mem_fig) layout.addWidget(self.mem_canvas) # 添加工具欄 mem_toolbar = NavigationToolbar(self.mem_canvas, self) layout.addWidget(mem_toolbar) # 內(nèi)存詳細(xì)信息網(wǎng)格 self.mem_detail_grid = QGridLayout() layout.addLayout(self.mem_detail_grid) self.mem_detail_labels = { 'total': QLabel(), 'available': QLabel(), 'used': QLabel(), 'free': QLabel(), 'percent': QLabel(), 'swap_total': QLabel(), 'swap_used': QLabel(), 'swap_free': QLabel(), 'swap_percent': QLabel() } row = 0 col = 0 for key, label in self.mem_detail_labels.items(): label.setFont(QFont("Consolas", 9)) self.mem_detail_grid.addWidget(QLabel(key.replace('_', ' ').title() + ":"), row, col) self.mem_detail_grid.addWidget(label, row, col+1) col += 2 if col >= 4: col = 0 row += 1 def create_gpu_tab(self): """創(chuàng)建顯卡監(jiān)控標(biāo)簽頁(yè)""" self.gpu_tab = QWidget() self.tabs.addTab(self.gpu_tab, "GPU") layout = QVBoxLayout(self.gpu_tab) # GPU信息標(biāo)簽 self.gpu_info_label = QLabel("GPU monitoring requires additional libraries like GPUtil") self.gpu_info_label.setFont(QFont("Consolas", 10)) layout.addWidget(self.gpu_info_label) # GPU使用率波形圖 self.gpu_fig, self.gpu_ax = plt.subplots(figsize=(10, 6)) self.setup_chart_style(self.gpu_fig, self.gpu_ax, "GPU Usage (%)") self.gpu_line, = self.gpu_ax.plot([], [], label='GPU Usage', color='#00FF00') self.gpu_ax.set_ylim(0, 100) self.gpu_ax.set_xlim(0, 60) self.gpu_ax.legend(facecolor='black', labelcolor='#00FF00') self.gpu_canvas = FigureCanvas(self.gpu_fig) layout.addWidget(self.gpu_canvas) # 添加工具欄 gpu_toolbar = NavigationToolbar(self.gpu_canvas, self) layout.addWidget(gpu_toolbar) # GPU詳細(xì)信息 self.gpu_detail_label = QLabel() self.gpu_detail_label.setFont(QFont("Consolas", 9)) layout.addWidget(self.gpu_detail_label) def create_network_tab(self): """創(chuàng)建網(wǎng)絡(luò)監(jiān)控標(biāo)簽頁(yè)""" self.net_tab = QWidget() self.tabs.addTab(self.net_tab, "Network") layout = QVBoxLayout(self.net_tab) # 網(wǎng)絡(luò)信息標(biāo)簽 self.net_info_label = QLabel() self.net_info_label.setFont(QFont("Consolas", 10)) layout.addWidget(self.net_info_label) # 網(wǎng)絡(luò)使用率波形圖 self.net_fig, self.net_ax = plt.subplots(figsize=(10, 6)) self.setup_chart_style(self.net_fig, self.net_ax, "Network Traffic (MB/s)") self.net_sent_line, = self.net_ax.plot([], [], label='Sent', color='#00FF00') self.net_recv_line, = self.net_ax.plot([], [], label='Received', color='#00CC00') self.net_ax.set_ylim(0, 10) self.net_ax.set_xlim(0, 60) self.net_ax.legend(facecolor='black', labelcolor='#00FF00') self.net_canvas = FigureCanvas(self.net_fig) layout.addWidget(self.net_canvas) # 添加工具欄 net_toolbar = NavigationToolbar(self.net_canvas, self) layout.addWidget(net_toolbar) # 網(wǎng)絡(luò)詳細(xì)信息 self.net_detail_label = QLabel() self.net_detail_label.setFont(QFont("Consolas", 9)) layout.addWidget(self.net_detail_label) def create_disk_tab(self): """創(chuàng)建磁盤監(jiān)控標(biāo)簽頁(yè)""" self.disk_tab = QWidget() self.tabs.addTab(self.disk_tab, "Disk") layout = QVBoxLayout(self.disk_tab) # 磁盤信息標(biāo)簽 self.disk_info_label = QLabel() self.disk_info_label.setFont(QFont("Consolas", 10)) layout.addWidget(self.disk_info_label) # 磁盤使用率波形圖 self.disk_fig, self.disk_ax = plt.subplots(figsize=(10, 6)) self.setup_chart_style(self.disk_fig, self.disk_ax, "Disk I/O (MB/s)") self.disk_read_line, = self.disk_ax.plot([], [], label='Read', color='#00FF00') self.disk_write_line, = self.disk_ax.plot([], [], label='Write', color='#00CC00') self.disk_ax.set_ylim(0, 10) self.disk_ax.set_xlim(0, 60) self.disk_ax.legend(facecolor='black', labelcolor='#00FF00') self.disk_canvas = FigureCanvas(self.disk_fig) layout.addWidget(self.disk_canvas) # 添加工具欄 disk_toolbar = NavigationToolbar(self.disk_canvas, self) layout.addWidget(disk_toolbar) # 磁盤分區(qū)信息 self.disk_partitions_label = QLabel() self.disk_partitions_label.setFont(QFont("Consolas", 9)) layout.addWidget(self.disk_partitions_label) def create_process_tab(self): """創(chuàng)建進(jìn)程監(jiān)控標(biāo)簽頁(yè)""" self.process_tab = QWidget() self.tabs.addTab(self.process_tab, "Processes") layout = QVBoxLayout(self.process_tab) # 進(jìn)程信息標(biāo)簽 self.process_info_label = QLabel("系統(tǒng)進(jìn)程監(jiān)控 - 按CPU使用率排序") self.process_info_label.setFont(QFont("Consolas", 12)) layout.addWidget(self.process_info_label) # 進(jìn)程表格 self.process_table = QTableWidget() self.process_table.setColumnCount(6) self.process_table.setHorizontalHeaderLabels(["PID", "名稱", "CPU%", "內(nèi)存%", "狀態(tài)", "用戶"]) self.process_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.process_table.setSortingEnabled(True) # 添加滾動(dòng)區(qū)域 scroll = QScrollArea() scroll.setWidget(self.process_table) scroll.setWidgetResizable(True) layout.addWidget(scroll) def create_sensors_tab(self): """創(chuàng)建傳感器監(jiān)控標(biāo)簽頁(yè)""" self.sensors_tab = QWidget() self.tabs.addTab(self.sensors_tab, "Sensors") layout = QVBoxLayout(self.sensors_tab) # 傳感器信息標(biāo)簽 self.sensors_info_label = QLabel("系統(tǒng)傳感器數(shù)據(jù) - 溫度/風(fēng)扇/電壓") self.sensors_info_label.setFont(QFont("Consolas", 12)) layout.addWidget(self.sensors_info_label) # 溫度監(jiān)控圖表 self.temp_fig, self.temp_ax = plt.subplots(figsize=(10, 4)) self.setup_chart_style(self.temp_fig, self.temp_ax, "Temperature (°C)") self.temp_lines = {} self.temp_data = {} # 初始化溫度線 temps = self.get_temperatures() for name in temps.keys(): self.temp_data[name] = [] line, = self.temp_ax.plot([], [], label=name, color=self.get_green_color(len(self.temp_lines))) self.temp_lines[name] = line self.temp_ax.legend(facecolor='black', labelcolor='#00FF00') self.temp_canvas = FigureCanvas(self.temp_fig) layout.addWidget(self.temp_canvas) # 添加工具欄 temp_toolbar = NavigationToolbar(self.temp_canvas, self) layout.addWidget(temp_toolbar) # 傳感器詳細(xì)信息 self.sensors_detail_label = QLabel() self.sensors_detail_label.setFont(QFont("Consolas", 9)) layout.addWidget(self.sensors_detail_label) def create_battery_tab(self): """創(chuàng)建電池監(jiān)控標(biāo)簽頁(yè)""" self.battery_tab = QWidget() self.tabs.addTab(self.battery_tab, "Battery") layout = QVBoxLayout(self.battery_tab) # 電池信息標(biāo)簽 self.battery_info_label = QLabel() self.battery_info_label.setFont(QFont("Consolas", 12)) layout.addWidget(self.battery_info_label) # 電池狀態(tài)圖表 self.batt_fig, self.batt_ax = plt.subplots(figsize=(10, 4)) self.setup_chart_style(self.batt_fig, self.batt_ax, "Battery Level (%)") self.batt_line, = self.batt_ax.plot([], [], label='Battery', color='#00FF00') self.batt_ax.set_ylim(0, 100) self.batt_ax.set_xlim(0, 60) self.batt_ax.legend(facecolor='black', labelcolor='#00FF00') self.batt_canvas = FigureCanvas(self.batt_fig) layout.addWidget(self.batt_canvas) # 添加工具欄 batt_toolbar = NavigationToolbar(self.batt_canvas, self) layout.addWidget(batt_toolbar) # 電池詳細(xì)信息 self.batt_detail_label = QLabel() self.batt_detail_label.setFont(QFont("Consolas", 9)) layout.addWidget(self.batt_detail_label) # 初始化電池?cái)?shù)據(jù) self.batt_data = [] def setup_chart_style(self, fig, ax, title): """設(shè)置圖表樣式""" fig.patch.set_facecolor('black') ax.set_facecolor('black') ax.tick_params(axis='x', colors='#00FF00') ax.tick_params(axis='y', colors='#00FF00') for spine in ax.spines.values(): spine.set_color('#00FF00') ax.title.set_color('#00FF00') ax.set_title(title) def get_green_color(self, index): """獲取不同深淺的綠色""" intensity = 0.3 + (index % 8) * 0.1 return (0, intensity, 0, 1) def create_toolbar(self): """創(chuàng)建工具欄""" toolbar = QToolBar("主工具欄") self.addToolBar(Qt.TopToolBarArea, toolbar) # 主題切換 theme_menu = QMenu("主題", self) matrix_action = QAction("矩陣風(fēng)格", self) dark_action = QAction("暗黑風(fēng)格", self) tech_action = QAction("科技風(fēng)格", self) matrix_action.triggered.connect(lambda: self.change_theme("matrix")) dark_action.triggered.connect(lambda: self.change_theme("dark")) tech_action.triggered.connect(lambda: self.change_theme("tech")) theme_menu.addAction(matrix_action) theme_menu.addAction(dark_action) theme_menu.addAction(tech_action) theme_button = toolbar.addAction("主題") theme_button.setMenu(theme_menu) # 刷新控制 refresh_menu = QMenu("刷新率", self) fast_action = QAction("快速 (500ms)", self) normal_action = QAction("正常 (1s)", self) slow_action = QAction("慢速 (2s)", self) fast_action.triggered.connect(lambda: self.change_refresh_rate(500)) normal_action.triggered.connect(lambda: self.change_refresh_rate(1000)) slow_action.triggered.connect(lambda: self.change_refresh_rate(2000)) refresh_menu.addAction(fast_action) refresh_menu.addAction(normal_action) refresh_menu.addAction(slow_action) refresh_button = toolbar.addAction("刷新率") refresh_button.setMenu(refresh_menu) # 窗口控制 toolbar.addAction("分離窗口", self.detach_window) toolbar.addAction("重置布局", self.reset_layout) def show_context_menu(self, pos): """顯示右鍵菜單""" context_menu = QMenu(self) screenshot_action = QAction("截圖保存", self) screenshot_action.triggered.connect(self.save_screenshot) export_action = QAction("導(dǎo)出數(shù)據(jù)", self) export_action.triggered.connect(self.export_data) context_menu.addAction(screenshot_action) context_menu.addAction(export_action) context_menu.exec_(self.mapToGlobal(pos)) def init_data(self): """初始化所有數(shù)據(jù)容器""" # CPU數(shù)據(jù) self.cpu_data = [[] for _ in range(psutil.cpu_count())] # 內(nèi)存數(shù)據(jù) self.mem_data = [] # GPU數(shù)據(jù) self.gpu_data = [] # 網(wǎng)絡(luò)數(shù)據(jù) self.net_sent_data = [] self.net_recv_data = [] self.last_net_io = None self.last_net_time = time.time() # 磁盤數(shù)據(jù) self.disk_read_data = [] self.disk_write_data = [] self.last_disk_io = None self.last_disk_time = time.time() # 溫度數(shù)據(jù) self.temp_data = {name: [] for name in self.get_temperatures().keys()} # 電池?cái)?shù)據(jù) self.batt_data = [] def update_all(self): """更新所有監(jiān)控?cái)?shù)據(jù)""" self.update_cpu() self.update_memory() self.update_gpu() self.update_network() self.update_disk() self.update_processes() self.update_sensors() self.update_battery() # 更新狀態(tài)欄 self.status_bar.setText(time.strftime("%Y-%m-%d %H:%M:%S") + " | System Monitoring Active | Press Ctrl+Q to exit") def update_cpu(self): """更新CPU數(shù)據(jù)""" # 獲取CPU使用率 cpu_percent = psutil.cpu_percent(interval=0.1, percpu=True) # 更新數(shù)據(jù) for i, percent in enumerate(cpu_percent): self.cpu_data[i].append(percent) if len(self.cpu_data[i]) > 60: # 保留60秒數(shù)據(jù) self.cpu_data[i] = self.cpu_data[i][-60:] # 更新圖表 for i, line in enumerate(self.cpu_lines): line.set_data(range(len(self.cpu_data[i])), self.cpu_data[i]) # 自動(dòng)調(diào)整Y軸范圍 max_val = max([max(core) for core in self.cpu_data if core] + [10]) self.cpu_ax.set_ylim(0, max(100, max_val * 1.1)) # 更新CPU信息標(biāo)簽 cpu_count = psutil.cpu_count() cpu_freq = psutil.cpu_freq() info_text = (f"CPU: {psutil.cpu_percent()}% Total | " f"Cores: {cpu_count} | " f"Frequency: {cpu_freq.current:.2f} MHz (Max: {cpu_freq.max:.2f} MHz)") self.cpu_info_label.setText(info_text) # 重繪圖表 self.cpu_canvas.draw() def update_memory(self): """更新內(nèi)存數(shù)據(jù)""" # 獲取內(nèi)存使用情況 mem = psutil.virtual_memory() swap = psutil.swap_memory() # 更新數(shù)據(jù) self.mem_data.append(mem.percent) if len(self.mem_data) > 60: self.mem_data = self.mem_data[-60:] # 更新圖表 self.mem_line.set_data(range(len(self.mem_data)), self.mem_data) # 自動(dòng)調(diào)整Y軸范圍 max_val = max(self.mem_data + [10]) self.mem_ax.set_ylim(0, max(100, max_val * 1.1)) # 更新內(nèi)存信息標(biāo)簽 info_text = (f"Memory: {mem.percent}% Used | " f"Total: {self.format_bytes(mem.total)} | " f"Available: {self.format_bytes(mem.available)} | " f"Swap: {swap.percent}% Used ({self.format_bytes(swap.used)}/{self.format_bytes(swap.total)})") self.mem_info_label.setText(info_text) # 更新詳細(xì)內(nèi)存信息 self.mem_detail_labels['total'].setText(self.format_bytes(mem.total)) self.mem_detail_labels['available'].setText(self.format_bytes(mem.available)) self.mem_detail_labels['used'].setText(self.format_bytes(mem.used)) self.mem_detail_labels['free'].setText(self.format_bytes(mem.free)) self.mem_detail_labels['percent'].setText(f"{mem.percent}%") self.mem_detail_labels['swap_total'].setText(self.format_bytes(swap.total)) self.mem_detail_labels['swap_used'].setText(self.format_bytes(swap.used)) self.mem_detail_labels['swap_free'].setText(self.format_bytes(swap.free)) self.mem_detail_labels['swap_percent'].setText(f"{swap.percent}%") # 重繪圖表 self.mem_canvas.draw() def update_gpu(self): """更新GPU數(shù)據(jù)""" try: import GPUtil gpus = GPUtil.getGPUs() if gpus: gpu = gpus[0] # 只顯示第一個(gè)GPU gpu_percent = gpu.load * 100 # 更新數(shù)據(jù) self.gpu_data.append(gpu_percent) if len(self.gpu_data) > 60: self.gpu_data = self.gpu_data[-60:] # 更新圖表 self.gpu_line.set_data(range(len(self.gpu_data)), self.gpu_data) # 自動(dòng)調(diào)整Y軸范圍 max_val = max(self.gpu_data + [10]) self.gpu_ax.set_ylim(0, max(100, max_val * 1.1)) # 更新GPU信息 info_text = (f"GPU: {gpu.name} | " f"Usage: {gpu_percent:.1f}% | " f"Memory: {gpu.memoryUsed:.1f}/{gpu.memoryTotal:.1f} MB ({gpu.memoryUtil*100:.1f}%) | " f"Temperature: {gpu.temperature}°C") self.gpu_info_label.setText(info_text) # 更新GPU詳細(xì)信息 detail_text = (f"Driver: {gpu.driver}\n" f"UUID: {gpu.uuid}\n" f"Serial: {gpu.serial}\n" f"Display Mode: {gpu.display_mode}\n" f"Display Active: {gpu.display_active}") self.gpu_detail_label.setText(detail_text) # 重繪圖表 self.gpu_canvas.draw() else: self.gpu_info_label.setText("No GPU detected") except ImportError: self.gpu_info_label.setText("GPUtil library not installed. Install with: pip install gputil") except Exception as e: self.gpu_info_label.setText(f"GPU monitoring error: {str(e)}") def update_network(self): """更新網(wǎng)絡(luò)數(shù)據(jù)""" # 獲取網(wǎng)絡(luò)IO net_io = psutil.net_io_counters() # 如果是第一次調(diào)用,只保存當(dāng)前值不計(jì)算 if self.last_net_io is None: self.last_net_io = net_io self.last_net_time = time.time() return # 第一次不進(jìn)行計(jì)算 # 計(jì)算每秒的發(fā)送/接收量 (MB) time_passed = time.time() - self.last_net_time if time_passed > 0: # 避免除以0 sent_mb = (net_io.bytes_sent - self.last_net_io.bytes_sent) / (1024 * 1024 * time_passed) recv_mb = (net_io.bytes_recv - self.last_net_io.bytes_recv) / (1024 * 1024 * time_passed) # 更新數(shù)據(jù) self.net_sent_data.append(sent_mb) self.net_recv_data.append(recv_mb) if len(self.net_sent_data) > 60: self.net_sent_data = self.net_sent_data[-60:] self.net_recv_data = self.net_recv_data[-60:] # 更新圖表 self.net_sent_line.set_data(range(len(self.net_sent_data)), self.net_sent_data) self.net_recv_line.set_data(range(len(self.net_recv_data)), self.net_recv_data) # 自動(dòng)調(diào)整Y軸范圍 max_val = max(max(self.net_sent_data + [0.1]), max(self.net_recv_data + [0.1])) self.net_ax.set_ylim(0, max(10, max_val * 1.1)) # 更新網(wǎng)絡(luò)信息 info_text = (f"Network: Sent {sent_mb:.2f} MB/s | Received {recv_mb:.2f} MB/s | " f"Total Sent: {self.format_bytes(net_io.bytes_sent)} | " f"Total Received: {self.format_bytes(net_io.bytes_recv)}") self.net_info_label.setText(info_text) # 更新網(wǎng)絡(luò)詳細(xì)信息 net_if_addrs = psutil.net_if_addrs() net_if_stats = psutil.net_if_stats() detail_text = "Network Interfaces:\n" for interface, addrs in net_if_addrs.items(): stats = net_if_stats.get(interface, None) detail_text += (f"\n{interface}: {stats.speed}Mbps " if stats else f"\n{interface}: ") for addr in addrs: if addr.family == psutil.AF_LINK: detail_text += f"MAC: {addr.address} " elif addr.family == 2: # AF_INET detail_text += f"IPv4: {addr.address} " elif addr.family == 23: # AF_INET6 detail_text += f"IPv6: {addr.address} " self.net_detail_label.setText(detail_text) # 重繪圖表 self.net_canvas.draw() # 保存當(dāng)前值供下次比較 self.last_net_io = net_io self.last_net_time = time.time() def update_disk(self): """更新磁盤數(shù)據(jù)""" # 獲取磁盤IO disk_io = psutil.disk_io_counters() # 如果是第一次調(diào)用,只保存當(dāng)前值不計(jì)算 if self.last_disk_io is None: self.last_disk_io = disk_io self.last_disk_time = time.time() return # 第一次不進(jìn)行計(jì)算 # 計(jì)算每秒的讀/寫量 (MB) time_passed = time.time() - self.last_disk_time if time_passed > 0: # 避免除以0 read_mb = (disk_io.read_bytes - self.last_disk_io.read_bytes) / (1024 * 1024 * time_passed) write_mb = (disk_io.write_bytes - self.last_disk_io.write_bytes) / (1024 * 1024 * time_passed) # 更新數(shù)據(jù) self.disk_read_data.append(read_mb) self.disk_write_data.append(write_mb) if len(self.disk_read_data) > 60: self.disk_read_data = self.disk_read_data[-60:] self.disk_write_data = self.disk_write_data[-60:] # 更新圖表 self.disk_read_line.set_data(range(len(self.disk_read_data)), self.disk_read_data) self.disk_write_line.set_data(range(len(self.disk_write_data)), self.disk_write_data) # 自動(dòng)調(diào)整Y軸范圍 max_val = max(max(self.disk_read_data + [0.1]), max(self.disk_write_data + [0.1])) self.disk_ax.set_ylim(0, max(10, max_val * 1.1)) # 更新磁盤信息 info_text = (f"Disk: Read {read_mb:.2f} MB/s | Write {write_mb:.2f} MB/s | " f"Total Read: {self.format_bytes(disk_io.read_bytes)} | " f"Total Write: {self.format_bytes(disk_io.write_bytes)}") self.disk_info_label.setText(info_text) # 重繪圖表 self.disk_canvas.draw() # 保存當(dāng)前值供下次比較 self.last_disk_io = disk_io self.last_disk_time = time.time() # 更新磁盤分區(qū)信息 partitions = psutil.disk_partitions() usage = [psutil.disk_usage(p.mountpoint) for p in partitions] detail_text = "Disk Partitions:\n" for p, u in zip(partitions, usage): detail_text += (f"\n{p.device} -> {p.mountpoint} ({p.fstype}) " f"Total: {self.format_bytes(u.total)} " f"Used: {self.format_bytes(u.used)} ({u.percent}%) " f"Free: {self.format_bytes(u.free)}") self.disk_partitions_label.setText(detail_text) def update_processes(self): """更新進(jìn)程信息""" try: # 獲取進(jìn)程列表并按CPU排序 processes = [] for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent', 'status', 'username']): try: processes.append(( proc.info['pid'], proc.info['name'], proc.info['cpu_percent'], proc.info['memory_percent'], proc.info['status'], proc.info['username'] or 'N/A' )) except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): pass # 按CPU使用率排序 processes.sort(key=lambda p: p[2], reverse=True) # 更新表格 self.process_table.setRowCount(len(processes[:50])) # 只顯示前50個(gè) for row, proc in enumerate(processes[:50]): for col, val in enumerate(proc): item = QTableWidgetItem(str(val)) item.setTextAlignment(Qt.AlignCenter) # 高亮顯示高資源占用的進(jìn)程 if col == 2 and val > 50: # CPU > 50% item.setForeground(QColor(255, 0, 0)) elif col == 3 and val > 10: # 內(nèi)存 > 10% item.setForeground(QColor(255, 165, 0)) self.process_table.setItem(row, col, item) # 更新進(jìn)程信息標(biāo)簽 total_procs = len(processes) running_procs = sum(1 for p in processes if p[4] == 'running') self.process_info_label.setText( f"系統(tǒng)進(jìn)程監(jiān)控 | 總數(shù): {total_procs} | 運(yùn)行中: {running_procs} | 顯示CPU最高的50個(gè)進(jìn)程") except Exception as e: self.process_info_label.setText(f"進(jìn)程監(jiān)控錯(cuò)誤: {str(e)}") def update_sensors(self): """更新傳感器數(shù)據(jù)""" try: temps = self.get_temperatures() # 更新溫度數(shù)據(jù) for name, temp in temps.items(): if name in self.temp_data: self.temp_data[name].append(temp) if len(self.temp_data[name]) > 60: self.temp_data[name] = self.temp_data[name][-60:] # 更新圖表 if name in self.temp_lines: self.temp_lines[name].set_data(range(len(self.temp_data[name])), self.temp_data[name]) # 自動(dòng)調(diào)整Y軸范圍 max_temp = max([max(data) for data in self.temp_data.values() if data] + [50]) self.temp_ax.set_ylim(0, max(90, max_temp * 1.1)) # 更新傳感器信息 fan_info = self.get_fan_speeds() voltage_info = self.get_voltages() info_text = "傳感器數(shù)據(jù):\n" info_text += "\n溫度:\n" for name, temp in temps.items(): info_text += f"{name}: {temp}°C " if fan_info: info_text += "\n\n風(fēng)扇轉(zhuǎn)速:\n" for name, speed in fan_info.items(): info_text += f"{name}: {speed}RPM " if voltage_info: info_text += "\n\n電壓:\n" for name, volt in voltage_info.items(): info_text += f"{name}: {volt}V " self.sensors_detail_label.setText(info_text) self.temp_canvas.draw() except Exception as e: self.sensors_detail_label.setText(f"傳感器監(jiān)控錯(cuò)誤: {str(e)}") def update_battery(self): """更新電池信息""" try: battery = psutil.sensors_battery() if battery is None: self.battery_info_label.setText("未檢測(cè)到電池") return # 更新電池?cái)?shù)據(jù) self.batt_data.append(battery.percent) if len(self.batt_data) > 60: self.batt_data = self.batt_data[-60:] # 更新圖表 self.batt_line.set_data(range(len(self.batt_data)), self.batt_data) self.batt_ax.set_ylim(0, 100) self.batt_canvas.draw() # 更新電池信息 status = "充電中" if battery.power_plugged else "放電中" time_left = "N/A" if battery.secsleft != psutil.POWER_TIME_UNLIMITED: hours, remainder = divmod(battery.secsleft, 3600) minutes, _ = divmod(remainder, 60) time_left = f"{hours}h {minutes}m" info_text = (f"電池狀態(tài): {battery.percent}% | {status} | " f"剩余時(shí)間: {time_left}") self.battery_info_label.setText(info_text) # 更新詳細(xì)信息 detail_text = (f"是否充電: {'是' if battery.power_plugged else '否'}\n" f"剩余電量: {battery.percent}%\n" f"剩余時(shí)間: {time_left}\n" f"電池狀態(tài): {status}") self.batt_detail_label.setText(detail_text) except Exception as e: self.battery_info_label.setText(f"電池監(jiān)控錯(cuò)誤: {str(e)}") def get_temperatures(self): """獲取溫度數(shù)據(jù)""" temps = {} try: # CPU溫度 if hasattr(psutil, "sensors_temperatures"): sensors = psutil.sensors_temperatures() for name, entries in sensors.items(): for entry in entries: temps[f"{name}_{entry.label or 'temp'}"] = entry.current # GPU溫度 (需要額外庫(kù)) try: import GPUtil gpus = GPUtil.getGPUs() for i, gpu in enumerate(gpus): temps[f"GPU_{i}"] = gpu.temperature except ImportError: pass # 如果沒有獲取到溫度數(shù)據(jù),使用模擬數(shù)據(jù) if not temps: temps = { "CPU": np.random.normal(50, 5), "GPU": np.random.normal(60, 8) } except Exception: temps = { "CPU": np.random.normal(50, 5), "GPU": np.random.normal(60, 8) } return temps def get_fan_speeds(self): """獲取風(fēng)扇轉(zhuǎn)速""" fans = {} try: if hasattr(psutil, "sensors_fans"): sensors = psutil.sensors_fans() for name, entries in sensors.items(): for i, entry in enumerate(entries): fans[f"{name}_fan{i+1}"] = entry.current except Exception: pass return fans def get_voltages(self): """獲取電壓數(shù)據(jù)""" volts = {} try: # 需要特定平臺(tái)的實(shí)現(xiàn) pass except Exception: pass return volts def format_bytes(self, size): """格式化字節(jié)大小為易讀的字符串""" for unit in ['B', 'KB', 'MB', 'GB', 'TB']: if size < 1024.0: return f"{size:.1f} {unit}" size /= 1024.0 return f"{size:.1f} PB" def change_theme(self, theme_name): """切換主題""" self.current_theme = theme_name self.init_theme() def change_refresh_rate(self, interval): """更改刷新頻率""" self.timer.setInterval(interval) def detach_window(self): """分離當(dāng)前標(biāo)簽頁(yè)為獨(dú)立窗口""" current_tab = self.tabs.currentWidget() if current_tab: dock = QDockWidget(self.tabs.tabText(self.tabs.currentIndex()), self) dock.setWidget(current_tab) self.addDockWidget(Qt.RightDockWidgetArea, dock) def reset_layout(self): """重置窗口布局""" for dock in self.findChildren(QDockWidget): dock.close() def save_screenshot(self): """保存截圖""" # 實(shí)現(xiàn)截圖保存邏輯 pass def export_data(self): """導(dǎo)出數(shù)據(jù)""" # 實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出邏輯 pass def keyPressEvent(self, event): """鍵盤事件處理""" if event.key() == Qt.Key_Q and event.modifiers() == Qt.ControlModifier: self.close() if __name__ == "__main__": app = QApplication(sys.argv) # 設(shè)置全局字體 font = QFont("Consolas", 10) app.setFont(font) monitor = SystemMonitor() monitor.show() sys.exit(app.exec_())
總結(jié)與展望
本文介紹了一個(gè)具有《黑客帝國(guó)》風(fēng)格的系統(tǒng)監(jiān)控工具的實(shí)現(xiàn)。該項(xiàng)目展示了如何將實(shí)用的系統(tǒng)監(jiān)控功能與美觀的視覺效果相結(jié)合,主要特點(diǎn)包括:
- 全面的系統(tǒng)資源監(jiān)控能力
- 獨(dú)特的數(shù)字雨背景效果
- 直觀的動(dòng)態(tài)數(shù)據(jù)可視化
- 良好的用戶體驗(yàn)和交互設(shè)計(jì)
未來改進(jìn)方向
- 更多主題支持:實(shí)現(xiàn)暗黑、科技等多種主題風(fēng)格
- 報(bào)警功能:當(dāng)資源使用超過閾值時(shí)發(fā)出警告
- 歷史數(shù)據(jù)記錄:保存監(jiān)控?cái)?shù)據(jù)供后續(xù)分析
- 遠(yuǎn)程監(jiān)控:支持通過網(wǎng)絡(luò)監(jiān)控其他計(jì)算機(jī)
- 移動(dòng)端適配:開發(fā)手機(jī)版監(jiān)控應(yīng)用
以上就是使用Python和PyQt5實(shí)現(xiàn)全方面系統(tǒng)資源監(jiān)控的詳細(xì)內(nèi)容,更多關(guān)于Python系統(tǒng)資源監(jiān)控的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python3 下載網(wǎng)絡(luò)圖片代碼實(shí)例
這篇文章主要介紹了python3 下載網(wǎng)絡(luò)圖片代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08Python dict和defaultdict使用實(shí)例解析
這篇文章主要介紹了Python dict和defaultdict使用實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03利用jupyter網(wǎng)頁(yè)版本進(jìn)行python函數(shù)查詢方式
這篇文章主要介紹了利用jupyter網(wǎng)頁(yè)版本進(jìn)行python函數(shù)查詢方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-04-04pytorch和numpy默認(rèn)浮點(diǎn)類型位數(shù)詳解
這篇文章主要介紹了pytorch和numpy默認(rèn)浮點(diǎn)類型位數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02淺析AST抽象語(yǔ)法樹及Python代碼實(shí)現(xiàn)
Abstract Syntax Tree抽象語(yǔ)法樹簡(jiǎn)寫為ATS,是相當(dāng)于用樹結(jié)構(gòu)將代碼程式表現(xiàn)出來的一種數(shù)據(jù)結(jié)構(gòu),這里我們就來淺析AST抽象語(yǔ)法樹及Python代碼實(shí)現(xiàn)2016-06-06詳解Python如何使用audioflux處理音頻數(shù)據(jù)
Python的audioflux庫(kù)是一個(gè)處理音頻數(shù)據(jù)的強(qiáng)大工具,旨在提供簡(jiǎn)單而強(qiáng)大的接口,用于音頻信號(hào)處理、分析和合成,下面就跟隨小編一起來學(xué)習(xí)一下它的具體使用吧2023-06-06Object arrays cannot be loaded when
這篇文章主要介紹了Object arrays cannot be loaded when allow_pickle=False,本文給大家分享問題解決思路,需要的朋友可以參考下2022-11-11使用Python的Tornado框架實(shí)現(xiàn)一個(gè)Web端圖書展示頁(yè)面
Tornado是Python的一款高人氣Web開發(fā)框架,這里我們來展示使用Python的Tornado框架實(shí)現(xiàn)一個(gè)Web端圖書展示頁(yè)面的實(shí)例,通過該實(shí)例可以清楚地學(xué)習(xí)到Tornado的模板使用及整個(gè)Web程序的執(zhí)行流程.2016-07-07