You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Matplotlib绘制的音频频谱图添加到PyQt指定布局中

问题修复方案

错误原因

  • 你在音频控件的set_audio方法中直接将matplotlib画布添加到了主窗口的布局,而非当前音频控件的内部区域,导致画布被放在了原有展示区的下方,出现挤压问题
  • 承载音频功能的DragAndDropAudio类继承自QLabel,没有配置内部布局,无法直接容纳matplotlib画布控件

修复后的完整代码

import sys, os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import * 

#audio plot on pyqt
import numpy as np
from scipy.io.wavfile import read
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


class UI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Programme")
        self.setFixedSize(600, 600)

        central = QWidget()
        self.setCentralWidget(central)
        self.layout = QVBoxLayout(central)
        # 调整对齐避免控件错位
        self.layout.setAlignment(Qt.AlignTop | Qt.AlignHCenter)

        combo = QComboBox(self)
        combo.addItems(("textbox", "image", "audio"))
        combo.setFixedSize(200, 30)
        self.layout.addWidget(combo)

        self.lastWidget = QLineEdit(self)
        self.lastWidget.setFixedSize(500, 500)
        self.layout.addWidget(self.lastWidget)
        combo.activated[str].connect(self.onChanged)  

        self.show()

    def onChanged(self, text):
        # 先移除旧控件
        self.layout.removeWidget(self.lastWidget)
        self.lastWidget.deleteLater()

        if text == 'textbox':
            self.lastWidget = QLineEdit()
            self.lastWidget.setFixedSize(500, 500)
        elif text == 'image':
            self.lastWidget = DragAndDropImage()
        elif text == 'audio':
            self.lastWidget = DragAndDropAudio()
        
        # 统一添加新控件
        self.layout.addWidget(self.lastWidget)
        self.update()

class DragAndDropAudio(QWidget):
    # 改继承QWidget方便添加内部布局
    def __init__(self):
        super().__init__()
        self.setAcceptDrops(True)
        self.setFixedSize(500, 500)
        # 给音频控件加内部布局
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        # 添加拖拽提示文本
        self.tip_label = QLabel('\n\n Drop Wav Audio Here \n\n')
        self.tip_label.setAlignment(Qt.AlignCenter)
        self.tip_label.setStyleSheet('''
            QLabel{
                border: 4px dashed #aaa
            }
        ''')
        self.layout.addWidget(self.tip_label)
 
    
    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            file_path = event.mimeData().urls()[0].toLocalFile()
            if file_path.lower().endswith('.wav'):
                self.set_audio(file_path)
                event.accept()
                return
        event.ignore() 
    
    def set_audio(self, file_path):
        # 先清空原有提示内容
        for i in reversed(range(self.layout.count())): 
            self.layout.itemAt(i).widget().deleteLater()

        # 生成matplotlib画布
        canvas = FigureCanvas(Figure(figsize=(5, 5), dpi=100))
        ax = canvas.figure.subplots()
        # 读取音频文件
        input_data = read(file_path)
        audio = input_data[1]
        # 绘制音频波形
        ax.plot(audio[0:1024])
        ax.set(xlabel='Sample Point', ylabel='Amplitude',
               title='Audio Waveform')
        # 把画布添加到当前控件的内部布局,不会影响主布局结构
        self.layout.addWidget(canvas)
        

class DragAndDropImage(QLabel):
    def __init__(self):
        super().__init__()
        self.setAcceptDrops(True)
        self.setFixedSize(500, 500)
        self.setAlignment(Qt.AlignCenter)
        self.setText('\n\n Drop Image Here (InputImage) \n\n')
        self.setStyleSheet('''
            QLabel{
                border: 4px dashed #aaa
            }
        ''')

    def setPixmap(self, image):
        super().setPixmap(image.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
    
    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            file_path = event.mimeData().urls()[0].toLocalFile()
            if file_path.lower().endswith(('.png','.jpg','.jpeg','.bmp')):
                event.accept()
                return
        event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            file_path = event.mimeData().urls()[0].toLocalFile()
            self.set_image(file_path)
            event.accept()
        else:
            event.ignore() 
    
    def set_image(self, file_path):
        self.setPixmap(QPixmap(file_path))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = UI()
    sys.exit(app.exec_())

关键修改说明

  • 将DragAndDropAudio的父类从QLabel改为QWidget,新增内部布局承载matplotlib画布,画布只会在当前控件的500*500区域内展示,不会挤压其他控件
  • 删除了原代码中ex.layout.addWidget(canvas)的错误调用,改为将画布添加到音频控件自身的内部布局中
  • 优化了音频、图片的拖拽校验逻辑,只接受对应格式的文件
  • 简化了onChanged方法逻辑,删除重复代码
  • 优化了图片展示的缩放逻辑,避免图片超出控件范围

内容的提问来源于stack exchange,提问作者Redoxsphere

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 09:18:01