使用Python和PyQt5實現(xiàn)全方面系統(tǒng)資源監(jiān)控
摘要
本文介紹了一個基于PyQt5和psutil庫開發(fā)的系統(tǒng)資源監(jiān)控工具,該工具不僅具有強大的系統(tǒng)監(jiān)控功能,還采用了《黑客帝國》電影中經(jīng)典的"數(shù)字雨"視覺效果作為背景。文章將從設計思路、功能實現(xiàn)、代碼解析等多個角度詳細介紹這個項目,幫助讀者理解如何開發(fā)一個美觀實用的系統(tǒng)監(jiān)控應用。
項目概述
在系統(tǒng)管理和性能優(yōu)化過程中,實時監(jiān)控系統(tǒng)資源使用情況是至關重要的。傳統(tǒng)的系統(tǒng)監(jiān)控工具如Windows任務管理器或Linux的top命令雖然功能強大,但界面往往較為單調(diào)。本項目將實用性與美觀性相結合,開發(fā)了一個具有《黑客帝國》風格的系統(tǒng)監(jiān)控工具。
該工具基于Python的PyQt5 GUI框架和psutil系統(tǒng)信息庫,能夠?qū)崟r監(jiān)控CPU、內(nèi)存、磁盤、網(wǎng)絡、進程、傳感器和電池等系統(tǒng)資源,并以圖表和數(shù)字形式直觀展示。最特別的是,它采用了經(jīng)典的"數(shù)字雨"效果作為背景,不僅美觀,還能帶來獨特的用戶體驗。
功能特點
1. 全面的系統(tǒng)監(jiān)控
CPU監(jiān)控:顯示總體使用率和每個核心的詳細使用情況
內(nèi)存監(jiān)控:實時顯示物理內(nèi)存和交換空間的使用情況
磁盤監(jiān)控:監(jiān)控磁盤I/O和各個分區(qū)的使用情況
網(wǎng)絡監(jiān)控:顯示網(wǎng)絡上傳下載速度和各接口狀態(tài)
進程監(jiān)控:列出系統(tǒng)進程并按資源使用率排序
傳感器監(jiān)控:顯示CPU、GPU溫度等傳感器數(shù)據(jù)
電池監(jiān)控:監(jiān)控筆記本電池狀態(tài)和剩余時間
2. 獨特的視覺效果
數(shù)字雨背景:經(jīng)典的《黑客帝國》風格下落字符效果
矩陣主題:綠色為主的配色方案,符合黑客帝國美學
動態(tài)圖表:實時更新的波形圖展示資源使用歷史
3. 實用的交互功能
多標簽頁設計:分類展示不同類型的監(jiān)控信息
主題切換:支持多種視覺主題(當前實現(xiàn)矩陣風格)
刷新率調(diào)整:可根據(jù)需要調(diào)整數(shù)據(jù)刷新頻率
窗口分離:可將任意標簽頁分離為獨立窗口
進程排序:支持按CPU或內(nèi)存使用率排序進程
4. 技術特點
基于PyQt5實現(xiàn)跨平臺GUI
使用psutil獲取系統(tǒng)信息
使用matplotlib繪制動態(tài)圖表
低資源占用,高效實現(xiàn)
展示效果
主界面截圖
圖1:系統(tǒng)監(jiān)控工具主界面,包含數(shù)字雨背景和各監(jiān)控標簽頁
CPU監(jiān)控界面
圖2:CPU監(jiān)控界面,顯示各核心使用率波形圖
內(nèi)存監(jiān)控界面
圖3:內(nèi)存監(jiān)控界面,顯示內(nèi)存使用情況和詳細信息
顯卡監(jiān)控界面
圖4:顯卡監(jiān)控界面,顯示顯卡使用情況和詳細信息
網(wǎng)絡監(jiān)控界面
圖5:網(wǎng)絡監(jiān)控界面,顯示網(wǎng)絡使用情況和詳細信息
磁盤監(jiān)控界面
圖6:磁盤監(jiān)控界面,顯示硬盤使用情況和詳細信息
進程監(jiān)控界面
圖7:進程監(jiān)控界面,顯示程序進程使用情況和詳細信息
傳感器監(jiān)控界面
圖8:傳感器監(jiān)控界面,顯示傳感器使用情況和詳細信息
電池監(jiān)控界面
圖9:電池監(jiān)控界面,監(jiān)控筆記本電池狀態(tài)和剩余時間
實現(xiàn)步驟
1. 環(huán)境準備
首先需要安裝必要的Python庫:
pip install pyqt5 psutil matplotlib
可選安裝GPU監(jiān)控支持:
pip install gputil
2. 項目結構設計
整個項目主要分為兩大組件:
MatrixRainWidget:負責數(shù)字雨效果的實現(xiàn)
SystemMonitor:主監(jiān)控界面,包含各種系統(tǒng)監(jiān)控功能
項目結構圖
3.數(shù)字雨效果實現(xiàn)
數(shù)字雨效果通過自定義QWidget實現(xiàn),主要步驟包括:
- 初始化隨機字符集
- 根據(jù)窗口大小計算合適的列數(shù)和行數(shù)
- 為每一列創(chuàng)建雨滴對象,包含位置、速度、亮度和字符
- 定時更新雨滴位置并重繪
4.系統(tǒng)監(jiān)控功能實現(xiàn)
系統(tǒng)監(jiān)控主界面采用QTabWidget組織不同監(jiān)控類別,每個標簽頁包含:
- 信息摘要標簽
- 動態(tài)波形圖表
- 詳細數(shù)據(jù)表格或標簽
使用QTimer定時更新數(shù)據(jù),通過psutil庫獲取系統(tǒng)信息。
5.圖表繪制
使用matplotlib繪制動態(tài)波形圖,關鍵點:
- 初始化圖表并設置矩陣風格樣式
- 維護歷史數(shù)據(jù)隊列
- 定時更新圖表數(shù)據(jù)
- 自動調(diào)整Y軸范圍
6.主題和交互
實現(xià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ù),會在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): # 當窗口大小改變時重新計算列數(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)化點:
- 使用QFontMetrics精確計算字符尺寸
- 采用numpy批量生成隨機字符
- 亮度漸變公式:brightness = 1 - i/length
多線程數(shù)據(jù)采集
這段代碼實現(xiàn)了數(shù)字雨效果的核心邏輯。關鍵在于:
- 使用QTimer定時觸發(fā)更新
- 根據(jù)窗口大小動態(tài)調(diào)整雨滴數(shù)量和位置
- 每個雨滴有獨立的位置、速度和亮度屬性
- 在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)建標簽頁(后添加,確保在上層) self.tabs = QTabWidget() self.tabs.setStyleSheet("background: transparent;") # 設置標簽頁透明 self.main_layout.addWidget(self.tabs)
主界面采用分層設計:
- 最底層是數(shù)字雨背景
- 上層是半透明的標簽頁控件
- 通過樣式表設置矩陣風格的主題
3. CPU監(jiān)控實現(xiàn)
def create_cpu_tab(self): """創(chuàng)建CPU監(jiān)控標簽頁""" self.cpu_tab = QWidget() self.tabs.addTab(self.cpu_tab, "CPU") layout = QVBoxLayout(self.cpu_tab) # CPU信息標簽 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)控標簽頁包含:
- 總體信息標簽
- 每個核心的波形圖
- 自動調(diào)整的坐標軸
- 矩陣風格的圖表樣式
4. 數(shù)據(jù)更新機制
def update_all(self): """更新所有監(jiān)控數(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]) # 自動調(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)一機制:
- QTimer定時觸發(fā)update_all
- 每個監(jiān)控類別有獨立的更新方法
- 維護固定長度的歷史數(shù)據(jù)隊列
- 自動調(diào)整圖表范圍
源碼下載
復制本文提供的完整代碼保存為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ù),會在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): # 當窗口大小改變時重新計算列數(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)建標簽頁(后添加,確保在上層) self.tabs = QTabWidget() self.tabs.setStyleSheet("background: transparent;") # 設置標簽頁透明 self.main_layout.addWidget(self.tabs) # 創(chuàng)建各個監(jiān)控標簽頁 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ù)據(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)控標簽頁""" self.cpu_tab = QWidget() self.tabs.addTab(self.cpu_tab, "CPU") layout = QVBoxLayout(self.cpu_tab) # CPU信息標簽 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)控標簽頁""" self.mem_tab = QWidget() self.tabs.addTab(self.mem_tab, "Memory") layout = QVBoxLayout(self.mem_tab) # 內(nèi)存信息標簽 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)存詳細信息網(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)控標簽頁""" self.gpu_tab = QWidget() self.tabs.addTab(self.gpu_tab, "GPU") layout = QVBoxLayout(self.gpu_tab) # GPU信息標簽 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詳細信息 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)絡監(jiān)控標簽頁""" self.net_tab = QWidget() self.tabs.addTab(self.net_tab, "Network") layout = QVBoxLayout(self.net_tab) # 網(wǎng)絡信息標簽 self.net_info_label = QLabel() self.net_info_label.setFont(QFont("Consolas", 10)) layout.addWidget(self.net_info_label) # 網(wǎng)絡使用率波形圖 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)絡詳細信息 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)控標簽頁""" self.disk_tab = QWidget() self.tabs.addTab(self.disk_tab, "Disk") layout = QVBoxLayout(self.disk_tab) # 磁盤信息標簽 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)建進程監(jiān)控標簽頁""" self.process_tab = QWidget() self.tabs.addTab(self.process_tab, "Processes") layout = QVBoxLayout(self.process_tab) # 進程信息標簽 self.process_info_label = QLabel("系統(tǒng)進程監(jiān)控 - 按CPU使用率排序") self.process_info_label.setFont(QFont("Consolas", 12)) layout.addWidget(self.process_info_label) # 進程表格 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) # 添加滾動區(qū)域 scroll = QScrollArea() scroll.setWidget(self.process_table) scroll.setWidgetResizable(True) layout.addWidget(scroll) def create_sensors_tab(self): """創(chuàng)建傳感器監(jiān)控標簽頁""" self.sensors_tab = QWidget() self.tabs.addTab(self.sensors_tab, "Sensors") layout = QVBoxLayout(self.sensors_tab) # 傳感器信息標簽 self.sensors_info_label = QLabel("系統(tǒng)傳感器數(shù)據(jù) - 溫度/風扇/電壓") 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) # 傳感器詳細信息 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)控標簽頁""" self.battery_tab = QWidget() self.tabs.addTab(self.battery_tab, "Battery") layout = QVBoxLayout(self.battery_tab) # 電池信息標簽 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) # 電池詳細信息 self.batt_detail_label = QLabel() self.batt_detail_label.setFont(QFont("Consolas", 9)) layout.addWidget(self.batt_detail_label) # 初始化電池數(shù)據(jù) self.batt_data = [] def setup_chart_style(self, fig, ax, title): """設置圖表樣式""" 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("矩陣風格", self) dark_action = QAction("暗黑風格", self) tech_action = QAction("科技風格", 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("導出數(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)絡數(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()} # 電池數(shù)據(jù) self.batt_data = [] def update_all(self): """更新所有監(jiān)控數(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]) # 自動調(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信息標簽 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) # 自動調(diào)整Y軸范圍 max_val = max(self.mem_data + [10]) self.mem_ax.set_ylim(0, max(100, max_val * 1.1)) # 更新內(nèi)存信息標簽 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) # 更新詳細內(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] # 只顯示第一個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) # 自動調(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詳細信息 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)絡數(shù)據(jù)""" # 獲取網(wǎng)絡IO net_io = psutil.net_io_counters() # 如果是第一次調(diào)用,只保存當前值不計算 if self.last_net_io is None: self.last_net_io = net_io self.last_net_time = time.time() return # 第一次不進行計算 # 計算每秒的發(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) # 自動調(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)絡信息 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)絡詳細信息 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() # 保存當前值供下次比較 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)用,只保存當前值不計算 if self.last_disk_io is None: self.last_disk_io = disk_io self.last_disk_time = time.time() return # 第一次不進行計算 # 計算每秒的讀/寫量 (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) # 自動調(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() # 保存當前值供下次比較 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): """更新進程信息""" try: # 獲取進程列表并按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個 for row, proc in enumerate(processes[:50]): for col, val in enumerate(proc): item = QTableWidgetItem(str(val)) item.setTextAlignment(Qt.AlignCenter) # 高亮顯示高資源占用的進程 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) # 更新進程信息標簽 total_procs = len(processes) running_procs = sum(1 for p in processes if p[4] == 'running') self.process_info_label.setText( f"系統(tǒng)進程監(jiān)控 | 總數(shù): {total_procs} | 運行中: {running_procs} | 顯示CPU最高的50個進程") except Exception as e: self.process_info_label.setText(f"進程監(jiān)控錯誤: {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]) # 自動調(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風扇轉速:\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)控錯誤: {str(e)}") def update_battery(self): """更新電池信息""" try: battery = psutil.sensors_battery() if battery is None: self.battery_info_label.setText("未檢測到電池") return # 更新電池數(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"剩余時間: {time_left}") self.battery_info_label.setText(info_text) # 更新詳細信息 detail_text = (f"是否充電: {'是' if battery.power_plugged else '否'}\n" f"剩余電量: {battery.percent}%\n" f"剩余時間: {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)控錯誤: {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溫度 (需要額外庫) 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): """獲取風扇轉速""" 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: # 需要特定平臺的實現(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): """分離當前標簽頁為獨立窗口""" 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): """保存截圖""" # 實現(xiàn)截圖保存邏輯 pass def export_data(self): """導出數(shù)據(jù)""" # 實現(xiàn)數(shù)據(jù)導出邏輯 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) # 設置全局字體 font = QFont("Consolas", 10) app.setFont(font) monitor = SystemMonitor() monitor.show() sys.exit(app.exec_())
總結與展望
本文介紹了一個具有《黑客帝國》風格的系統(tǒng)監(jiān)控工具的實現(xiàn)。該項目展示了如何將實用的系統(tǒng)監(jiān)控功能與美觀的視覺效果相結合,主要特點包括:
- 全面的系統(tǒng)資源監(jiān)控能力
- 獨特的數(shù)字雨背景效果
- 直觀的動態(tài)數(shù)據(jù)可視化
- 良好的用戶體驗和交互設計
未來改進方向
- 更多主題支持:實現(xiàn)暗黑、科技等多種主題風格
- 報警功能:當資源使用超過閾值時發(fā)出警告
- 歷史數(shù)據(jù)記錄:保存監(jiān)控數(shù)據(jù)供后續(xù)分析
- 遠程監(jiān)控:支持通過網(wǎng)絡監(jiān)控其他計算機
- 移動端適配:開發(fā)手機版監(jiān)控應用
以上就是使用Python和PyQt5實現(xiàn)全方面系統(tǒng)資源監(jiān)控的詳細內(nèi)容,更多關于Python系統(tǒng)資源監(jiān)控的資料請關注腳本之家其它相關文章!
相關文章
利用jupyter網(wǎng)頁版本進行python函數(shù)查詢方式
這篇文章主要介紹了利用jupyter網(wǎng)頁版本進行python函數(shù)查詢方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04詳解Python如何使用audioflux處理音頻數(shù)據(jù)
Python的audioflux庫是一個處理音頻數(shù)據(jù)的強大工具,旨在提供簡單而強大的接口,用于音頻信號處理、分析和合成,下面就跟隨小編一起來學習一下它的具體使用吧2023-06-06Object arrays cannot be loaded when
這篇文章主要介紹了Object arrays cannot be loaded when allow_pickle=False,本文給大家分享問題解決思路,需要的朋友可以參考下2022-11-11使用Python的Tornado框架實現(xiàn)一個Web端圖書展示頁面
Tornado是Python的一款高人氣Web開發(fā)框架,這里我們來展示使用Python的Tornado框架實現(xiàn)一個Web端圖書展示頁面的實例,通過該實例可以清楚地學習到Tornado的模板使用及整個Web程序的執(zhí)行流程.2016-07-07