我的 PyQt 图的 Y 轴是颠倒的(甚至是文本)?
My PyQt plot's Y axes are upside down (even the text)?
今天是我尝试使用 PyQtGraph 的第一天。到目前为止我真的很喜欢它,除了我似乎无法完全理解它是如何工作的..
我正在尝试将两个 FFT 绘图小部件放入同一个 window。经过多次试验和错误后,我发现我认为是正确的方法。但是现在我有两个显示正确信息的图,但是 Y 轴上的所有内容都反转了。
而且缩放和平移似乎也不正确(整个图移动,而不仅仅是其中的数据)。
此图显示了单个 GraphicsWindow 中的两个实时音频 fft 图。在左侧,我将 addPlot 与 addItem 结合使用,而在右侧,我将 addViewBox 与 addItem 结合使用。
为了彻底,我尝试使用 item.invertY(True) 和 item.scale(1,-1)。
在这两种情况下,它都会反转 Y 轴数据而不是文本或轴,也不会解决 panning/zooming 问题..
这个 Python 脚本是我能写的全部。
它基于此文件:pyqtgraph live running spectrogram from microphone
import numpy as np
import pyqtgraph as pg
import pyaudio
from PyQt4 import QtCore, QtGui
FS = 44100 #Hz
CHUNKSZ = 1024 #samples
class MicrophoneRecorder():
def __init__(self, signal):
self.signal = signal
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paInt16,
channels=1,
rate=FS,
input=True,
frames_per_buffer=CHUNKSZ)
def read(self):
data = self.stream.read(CHUNKSZ)
y = np.fromstring(data, 'int16')
self.signal.emit(y)
def close(self):
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
class SpectrogramWidget2(pg.PlotWidget):
read_collected = QtCore.pyqtSignal(np.ndarray)
def __init__(self):
super(SpectrogramWidget2, self).__init__()
self.img = pg.ImageItem()
self.addItem(self.img)
self.img_array = np.zeros((1000, CHUNKSZ/2+1))
# bipolar colormap
pos = np.array([0., 0.5, 1.])
color = np.array([[0,0,0,255], [0,255,0,255], [255,0,0,255]], dtype=np.ubyte)
cmap = pg.ColorMap(pos, color)
pg.colormap
lut = cmap.getLookupTable(0.0, 1.0, 256)
# set colormap
self.img.setLookupTable(lut)
self.img.setLevels([0,100])
# setup the correct scaling for y-axis
freq = np.arange((CHUNKSZ/2)+1)/(float(CHUNKSZ)/FS)
yscale = 1.0/(self.img_array.shape[1]/freq[-1])
self.img.scale((1./FS)*CHUNKSZ, yscale)
self.setLabel('left', 'Frequency', units='Hz')
# prepare window for later use
self.win = np.hanning(CHUNKSZ)
#self.show()
def update(self, chunk):
# normalized, windowed frequencies in data chunk
spec = np.fft.rfft(chunk*self.win) / CHUNKSZ
# get magnitude
psd = abs(spec)
# convert to dB scaleaxis
psd = 20 * np.log10(psd)
# roll down one and replace leading edge with new data
self.img_array = np.roll(self.img_array, -1, 0)
self.img_array[-1:] = psd
self.img.setImage(self.img_array, autoLevels=False)
class SpectrogramWidget(pg.PlotWidget):
read_collected = QtCore.pyqtSignal(np.ndarray)
def __init__(self):
super(SpectrogramWidget, self).__init__()
self.img = pg.ImageItem()
self.addItem(self.img)
self.img_array = np.zeros((1000, CHUNKSZ/2+1))
# bipolar colormap
pos = np.array([0., 0.5, 1.])
color = np.array([[0,0,0,255], [0,255,0,255], [255,0,0,255]], dtype=np.ubyte)
cmap = pg.ColorMap(pos, color)
pg.colormap
lut = cmap.getLookupTable(0.0, 1.0, 256)
# set colormap
self.img.setLookupTable(lut)
self.img.setLevels([0,100])
# setup the correct scaling for y-axis
freq = np.arange((CHUNKSZ/2)+1)/(float(CHUNKSZ)/FS)
yscale = 1.0/(self.img_array.shape[1]/freq[-1])
self.img.scale((1./FS)*CHUNKSZ, yscale)
self.setLabel('left', 'Frequency', units='Hz')
# prepare window for later use
self.win = np.hanning(CHUNKSZ)
#self.show()
def update(self, chunk):
# normalized, windowed frequencies in data chunk
spec = np.fft.rfft(chunk*self.win) / CHUNKSZ
# get magnitude
psd = abs(spec)
# convert to dB scaleaxis
psd = 20 * np.log10(psd)
# roll down one and replace leading edge with new data
self.img_array = np.roll(self.img_array, -1, 0)
self.img_array[-1:] = psd
self.img.setImage(self.img_array, autoLevels=False)
if __name__ == '__main__':
app = QtGui.QApplication([])
win = pg.GraphicsWindow(title="Basic plotting examples")
#win.resize(1000,600)
w = SpectrogramWidget()
w.read_collected.connect(w.update)
spectrum1 = win.addPlot(title="Spectrum 1")#win.addViewBox()
item = w.getPlotItem()
spectrum1.addItem(item)
w2 = SpectrogramWidget2()
w2.read_collected.connect(w2.update)
spectrum2 = win.addViewBox()
spectrum2.addItem(w2.getPlotItem())
mic = MicrophoneRecorder(w.read_collected)
mic2 = MicrophoneRecorder(w2.read_collected)
# time (seconds) between reads
interval = FS/CHUNKSZ
t = QtCore.QTimer()
t.timeout.connect(mic.read)
t.start((1000/interval) ) #QTimer takes ms
t2 = QtCore.QTimer()
t2.timeout.connect(mic2.read)
t2.start((1000/interval) ) #QTimer takes ms
app.exec_()
mic.close()
感谢您的帮助!
我不知道为什么这样做会导致事情被镜像,但问题与在另一个情节中使用情节中的 plotItem 有关(我想这就是你的在做什么?)
无论如何,PlotWidget
s 不应该那样使用。它们只是普通的 Qt 小部件,所以将它们添加到 Qt 布局中就像添加任何其他 Qt 小部件一样。
if __name__ == '__main__':
app = QtGui.QApplication([])
win = QtGui.QMainWindow()
widget = QtGui.QWidget()
win.setCentralWidget(widget)
layout = QtGui.QHBoxLayout(widget)
win.show()
w = SpectrogramWidget()
w.read_collected.connect(w.update)
layout.addWidget(w)
w2 = SpectrogramWidget2()
w2.read_collected.connect(w2.update)
layout.addWidget(w2)
# .... etc
P.S。你有两个相同的 classes 但名字不同是有原因的吗?您可以实例化相同 class 的多个副本。例如
w = SpectrogramWidget()
w2 = SpectrogramWidget()
今天是我尝试使用 PyQtGraph 的第一天。到目前为止我真的很喜欢它,除了我似乎无法完全理解它是如何工作的..
我正在尝试将两个 FFT 绘图小部件放入同一个 window。经过多次试验和错误后,我发现我认为是正确的方法。但是现在我有两个显示正确信息的图,但是 Y 轴上的所有内容都反转了。
而且缩放和平移似乎也不正确(整个图移动,而不仅仅是其中的数据)。
此图显示了单个 GraphicsWindow 中的两个实时音频 fft 图。在左侧,我将 addPlot 与 addItem 结合使用,而在右侧,我将 addViewBox 与 addItem 结合使用。
为了彻底,我尝试使用 item.invertY(True) 和 item.scale(1,-1)。 在这两种情况下,它都会反转 Y 轴数据而不是文本或轴,也不会解决 panning/zooming 问题..
这个 Python 脚本是我能写的全部。
它基于此文件:pyqtgraph live running spectrogram from microphone
import numpy as np
import pyqtgraph as pg
import pyaudio
from PyQt4 import QtCore, QtGui
FS = 44100 #Hz
CHUNKSZ = 1024 #samples
class MicrophoneRecorder():
def __init__(self, signal):
self.signal = signal
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paInt16,
channels=1,
rate=FS,
input=True,
frames_per_buffer=CHUNKSZ)
def read(self):
data = self.stream.read(CHUNKSZ)
y = np.fromstring(data, 'int16')
self.signal.emit(y)
def close(self):
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
class SpectrogramWidget2(pg.PlotWidget):
read_collected = QtCore.pyqtSignal(np.ndarray)
def __init__(self):
super(SpectrogramWidget2, self).__init__()
self.img = pg.ImageItem()
self.addItem(self.img)
self.img_array = np.zeros((1000, CHUNKSZ/2+1))
# bipolar colormap
pos = np.array([0., 0.5, 1.])
color = np.array([[0,0,0,255], [0,255,0,255], [255,0,0,255]], dtype=np.ubyte)
cmap = pg.ColorMap(pos, color)
pg.colormap
lut = cmap.getLookupTable(0.0, 1.0, 256)
# set colormap
self.img.setLookupTable(lut)
self.img.setLevels([0,100])
# setup the correct scaling for y-axis
freq = np.arange((CHUNKSZ/2)+1)/(float(CHUNKSZ)/FS)
yscale = 1.0/(self.img_array.shape[1]/freq[-1])
self.img.scale((1./FS)*CHUNKSZ, yscale)
self.setLabel('left', 'Frequency', units='Hz')
# prepare window for later use
self.win = np.hanning(CHUNKSZ)
#self.show()
def update(self, chunk):
# normalized, windowed frequencies in data chunk
spec = np.fft.rfft(chunk*self.win) / CHUNKSZ
# get magnitude
psd = abs(spec)
# convert to dB scaleaxis
psd = 20 * np.log10(psd)
# roll down one and replace leading edge with new data
self.img_array = np.roll(self.img_array, -1, 0)
self.img_array[-1:] = psd
self.img.setImage(self.img_array, autoLevels=False)
class SpectrogramWidget(pg.PlotWidget):
read_collected = QtCore.pyqtSignal(np.ndarray)
def __init__(self):
super(SpectrogramWidget, self).__init__()
self.img = pg.ImageItem()
self.addItem(self.img)
self.img_array = np.zeros((1000, CHUNKSZ/2+1))
# bipolar colormap
pos = np.array([0., 0.5, 1.])
color = np.array([[0,0,0,255], [0,255,0,255], [255,0,0,255]], dtype=np.ubyte)
cmap = pg.ColorMap(pos, color)
pg.colormap
lut = cmap.getLookupTable(0.0, 1.0, 256)
# set colormap
self.img.setLookupTable(lut)
self.img.setLevels([0,100])
# setup the correct scaling for y-axis
freq = np.arange((CHUNKSZ/2)+1)/(float(CHUNKSZ)/FS)
yscale = 1.0/(self.img_array.shape[1]/freq[-1])
self.img.scale((1./FS)*CHUNKSZ, yscale)
self.setLabel('left', 'Frequency', units='Hz')
# prepare window for later use
self.win = np.hanning(CHUNKSZ)
#self.show()
def update(self, chunk):
# normalized, windowed frequencies in data chunk
spec = np.fft.rfft(chunk*self.win) / CHUNKSZ
# get magnitude
psd = abs(spec)
# convert to dB scaleaxis
psd = 20 * np.log10(psd)
# roll down one and replace leading edge with new data
self.img_array = np.roll(self.img_array, -1, 0)
self.img_array[-1:] = psd
self.img.setImage(self.img_array, autoLevels=False)
if __name__ == '__main__':
app = QtGui.QApplication([])
win = pg.GraphicsWindow(title="Basic plotting examples")
#win.resize(1000,600)
w = SpectrogramWidget()
w.read_collected.connect(w.update)
spectrum1 = win.addPlot(title="Spectrum 1")#win.addViewBox()
item = w.getPlotItem()
spectrum1.addItem(item)
w2 = SpectrogramWidget2()
w2.read_collected.connect(w2.update)
spectrum2 = win.addViewBox()
spectrum2.addItem(w2.getPlotItem())
mic = MicrophoneRecorder(w.read_collected)
mic2 = MicrophoneRecorder(w2.read_collected)
# time (seconds) between reads
interval = FS/CHUNKSZ
t = QtCore.QTimer()
t.timeout.connect(mic.read)
t.start((1000/interval) ) #QTimer takes ms
t2 = QtCore.QTimer()
t2.timeout.connect(mic2.read)
t2.start((1000/interval) ) #QTimer takes ms
app.exec_()
mic.close()
感谢您的帮助!
我不知道为什么这样做会导致事情被镜像,但问题与在另一个情节中使用情节中的 plotItem 有关(我想这就是你的在做什么?)
无论如何,PlotWidget
s 不应该那样使用。它们只是普通的 Qt 小部件,所以将它们添加到 Qt 布局中就像添加任何其他 Qt 小部件一样。
if __name__ == '__main__':
app = QtGui.QApplication([])
win = QtGui.QMainWindow()
widget = QtGui.QWidget()
win.setCentralWidget(widget)
layout = QtGui.QHBoxLayout(widget)
win.show()
w = SpectrogramWidget()
w.read_collected.connect(w.update)
layout.addWidget(w)
w2 = SpectrogramWidget2()
w2.read_collected.connect(w2.update)
layout.addWidget(w2)
# .... etc
P.S。你有两个相同的 classes 但名字不同是有原因的吗?您可以实例化相同 class 的多个副本。例如
w = SpectrogramWidget()
w2 = SpectrogramWidget()