代码
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.animation import FuncAnimation
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QLabel, QSplitter, QGroupBox, QGridLayout, QDoubleSpinBox, QSpinBox, QCheckBox)
from PyQt5.QtCore import Qt# 设置matplotlib支持中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]# 初始化日志列表
log_messages = []
class WaveformApp(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("动态波形图")self.resize(1000, 700)# 创建中心部件central_widget = QWidget()self.setCentralWidget(central_widget)# 创建主布局main_layout = QVBoxLayout(central_widget)# 创建水平分割器(参数面板和波形/日志)main_splitter = QSplitter(Qt.Horizontal)# 创建参数控制面板self.create_parameter_panel()main_splitter.addWidget(self.param_panel)# 创建右侧垂直分割器(波形和日志)right_splitter = QSplitter(Qt.Vertical)# 创建图形区域self.fig, self.ax = plt.subplots(figsize=(8, 5))self.canvas = FigureCanvas(self.fig)toolbar = NavigationToolbar(self.canvas, self)# 创建公式显示区域self.formula_label = QLabel()self.formula_label.setStyleSheet("font-size: 14pt; font-weight: bold;")# 波形参数self.params = {'amplitude': 1.0, # 振幅'frequency': 1.0, # 频率(Hz)'phase': 0.0, # 相位(弧度)'noise': 0.5, # 噪声强度'time_range': 10.0, # 时间范围(秒)'num_points': 1000, # 采样点数'speed': 0.1 # 动画速度因子}# 现在可以安全地更新公式显示self.update_formula_display()# 创建日志区域log_widget = QWidget()log_layout = QVBoxLayout(log_widget)log_label = QLabel("操作日志:")self.log_text = QTextEdit()self.log_text.setReadOnly(True)log_layout.addWidget(log_label)log_layout.addWidget(self.log_text)# 添加到右侧分割器formula_frame = QWidget()formula_layout = QVBoxLayout(formula_frame)formula_layout.addWidget(self.formula_label)right_splitter.addWidget(self.canvas)right_splitter.addWidget(formula_frame)right_splitter.addWidget(log_widget)right_splitter.setSizes([400, 50, 200])# 添加到主分割器main_splitter.addWidget(right_splitter)main_splitter.setSizes([250, 750])# 添加到主布局main_layout.addWidget(toolbar)main_layout.addWidget(main_splitter)# 初始化数据和图形self.t, self.data = self.init_data()self.line, = self.ax.plot(self.t, self.data)self.ax.set_xlabel('时间 (秒)')self.ax.set_ylabel('幅度')self.ax.set_title('动态波形图')self.fig.tight_layout()# 创建动画self.paused = Falseself.ani = FuncAnimation(self.fig, self.update, frames=range(1000), interval=100, repeat=True, blit=True)def create_parameter_panel(self):self.param_panel = QGroupBox("波形参数")param_layout = QGridLayout(self.param_panel)# 创建参数控件row = 0# 振幅param_layout.addWidget(QLabel("振幅:"), row, 0)self.amplitude_spin = QDoubleSpinBox()self.amplitude_spin.setRange(0.1, 5.0)self.amplitude_spin.setValue(1.0)self.amplitude_spin.setSingleStep(0.1)self.amplitude_spin.valueChanged.connect(self.on_param_changed)param_layout.addWidget(self.amplitude_spin, row, 1)row += 1# 频率param_layout.addWidget(QLabel("频率 (Hz):"), row, 0)self.frequency_spin = QDoubleSpinBox()self.frequency_spin.setRange(0.1, 5.0)self.frequency_spin.setValue(1.0)self.frequency_spin.setSingleStep(0.1)self.frequency_spin.valueChanged.connect(self.on_param_changed)param_layout.addWidget(self.frequency_spin, row, 1)row += 1# 相位param_layout.addWidget(QLabel("相位 (弧度):"), row, 0)self.phase_spin = QDoubleSpinBox()self.phase_spin.setRange(0.0, 2*np.pi)self.phase_spin.setValue(0.0)self.phase_spin.setSingleStep(0.1)self.phase_spin.valueChanged.connect(self.on_param_changed)param_layout.addWidget(self.phase_spin, row, 1)row += 1# 噪声强度param_layout.addWidget(QLabel("噪声强度:"), row, 0)self.noise_spin = QDoubleSpinBox()self.noise_spin.setRange(0.0, 2.0)self.noise_spin.setValue(0.5)self.noise_spin.setSingleStep(0.1)self.noise_spin.valueChanged.connect(self.on_param_changed)param_layout.addWidget(self.noise_spin, row, 1)row += 1# 时间范围param_layout.addWidget(QLabel("时间范围 (秒):"), row, 0)self.time_range_spin = QDoubleSpinBox()self.time_range_spin.setRange(1.0, 30.0)self.time_range_spin.setValue(10.0)self.time_range_spin.setSingleStep(1.0)self.time_range_spin.valueChanged.connect(self.on_param_changed)param_layout.addWidget(self.time_range_spin, row, 1)row += 1# 采样点数param_layout.addWidget(QLabel("采样点数:"), row, 0)self.num_points_spin = QSpinBox()self.num_points_spin.setRange(100, 5000)self.num_points_spin.setValue(1000)self.num_points_spin.setSingleStep(100)self.num_points_spin.valueChanged.connect(self.on_param_changed)param_layout.addWidget(self.num_points_spin, row, 1)row += 1# 动画速度param_layout.addWidget(QLabel("动画速度:"), row, 0)self.speed_spin = QDoubleSpinBox()self.speed_spin.setRange(0.01, 1.0)self.speed_spin.setValue(0.1)self.speed_spin.setSingleStep(0.01)self.speed_spin.valueChanged.connect(self.on_param_changed)param_layout.addWidget(self.speed_spin, row, 1)row += 1# 显示网格self.grid_check = QCheckBox("显示网格")self.grid_check.setChecked(True)self.grid_check.stateChanged.connect(self.on_param_changed)param_layout.addWidget(self.grid_check, row, 0, 1, 2)row += 1# 暂停/继续按钮self.pause_button = QCheckBox("暂停动画")self.pause_button.stateChanged.connect(self.toggle_pause)param_layout.addWidget(self.pause_button, row, 0, 1, 2)def on_param_changed(self):# 更新参数self.params['amplitude'] = self.amplitude_spin.value()self.params['frequency'] = self.frequency_spin.value()self.params['phase'] = self.phase_spin.value()self.params['noise'] = self.noise_spin.value()self.params['time_range'] = self.time_range_spin.value()self.params['num_points'] = self.num_points_spin.value()self.params['speed'] = self.speed_spin.value()# 更新网格显示self.ax.grid(self.grid_check.isChecked())# 重新初始化数据self.t, self.data = self.init_data()self.line.set_data(self.t, self.data)self.ax.set_xlim(0, self.params['time_range'])# 更新公式显示self.update_formula_display()# 重绘图形self.canvas.draw()log_msg = "参数已更新"log_messages.append(log_msg)self.update_log(log_msg)def toggle_pause(self, state):self.paused = state == Qt.Checkedlog_msg = "动画已暂停" if self.paused else "动画已恢复"log_messages.append(log_msg)self.update_log(log_msg)def update_formula_display(self):# 显示当前使用的数学公式formula = f"y(t) = {self.params['amplitude']:.1f} × sin(2π × {self.params['frequency']:.1f}t + {self.params['phase']:.1f}) + {self.params['noise']:.1f} × 随机噪声"self.formula_label.setText(formula)def init_data(self):t = np.linspace(0, self.params['time_range'], self.params['num_points'])data = self.params['amplitude'] * np.sin(2 * np.pi * self.params['frequency'] * t + self.params['phase'])data += self.params['noise'] * np.random.randn(len(t))log_msg = f"数据已重新生成: 时间范围={self.params['time_range']}秒, 点数={self.params['num_points']}"log_messages.append(log_msg)self.update_log(log_msg)return t, datadef update(self, frame):if self.paused:return self.line,# 计算新数据new_data = self.params['amplitude'] * np.sin(2 * np.pi * self.params['frequency'] * self.t + self.params['phase'] + frame * self.params['speed'])new_data += self.params['noise'] * np.random.randn(len(self.t))self.line.set_ydata(new_data)log_msg = f"第{frame}帧更新,生成新的波形数据"log_messages.append(log_msg)self.update_log(log_msg)return self.line,def update_log(self, message):self.log_text.append(message)# 滚动到底部cursor = self.log_text.textCursor()cursor.movePosition(cursor.End)self.log_text.setTextCursor(cursor)def main():app = QApplication(sys.argv)window = WaveformApp()window.show()sys.exit(app.exec_())if __name__ == "__main__":main()