# monitor **Repository Path**: xiaohao-xueb/monitor ## Basic Information - **Project Name**: monitor - **Description**: No description available - **Primary Language**: C/C++ - **License**: AFL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-08-14 - **Last Updated**: 2025-08-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # CPU监控系统面试复习文档 ## 项目概述 ### 项目简介 这是一个基于Qt6和C++17开发的**智能运维监控系统**,实现了本地和远程CPU使用率的实时监控。项目采用多线程架构,通过自定义绘制引擎展示CPU使用率趋势图,并支持SSH远程监控功能。 ### 核心功能 - **本地CPU监控**:实时读取`/proc/stat`文件,计算CPU使用率 - **远程SSH监控**:通过SSH协议远程获取主机CPU数据 - **自定义图表**:基于QPainter实现高性能折线图绘制 - **数据源切换**:支持本地/远程数据的动态切换显示 - **多线程架构**:数据采集与UI渲染完全分离 ### 技术栈 - **C++17** - 现代C++特性,RAII资源管理 - **Qt6框架** - 跨平台GUI开发 - **多线程框架** - QThread + QMutex + QWaitCondition - **信号槽机制** - 线程间安全通信 - **Linux系统编程** - `/proc`文件系统读取 - **进程间通信** - QProcess执行SSH命令 - **SSH协议** - 零部署远程监控 - **自定义绘制引擎** - QPainter + QPainterPath ## 技术架构 ### 系统架构图 ``` ┌─────────────────────────────────────────────────────────────┐ │ MainWindow │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │ │ │ CPUMonitor │ │ SSHMonitor │ │ ChartWidget │ │ │ │ │ │ │ │ │ │ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────┐ │ │ │ │ │DataCollection│ │ │ │ QProcess │ │ │ │QPainter │ │ │ │ │ │ Thread │ │ │ │ QTimer │ │ │ │绘制引擎 │ │ │ │ │ └─────────────┘ │ │ └─────────────┘ │ │ └─────────┘ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────┐ │ 信号槽机制 │ │ 线程间通信 │ └─────────────────┘ ``` ### 模块依赖关系 ``` UI层 (MainWindow) ├── 依赖: CPUMonitor, SSHMonitor, ChartWidget ├── 提供: 用户界面和交互控制 数据采集层 (CPUMonitor + DataCollectionThread) ├── 依赖: 无 ├── 提供: 本地CPU数据采集 网络通信层 (SSHMonitor) ├── 依赖: QProcess, QTimer ├── 提供: 远程CPU数据获取 显示层 (ChartWidget) ├── 依赖: QPainter, QPainterPath ├── 提供: 数据可视化显示 ``` ## 核心模块详解 ### 1. 数据采集线程模块 (DataCollectionThread) #### 核心代码 ```cpp class DataCollectionThread : public QThread { Q_OBJECT public: explicit DataCollectionThread(QObject *parent = nullptr); ~DataCollectionThread(); void stopThread(); CPUData getCurrentData() const; signals: void dataUpdated(CPUData data); protected: void run() override; private: CPUData readCPUStats(); // 线程控制 volatile bool stopFlag; mutable QMutex dataMutex; QWaitCondition waitCondition; // 数据存储 CPUData currentData; // CPU计算相关 bool firstRead; unsigned long long lastUser, lastSystem, lastIdle, lastTotal; }; ``` #### 关键实现 ```cpp void DataCollectionThread::run() { qDebug() << "数据采集线程创建开始执行任务!"; while(!stopFlag) { // 读取CPU数据 CPUData newdata = readCPUStats(); { QMutexLocker locker(&dataMutex); currentData = newdata; } // 发送信号 emit dataUpdated(newdata); // 等待1秒或线程停止 QMutexLocker locker(&dataMutex); if(!stopFlag) { waitCondition.wait(&dataMutex, 1000); } } } CPUData DataCollectionThread::readCPUStats() { CPUData data; QFile file("/proc/stat"); if(!file.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text)) { return data; } QTextStream in(&file); QString line = in.readLine(); if(line.startsWith("cpu ")) { QStringList parts = line.split(" ", Qt::SkipEmptyParts); if(parts.size() >= 5) { unsigned long long user = parts[1].toULongLong(); unsigned long long nice = parts[2].toULongLong(); unsigned long long system = parts[3].toULongLong(); unsigned long long idle = parts[4].toULongLong(); unsigned long long totalCurrent = user + nice + system + idle; if (!firstRead) { // 计算CPU使用率 unsigned long long totalDiff = totalCurrent - lastTotal; unsigned long long idleDiff = idle - lastIdle; unsigned long long userDiff = user + nice - lastUser; unsigned long long systemDiff = system - lastSystem; if (totalDiff > 0) { data.total = ((double)(totalDiff - idleDiff) / totalDiff) * 100.0; data.user = ((double)userDiff / totalDiff) * 100.0; data.system = ((double)systemDiff / totalDiff) * 100.0; data.idle = ((double)idleDiff / totalDiff) * 100.0; } } // 保存当前值 lastUser = user + nice; lastSystem = system; lastIdle = idle; lastTotal = totalCurrent; firstRead = false; } } file.close(); data.timestamp = QDateTime::currentMSecsSinceEpoch(); return data; } ``` #### 核心知识点 **1. 多线程编程** - **QThread继承**:通过继承QThread实现自定义线程类 - **线程安全**:使用QMutex保护共享数据,避免数据竞争 - **条件变量**:使用QWaitCondition实现线程等待和唤醒 - **优雅停止**:通过stopFlag标志和wakeAll()实现线程优雅停止 **2. Linux系统编程** - **/proc文件系统**:Linux内核提供的虚拟文件系统,包含系统信息 - **/proc/stat**:包含CPU使用统计信息,格式为`cpu user nice system idle iowait irq softirq` - **CPU使用率计算**:通过两次读取的差值计算使用率百分比 **3. 信号槽机制** - **跨线程信号**:Qt的信号槽机制支持跨线程通信 - **异步更新**:后台线程通过信号通知主线程数据更新 #### 流程图 ```mermaid flowchart TD A[启动DataCollectionThread] --> B[初始化变量] B --> B1[firstRead=true, stopFlag=false] B1 --> C[进入run主循环] C --> D{stopFlag为true?} D -->|是| E[线程结束] D -->|否| F[调用readCPUStats] F --> G[打开/proc/stat文件] G --> H{文件打开成功?} H -->|否| I[返回空数据] H -->|是| J[读取第一行CPU数据] J --> K{数据格式正确?} K -->|否| I K -->|是| L{firstRead为true?} L -->|是| M[保存当前值到last变量] L -->|否| N[计算CPU使用率] M --> M1[firstRead=false] M1 --> O[等待1秒] N --> P[计算差值: totalDiff, userDiff, systemDiff, idleDiff] P --> Q{totalDiff > 0?} Q -->|否| O Q -->|是| R[计算百分比使用率] R --> S[数据范围限制0-100%] S --> T[设置时间戳] T --> U[更新currentData] U --> V[发送dataUpdated信号] V --> W[等待1秒或线程停止] W --> D O --> D I --> D ``` ### 2. SSH远程监控模块 (SSHMonitor) #### 核心代码 ```cpp class SSHMonitor : public QObject { Q_OBJECT public: explicit SSHMonitor(QObject *parent = nullptr); ~SSHMonitor(); bool connectToHost(const QString &host, const QString &user); void disconnect(); bool isConnected() const; CPUData getRemoteCPUData() const; signals: void dataUpdated(const CPUData &data); void connectionStatusChanged(bool connected); void errorOccurred(const QString &error); private slots: void onSSHCommandFinished(int exitCode, QProcess::ExitStatus exitStatus); void onSSHCommandError(QProcess::ProcessError error); void updateRemoteData(); private: QString executeSSHCommand(const QString &command); CPUData parseCPUStats(const QString &data); QString m_host; QString m_user; bool m_connected; QTimer *m_updateTimer; QProcess *m_sshProcess; // CPU计算相关 bool m_firstRead; unsigned long long m_lastUser, m_lastSystem, m_lastIdle, m_lastTotal; CPUData m_currentData; }; ``` #### 关键实现 ```cpp bool SSHMonitor::connectToHost(const QString &host, const QString &user) { if(m_connected) { disconnect(); } m_host = host; m_user = user; // 先设置连接状态为true,允许executeSSHCommand执行 m_connected = true; // 测试SSH连接 QString testResult = executeSSHCommand("echo 'SSH connection test'"); if(testResult.contains("SSH connection test")) { // 连接成功,启动定时器 m_updateTimer->start(1000); emit connectionStatusChanged(true); return true; } else { m_connected = false; emit errorOccurred("SSH连接失败"); return false; } } QString SSHMonitor::executeSSHCommand(const QString &command) { if(!m_connected) { return QString(); } QStringList arguments; arguments << "-o" << "StrictHostKeyChecking=no"; arguments << "-o" << "UserKnownHostsFile=/dev/null"; arguments << "-o" << "ConnectTimeout=10"; arguments << "-o" << "BatchMode=yes"; arguments << QString("%1@%2").arg(m_user, m_host); arguments << command; m_sshProcess->start("ssh", arguments); m_sshProcess->waitForFinished(10000); // 10秒超时 if(m_sshProcess->exitCode() == 0) { return m_sshProcess->readAllStandardOutput(); } return QString(); } void SSHMonitor::updateRemoteData() { if(!m_connected) return; QString result = executeSSHCommand("cat /proc/stat"); if(!result.isEmpty()) { CPUData data = parseCPUStats(result); m_currentData = data; emit dataUpdated(data); } } ``` #### 核心知识点 **1. 进程间通信** - **QProcess类**:Qt提供的进程管理类,用于启动外部程序 - **SSH命令执行**:通过QProcess执行SSH命令获取远程数据 - **超时处理**:使用waitForFinished()设置命令执行超时 - **错误处理**:通过exitCode()判断命令执行是否成功 **2. 定时器机制** - **QTimer类**:Qt定时器,用于定期执行任务 - **定时更新**:每1秒执行一次远程数据获取 - **信号槽连接**:定时器超时信号连接到更新函数 **3. SSH协议** - **零部署优势**:无需在远程主机安装额外程序 - **SSH选项**:使用各种SSH选项确保连接稳定 - **密钥认证**:支持SSH密钥认证,无需密码输入 #### 流程图 ```mermaid flowchart TD A[SSHMonitor初始化] --> B[创建QProcess和QTimer] B --> C[连接信号槽] C --> D[用户点击连接按钮] D --> E[调用connectToHost] E --> F[设置m_connected=true] F --> G[执行测试命令] G --> H{命令执行成功?} H -->|否| I[设置m_connected=false] H -->|是| J[启动定时器] I --> I1[发送errorOccurred信号] J --> J1[每1秒执行updateRemoteData] J1 --> K[发送connectionStatusChanged信号] K --> L[定时器触发updateRemoteData] L --> M[执行SSH命令] M --> N{命令执行成功?} N -->|否| O[发送errorOccurred信号] N -->|是| P[解析/proc/stat数据] P --> Q{firstRead为true?} Q -->|是| R[保存当前值到last变量] Q -->|否| S[计算CPU使用率] R --> R1[firstRead=false] R1 --> T[等待下次定时器触发] S --> U[计算差值并计算百分比] U --> V[设置时间戳] V --> W[发送dataUpdated信号] W --> T T --> L O --> T ``` ### 3. 图表绘制模块 (ChartWidget) #### 核心代码 ```cpp class ChartWidget : public QWidget { Q_OBJECT public: explicit ChartWidget(QWidget *parent = nullptr); void updateData(const CPUData &data, bool isLocalData = true); void clearData(); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: void drawBackground(QPainter &painter); void drawGrid(QPainter &painter); void drawAxes(QPainter &painter); void drawLines(QPainter &painter); void drawLegend(QPainter &painter); QPoint mapToWidget(qint64 timestamp, double value); QString formatTime(qint64 timestamp); // 数据存储 QList dataHistory; QList dataSourceFlags; // true=本地数据, false=远程数据 static const int MAX_DATA_POINTS = 60; // 绘图区域 QRect chartRect; QRect legendRect; // 坐标轴范围 qint64 minTime, maxTime; double minValue, maxValue; // 颜色定义 QColor totalColor, userColor, systemColor, gridColor, axisColor, backgroundColor; }; ``` #### 关键实现 ```cpp void ChartWidget::updateData(const CPUData &data, bool isLocalData) { // 添加数据 dataHistory.append(data); dataSourceFlags.append(isLocalData); // 限制数据点的数量 if(dataHistory.size() > MAX_DATA_POINTS) { dataHistory.removeFirst(); dataSourceFlags.removeFirst(); } // 更新坐标轴范围 if(!dataHistory.isEmpty()) { minTime = dataHistory.first().timestamp; maxTime = dataHistory.last().timestamp; // 确保显示60s数据 if(maxTime-minTime < 60000) { maxTime = minTime + 60000; } update(); // 触发重绘 } } void ChartWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 计算绘图区域 int margin = 60; int legendHeight = 80; chartRect = QRect(margin, margin, width() - 2 * margin, height() - 2 * margin - legendHeight); legendRect = QRect(margin, height() - margin - legendHeight, width() - 2 * margin, legendHeight); // 绘制各个部分 drawBackground(painter); drawGrid(painter); drawAxes(painter); drawLines(painter); drawLegend(painter); } void ChartWidget::drawLines(QPainter &painter) { if(dataHistory.size() < 2) return; QPainterPath localTotalPath, localUserPath, localSystemPath; QPainterPath remoteTotalPath, remoteUserPath, remoteSystemPath; bool firstLocal = true, firstRemote = true; for (int i = 0; i < dataHistory.size(); ++i) { const CPUData &data = dataHistory[i]; bool isLocal = dataSourceFlags[i]; if (isLocal) { // 绘制本地数据(实线) QPoint totalPoint = mapToWidget(data.timestamp, data.total); QPoint userPoint = mapToWidget(data.timestamp, data.user); QPoint systemPoint = mapToWidget(data.timestamp, data.system); if (firstLocal) { localTotalPath.moveTo(totalPoint); localUserPath.moveTo(userPoint); localSystemPath.moveTo(systemPoint); firstLocal = false; } else { localTotalPath.lineTo(totalPoint); localUserPath.lineTo(userPoint); localSystemPath.lineTo(systemPoint); } } else { // 绘制远程数据(虚线) QPoint totalPoint = mapToWidget(data.timestamp, data.total); QPoint userPoint = mapToWidget(data.timestamp, data.user); QPoint systemPoint = mapToWidget(data.timestamp, data.system); if (firstRemote) { remoteTotalPath.moveTo(totalPoint); remoteUserPath.moveTo(userPoint); remoteSystemPath.moveTo(systemPoint); firstRemote = false; } else { remoteTotalPath.lineTo(totalPoint); remoteUserPath.lineTo(userPoint); remoteSystemPath.lineTo(systemPoint); } } } // 绘制本地数据(实线) painter.setPen(QPen(totalColor, 2, Qt::SolidLine)); painter.drawPath(localTotalPath); painter.setPen(QPen(userColor, 2, Qt::SolidLine)); painter.drawPath(localUserPath); painter.setPen(QPen(systemColor, 2, Qt::SolidLine)); painter.drawPath(localSystemPath); // 绘制远程数据(虚线) painter.setPen(QPen(totalColor, 2, Qt::DashLine)); painter.drawPath(remoteTotalPath); painter.setPen(QPen(userColor, 2, Qt::DashLine)); painter.drawPath(remoteUserPath); painter.setPen(QPen(systemColor, 2, Qt::DashLine)); painter.drawPath(remoteSystemPath); } ``` #### 核心知识点 **1. Qt绘图系统** - **QPainter类**:Qt的2D绘图引擎,提供丰富的绘图功能 - **QPainterPath类**:用于创建复杂的图形路径,支持平滑曲线 - **抗锯齿渲染**:使用setRenderHint(QPainter::Antialiasing)提高绘图质量 - **坐标映射**:将数据坐标映射到屏幕坐标 **2. 自定义控件** - **QWidget继承**:通过继承QWidget创建自定义控件 - **paintEvent重写**:重写paintEvent()函数实现自定义绘制 - **resizeEvent处理**:处理控件大小变化事件 **3. 数据可视化** - **实时更新**:通过update()触发重绘,实现实时数据更新 - **数据管理**:使用QList管理历史数据,限制数据点数量 - **视觉区分**:通过实线和虚线区分本地和远程数据 #### 流程图 ```mermaid flowchart TD A[ChartWidget初始化] --> B[设置颜色和样式] B --> C[用户调用updateData] C --> D[添加数据到dataHistory] D --> E[添加数据源标识到dataSourceFlags] E --> F{dataHistory大小 > MAX_DATA_POINTS?} F -->|是| G[移除最旧的数据点] F -->|否| H[更新时间轴范围] G --> H H --> I[计算minTime和maxTime] I --> J[确保显示60秒数据] J --> K[调用update触发重绘] K --> L[paintEvent被调用] L --> M[创建QPainter并设置抗锯齿] M --> N[计算绘图区域] N --> N1[chartRect和legendRect] N1 --> O[调用drawBackground] O --> P[调用drawGrid] P --> Q[调用drawAxes] Q --> R[调用drawLines] R --> S[遍历dataHistory] S --> T{数据点类型?} T -->|本地数据| U[绘制实线路径] T -->|远程数据| V[绘制虚线路径] U --> W[设置实线样式] V --> X[设置虚线样式] W --> Y[绘制CPU线条] X --> Y Y --> Z[调用drawLegend] Z --> AA[绘制完成] ``` ### 4. 主窗口控制模块 (MainWindow) #### 核心代码 ```cpp class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void onCPUDataUpdated(CPUData data); void onSSHDataUpdated(CPUData data); void onSSHConnectionStatusChanged(bool connected); void onSSHErrorOccurred(const QString &error); void onToggleDataSelected(); void connectToSSH(); void disconnectFromSSH(); private: void setupUI(); void setupRemoteUI(); void setupDataSourceUI(); void updateLabels(CPUData data); // UI组件 ChartWidget *chartWidget; QLabel *totalLabel, *userLabel, *systemLabel, *idleLabel; QLineEdit *hostEdit, *userEdit; QPushButton *connectBtn, *disconnectBtn, *toggleDataBtn; QGroupBox *remoteGroupBox, *dataSourceGroupBox; QLabel *statusLabel; // 数据源控制 bool showLocalData; // 监控器 CPUMonitor *cpuMonitor; SSHMonitor *sshMonitor; }; ``` #### 关键实现 ```cpp void MainWindow::setupUI() { // 创建中央部件 QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); // 创建主布局 QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); // 标题 QLabel *titleLabel = new QLabel("CPU使用率实时监控", this); QFont titleFont = titleLabel->font(); titleFont.setPointSize(16); titleFont.setBold(true); titleLabel->setFont(titleFont); titleLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(titleLabel); // 图表区域 chartWidget = new ChartWidget(this); chartWidget->setMinimumSize(800, 400); mainLayout->addWidget(chartWidget); // 详细信息区域 QHBoxLayout *infoLayout = new QHBoxLayout(); totalLabel = new QLabel("总CPU使用率: 0.0%", this); userLabel = new QLabel("用户态CPU: 0.0%", this); systemLabel = new QLabel("系统态CPU: 0.0%", this); idleLabel = new QLabel("空闲CPU: 0.0%", this); infoLayout->addWidget(totalLabel); infoLayout->addWidget(userLabel); infoLayout->addWidget(systemLabel); infoLayout->addWidget(idleLabel); infoLayout->addStretch(); mainLayout->addLayout(infoLayout); // 添加远程监控UI setupRemoteUI(); mainLayout->addWidget(remoteGroupBox); // 添加数据源切换UI setupDataSourceUI(); mainLayout->addWidget(dataSourceGroupBox); } void MainWindow::onCPUDataUpdated(CPUData data) { // 根据数据源选择决定是否更新图表 if (showLocalData) { chartWidget->updateData(data, true); // true表示本地数据 updateLabels(data); } } void MainWindow::onSSHDataUpdated(CPUData data) { // 根据数据源选择决定是否更新图表 if (!showLocalData) { chartWidget->updateData(data, false); // false表示远程数据 updateLabels(data); } } void MainWindow::onToggleDataSelected() { // 清空图表数据 chartWidget->clearData(); // 切换数据源 showLocalData = !showLocalData; // 更新按钮文本和样式 if (showLocalData) { toggleDataBtn->setText("切换到远程数据"); toggleDataBtn->setStyleSheet("background-color: #4CAF50; color: white;"); } else { toggleDataBtn->setText("切换到本地数据"); toggleDataBtn->setStyleSheet("background-color: #2196F3; color: white;"); } } void MainWindow::connectToSSH() { QString host = hostEdit->text().trimmed(); QString user = userEdit->text().trimmed(); if (host.isEmpty() || user.isEmpty()) { QMessageBox::warning(this, "输入错误", "请输入主机地址和用户名"); return; } connectBtn->setEnabled(false); statusLabel->setText("正在连接..."); if (sshMonitor->connectToHost(host, user)) { statusLabel->setText("连接成功"); disconnectBtn->setEnabled(true); } else { statusLabel->setText("连接失败"); connectBtn->setEnabled(true); } } ``` #### 核心知识点 **1. Qt界面设计** - **布局管理**:使用QVBoxLayout、QHBoxLayout等布局管理器 - **控件创建**:创建各种Qt控件(QLabel、QPushButton、QLineEdit等) - **样式设置**:通过setStyleSheet()设置控件样式 - **信号槽连接**:连接控件的信号到对应的槽函数 **2. 状态管理** - **数据源切换**:通过showLocalData标志控制显示本地或远程数据 - **UI状态同步**:根据连接状态更新按钮的启用/禁用状态 - **错误处理**:通过QMessageBox显示错误信息 **3. 模块协调** - **信号转发**:将各个模块的信号转发到对应的处理函数 - **数据过滤**:根据当前数据源选择过滤数据更新 - **资源管理**:管理各个模块的生命周期 #### 流程图 ```mermaid flowchart TD A[MainWindow初始化] --> B[创建CPUMonitor和SSHMonitor] B --> C[调用setupUI] C --> D[创建ChartWidget] D --> E[创建标签和按钮] E --> F[调用setupRemoteUI] F --> G[创建SSH连接界面] G --> H[调用setupDataSourceUI] H --> I[创建数据源切换按钮] I --> J[连接信号槽] J --> K[等待用户操作] K --> L{用户操作类型?} L -->|本地数据更新| M[onCPUDataUpdated] L -->|远程数据更新| N[onSSHDataUpdated] L -->|SSH连接状态变化| O[onSSHConnectionStatusChanged] L -->|SSH错误| P[onSSHErrorOccurred] L -->|切换数据源| Q[onToggleDataSelected] L -->|连接SSH| R[connectToSSH] L -->|断开SSH| S[disconnectFromSSH] M --> T{showLocalData为true?} T -->|是| U[更新图表和标签] T -->|否| V[忽略数据] N --> W{showLocalData为false?} W -->|是| U W -->|否| V Q --> X[清空图表数据] X --> Y[切换showLocalData状态] Y --> Z[更新按钮文本和样式] R --> AA[调用sshMonitor->connectToHost] S --> BB[调用sshMonitor->disconnect] U --> K V --> K Z --> K AA --> K BB --> K O --> K P --> K ``` ## 总体系统流程图 ### 系统初始化流程 ```mermaid flowchart TD A[程序启动] --> B[MainWindow初始化] B --> C[创建CPUMonitor] C --> D[创建DataCollectionThread] D --> E[创建SSHMonitor] E --> F[创建ChartWidget] F --> G[设置UI界面] G --> H[连接信号槽] H --> I[启动数据采集线程] I --> J[等待用户操作] ``` ### 本地监控数据流 ```mermaid flowchart TD A[用户启动本地监控] --> B[DataCollectionThread开始运行] B --> C[读取/proc/stat] C --> D[计算CPU使用率] D --> E[发送dataUpdated信号] E --> F{showLocalData为true?} F -->|是| G[更新ChartWidget] F -->|否| H[忽略数据] G --> I[ChartWidget重绘] I --> J[显示折线图] J --> K[返回等待状态] H --> K ``` ### 远程监控数据流 ```mermaid flowchart TD A[用户连接远程主机] --> B[SSHMonitor建立连接] B --> C[执行SSH命令] C --> D[获取远程/proc/stat] D --> E[解析远程CPU数据] E --> F[发送dataUpdated信号] F --> G{showLocalData为false?} G -->|是| H[更新ChartWidget] G -->|否| I[忽略数据] H --> J[ChartWidget重绘] J --> K[显示折线图] K --> L[返回等待状态] I --> L ``` ### 数据源切换流程 ```mermaid flowchart TD A[用户切换数据源] --> B[清空图表数据] B --> C[更新showLocalData标志] C --> D[更新按钮文本和样式] D --> E[返回等待状态] ``` ### 系统退出流程 ```mermaid flowchart TD A[用户退出程序] --> B[停止所有线程] B --> C[释放资源] C --> D[程序结束] ```