python实现一个示波器仿真,可以改参数同步效果

python实现一个示波器仿真,可以改参数同步效果_第1张图片

代码

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 = False
        self.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.Checked
        log_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, data
    
    def 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()    

你可能感兴趣的:(python,开发语言)