2025年4月24日 星期四

PyQt5 GUI:IMU 時域圖顯示器

以下是 ✅ 完整支援 UTF-8 中文顯示 的 PyQt5 GUI 程式碼範例,包含:

  • matplotlib 圖表中文字支援 ✅

  • PyQt5 控制項中文顯示 ✅

  • pandas.read_csv() 使用 encoding='utf-8' 讀取中文 CSV ✅


✅ 全中文友善 PyQt5 GUI:IMU 時域圖顯示器

[python]
import sys import pandas as pd import numpy as np import matplotlib from PyQt5.QtWidgets import ( QApplication, QWidget, QVBoxLayout, QPushButton, QFileDialog, QComboBox, QLabel ) from PyQt5.QtCore import QTimer from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure import torch import torch.nn as nn # ✅ 設定 matplotlib 支援中文顯示 matplotlib.rcParams['font.sans-serif'] = ['Microsoft JhengHei', 'Taipei Sans TC Beta', 'Arial Unicode MS', 'SimHei'] matplotlib.rcParams['axes.unicode_minus'] = False # ✅ 模擬模型(可替換為自己訓練的模型) class DummyLSTM(nn.Module): def __init__(self, input_dim=6, hidden_dim=64, num_layers=1, num_classes=4): super().__init__() self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True) self.fc = nn.Linear(hidden_dim, num_classes) def forward(self, x): out, _ = self.lstm(x) out = self.fc(out[:, -1, :]) return out class IMUGUI(QWidget): def __init__(self): super().__init__() self.setWindowTitle('IMU 時域顯示器(支援 UTF-8 中文)') self.resize(900, 700) self.data = None self.index = 0 self.fs = 100 self.seq_len = 100 self.timer = QTimer() self.timer.timeout.connect(self.next_frame) self.model = DummyLSTM().eval() self.device = torch.device('cpu') layout = QVBoxLayout() self.load_button = QPushButton('📂 載入 CSV 檔案') self.play_button = QPushButton('▶️ 播放 / 暫停') self.combo = QComboBox() self.combo.addItems(['顯示 1 秒', '顯示 2 秒', '顯示 3 秒']) self.label = QLabel('📌 尚未載入資料') self.canvas = FigureCanvas(Figure()) self.ax = self.canvas.figure.add_subplot(111) layout.addWidget(self.load_button) layout.addWidget(self.play_button) layout.addWidget(self.combo) layout.addWidget(self.label) layout.addWidget(self.canvas) self.setLayout(layout) self.load_button.clicked.connect(self.load_csv) self.combo.currentIndexChanged.connect(self.set_seq_len) self.play_button.clicked.connect(self.toggle_play) def load_csv(self): fname, _ = QFileDialog.getOpenFileName(self, '選擇 CSV 檔案', '', 'CSV files (*.csv)') if fname: try: self.data = pd.read_csv(fname, encoding='utf-8') except UnicodeDecodeError: self.data = pd.read_csv(fname, encoding='big5') self.label.setText(f'✅ 已載入:{fname.split("/")[-1]}') self.set_seq_len() self.index = 0 self.update_plot() def set_seq_len(self): self.seq_len = self.fs * (self.combo.currentIndex() + 1) def toggle_play(self): if self.timer.isActive(): self.timer.stop() self.play_button.setText("▶️ 播放 / 暫停") else: self.timer.start(100) self.play_button.setText("⏸️ 播放中...") def next_frame(self): if self.data is None: return self.index += 1 if self.index + self.seq_len > len(self.data): self.timer.stop() self.play_button.setText("▶️ 播放 / 暫停") self.index = 0 self.update_plot() def update_plot(self): if self.data is None or self.index + self.seq_len > len(self.data): return segment = self.data.iloc[self.index:self.index + self.seq_len] time = [i * 0.01 for i in range(self.seq_len)] self.ax.clear() colors = ['red', 'blue', 'green'] for i in range(3): # b1~b3 self.ax.plot(time, segment[f'b{i+1}'], color=colors[i], label=f'b{i+1}') for i in range(3): # b4~b6 self.ax.plot(time, segment[f'b{i+4}'], linestyle='--', color=colors[i], label=f'b{i+4}') # 顯示實際類別與預測類別 actual_label = segment['type'].iloc[-1] imu_values = segment[[f'b{i+1}' for i in range(6)]].values imu_tensor = torch.tensor(imu_values, dtype=torch.float32).unsqueeze(0) with torch.no_grad(): pred = self.model(imu_tensor).argmax().item() self.ax.set_title(f'第 {self.index} ~ {self.index+self.seq_len} 筆|實際類別:{actual_label}|預測:{pred}') self.ax.set_xlabel('時間(秒)') self.ax.set_ylabel('IMU 數值') self.ax.legend() self.canvas.draw() if __name__ == '__main__': app = QApplication(sys.argv) gui = IMUGUI() gui.show() sys.exit(app.exec_())

✅ 特點總結:

項目說明
📂 載入中文 CSV使用 utf-8 或 fallback big5
🈶 中文標題、按鈕、圖例完全繁體中文化介面
📊 Matplotlib 中文支援不亂碼顯示圖表中文
⏱️ 支援即時播放自動滑動時間窗格,每 100ms 前進


================================================================

self.timer.start(100)PyQt5 中的 QTimer 定時器啟動指令,以下是它的詳細說明:


📘 指令說明

python
self.timer.start(100)

表示 啟動 QTimer 並每隔 100 毫秒(0.1 秒)觸發一次 timeout 事件


🧠 在你的 GUI 中代表什麼?

  • 你設置了一個 QTimer() 物件並綁定了:

    python
    self.timer.timeout.connect(self.next_frame)
  • 所以每次 100 毫秒 會執行一次 self.next_frame() 函式。

  • self.next_frame() 的作用是:

    • 將目前視窗滑動 1 筆

    • 重新呼叫 update_plot() 更新圖表內容

  • 結果就是:形成 IMU 時域圖的播放動畫效果(類似影片滑動窗)


⏱️ 參數 100 的意義

數值播放速度效果
100每秒更新 10 次(建議值)
50每秒更新 20 次(更快)
200每秒更新 5 次(慢動作)

✅ 延伸:可根據播放需求微調

你可以讓使用者手動選擇播放速率,例如新增:

python
self.speed_combo = QComboBox() self.speed_combo.addItems(['0.1 秒/格', '0.2 秒/格', '0.05 秒/格'])

並在 toggle_play() 中讀取:

python
interval = [100, 200, 50][self.speed_combo.currentIndex()] self.timer.start(interval)



沒有留言:

張貼留言