Added bar plots, different figures, and lots of things have been changed
This commit is contained in:
parent
3357a0ded6
commit
475f2fa6aa
@ -13,6 +13,7 @@ class ReverbTime:
|
|||||||
"""
|
"""
|
||||||
Tool to estimate the reverberation time
|
Tool to estimate the reverberation time
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, fs, level):
|
def __init__(self, fs, level):
|
||||||
"""
|
"""
|
||||||
Initialize Reverberation time computer.
|
Initialize Reverberation time computer.
|
||||||
|
@ -12,7 +12,7 @@ from .lasp_common import (FreqWeighting, sens, calfile,
|
|||||||
TimeWeighting, getTime, P_REF)
|
TimeWeighting, getTime, P_REF)
|
||||||
from .lasp_weighcal import WeighCal
|
from .lasp_weighcal import WeighCal
|
||||||
from .lasp_gui_tools import wait_cursor
|
from .lasp_gui_tools import wait_cursor
|
||||||
from .lasp_figure import FigureDialog, PlotOptions
|
from .lasp_figure import PlotOptions, Plotable
|
||||||
from .ui_slmwidget import Ui_SlmWidget
|
from .ui_slmwidget import Ui_SlmWidget
|
||||||
|
|
||||||
__all__ = ['SLM', 'SlmWidget']
|
__all__ = ['SLM', 'SlmWidget']
|
||||||
@ -96,16 +96,25 @@ class SLM:
|
|||||||
|
|
||||||
|
|
||||||
class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
||||||
def __init__(self):
|
def __init__(self, parent=None):
|
||||||
"""
|
"""
|
||||||
Initialize the SlmWidget.
|
Initialize the SlmWidget.
|
||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__(parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
FreqWeighting.fillComboBox(self.freqweighting)
|
|
||||||
|
|
||||||
|
FreqWeighting.fillComboBox(self.tfreqweighting)
|
||||||
|
FreqWeighting.fillComboBox(self.eqfreqweighting)
|
||||||
self.setMeas(None)
|
self.setMeas(None)
|
||||||
|
|
||||||
|
def init(self, fm):
|
||||||
|
"""
|
||||||
|
Register combobox of the figure dialog to plot to in the FigureManager
|
||||||
|
"""
|
||||||
|
super().init(fm)
|
||||||
|
fm.registerCombo(self.tfigure)
|
||||||
|
fm.registerCombo(self.eqfigure)
|
||||||
|
|
||||||
def setMeas(self, meas):
|
def setMeas(self, meas):
|
||||||
"""
|
"""
|
||||||
Set the current measurement for this widget.
|
Set the current measurement for this widget.
|
||||||
@ -119,27 +128,64 @@ class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
|||||||
else:
|
else:
|
||||||
self.setEnabled(True)
|
self.setEnabled(True)
|
||||||
rt = meas.recTime
|
rt = meas.recTime
|
||||||
self.starttime.setRange(0, rt, 0)
|
self.tstarttime.setRange(0, rt, 0)
|
||||||
self.stoptime.setRange(0, rt, rt)
|
self.tstoptime.setRange(0, rt, rt)
|
||||||
|
|
||||||
self.channel.clear()
|
self.eqstarttime.setRange(0, rt, 0)
|
||||||
|
self.eqstoptime.setRange(0, rt, rt)
|
||||||
|
|
||||||
|
self.tchannel.clear()
|
||||||
|
self.eqchannel.clear()
|
||||||
for i in range(meas.nchannels):
|
for i in range(meas.nchannels):
|
||||||
self.channel.addItem(str(i))
|
self.tchannel.addItem(str(i))
|
||||||
self.channel.setCurrentIndex(0)
|
self.eqchannel.addItem(str(i))
|
||||||
|
self.tchannel.setCurrentIndex(0)
|
||||||
|
self.eqchannel.setCurrentIndex(0)
|
||||||
|
|
||||||
def compute(self):
|
def computeEq(self):
|
||||||
"""
|
"""
|
||||||
Compute Sound Level using settings. This method is
|
Compute equivalent levels for a piece of time
|
||||||
called whenever the Compute button is pushed in the SLM tab
|
|
||||||
"""
|
"""
|
||||||
meas = self.meas
|
meas = self.meas
|
||||||
fs = meas.samplerate
|
fs = meas.samplerate
|
||||||
channel = self.channel.currentIndex()
|
channel = self.eqchannel.currentIndex()
|
||||||
tw = TimeWeighting.getCurrent(self.timeweighting)
|
fw = FreqWeighting.getCurrent(self.eqfreqweighting)
|
||||||
fw = FreqWeighting.getCurrent(self.freqweighting)
|
|
||||||
|
startpos = self.eqstarttime.value
|
||||||
|
stoppos = self.eqstoptime.value
|
||||||
|
N = meas.N
|
||||||
|
istart = int(startpos*fs)
|
||||||
|
if istart >= N:
|
||||||
|
raise ValueError("Invalid start position")
|
||||||
|
istop = int(stoppos*fs)
|
||||||
|
if istart > N:
|
||||||
|
raise ValueError("Invalid stop position")
|
||||||
|
|
||||||
|
|
||||||
|
with wait_cursor():
|
||||||
|
# This one exctracts the calfile and sensitivity from global
|
||||||
|
# variables defined at the top. # TODO: Change this to a more
|
||||||
|
# robust variant.
|
||||||
|
weighcal = WeighCal(fw, nchannels=1,
|
||||||
|
fs=fs, calfile=calfile,
|
||||||
|
sens=sens)
|
||||||
|
praw = meas.praw()[istart:istop, [channel]]
|
||||||
|
pto = PlotOptions()
|
||||||
|
fig, new = self.getFigure(self.eqfigure, pto, 'bar')
|
||||||
|
fig.show()
|
||||||
|
|
||||||
|
def computeT(self):
|
||||||
|
"""
|
||||||
|
Compute sound levels as a function of time.
|
||||||
|
"""
|
||||||
|
meas = self.meas
|
||||||
|
fs = meas.samplerate
|
||||||
|
channel = self.tchannel.currentIndex()
|
||||||
|
tw = TimeWeighting.getCurrent(self.ttimeweighting)
|
||||||
|
fw = FreqWeighting.getCurrent(self.tfreqweighting)
|
||||||
|
|
||||||
# Downsampling factor of result
|
# Downsampling factor of result
|
||||||
dsf = self.downsampling.value()
|
dsf = self.tdownsampling.value()
|
||||||
# gb = self.slmFre
|
# gb = self.slmFre
|
||||||
|
|
||||||
with wait_cursor():
|
with wait_cursor():
|
||||||
@ -162,8 +208,11 @@ class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
|||||||
pto = PlotOptions()
|
pto = PlotOptions()
|
||||||
pto.ylabel = f'L{fw[0]} [dB({fw[0]})]'
|
pto.ylabel = f'L{fw[0]} [dB({fw[0]})]'
|
||||||
pto.xlim = (time[0], time[-1])
|
pto.xlim = (time[0], time[-1])
|
||||||
fig, new = self.getFigure(pto)
|
|
||||||
fig.fig.plot(time, filtered)
|
pta = Plotable(time, filtered)
|
||||||
|
|
||||||
|
fig, new = self.getFigure(self.tfigure, pto, 'line')
|
||||||
|
fig.fig.add(pta)
|
||||||
fig.show()
|
fig.show()
|
||||||
|
|
||||||
stats = f"""Statistical results:
|
stats = f"""Statistical results:
|
||||||
@ -175,3 +224,13 @@ Maximum level (L{fw[0]} max): {Lmax:4.4} [dB({fw[0]})]
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self.results.setPlainText(stats)
|
self.results.setPlainText(stats)
|
||||||
|
|
||||||
|
def compute(self):
|
||||||
|
"""
|
||||||
|
Compute Sound Level using settings. This method is
|
||||||
|
called whenever the Compute button is pushed in the SLM tab
|
||||||
|
"""
|
||||||
|
if self.ttab.isVisible():
|
||||||
|
self.computeT()
|
||||||
|
elif self.eqtab.isVisible():
|
||||||
|
self.computeEq()
|
||||||
|
3
lasp/plot/__init__.py
Normal file
3
lasp/plot/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from .bar import BarScene
|
307
lasp/plot/bar.py
Normal file
307
lasp/plot/bar.py
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Author: J.A. de Jong - ASCEE
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Class for plotting bars on a QGraphicsScene.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from ..lasp_gui_tools import ASCEEColors
|
||||||
|
from PySide.QtGui import (
|
||||||
|
QGraphicsScene, QGraphicsView, QPen, QBrush, QGraphicsRectItem,
|
||||||
|
QGraphicsTextItem, QPainter, QImage
|
||||||
|
)
|
||||||
|
|
||||||
|
# from PySide.QtOpenGL import
|
||||||
|
from PySide.QtCore import Qt, QRectF, QLineF, QSize, QRect, QPointF
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
leftoffset = .1 # Left offset of the figure
|
||||||
|
rightoffset = 0
|
||||||
|
topoffset = .05
|
||||||
|
bottomoffset = .1
|
||||||
|
nyticks = 6
|
||||||
|
ticklength = .01
|
||||||
|
|
||||||
|
# Distance between two bar groups in units of bar thicknesses
|
||||||
|
dxbars = 2
|
||||||
|
|
||||||
|
DEFAULT_COLORS = [ASCEEColors.blue, ASCEEColors.green, Qt.red]
|
||||||
|
|
||||||
|
|
||||||
|
class BarScene(QGraphicsScene):
|
||||||
|
"""
|
||||||
|
Graphhics Scene for plotting bars
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent, xvals, G, ylim=(0, 1),
|
||||||
|
grid=True,
|
||||||
|
xlabel=None,
|
||||||
|
ylabel=None,
|
||||||
|
title=None,
|
||||||
|
colors=DEFAULT_COLORS, size=(800, 600),
|
||||||
|
legend=None):
|
||||||
|
"""
|
||||||
|
Initialize a bar scene
|
||||||
|
|
||||||
|
Args:
|
||||||
|
xvals: labels and x positions of the bars
|
||||||
|
G: Number of bars per x value
|
||||||
|
ylim: y limits of the figure
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
# self.setBackgroundBrush(ASCEEColors.bgBrush(0, size[0]))
|
||||||
|
self.ylim = ylim
|
||||||
|
N = len(xvals)
|
||||||
|
self.N = N
|
||||||
|
self.G = G
|
||||||
|
self.bgs = []
|
||||||
|
|
||||||
|
self.size = size
|
||||||
|
self.colors = colors
|
||||||
|
|
||||||
|
# Size of the frame
|
||||||
|
Lx = 1 - rightoffset - leftoffset
|
||||||
|
Ly = 1 - topoffset - bottomoffset
|
||||||
|
|
||||||
|
# The main frame where the bars are in.
|
||||||
|
mainframe = self.createRect(leftoffset,
|
||||||
|
bottomoffset,
|
||||||
|
Lx,
|
||||||
|
Ly)
|
||||||
|
self.addItem(mainframe)
|
||||||
|
|
||||||
|
# Set the y ticks and ticklabels
|
||||||
|
self.yticks = []
|
||||||
|
txtmaxwidth = 0
|
||||||
|
for i in range(nyticks):
|
||||||
|
y = bottomoffset+Ly*i/(nyticks-1)
|
||||||
|
|
||||||
|
ytick = self.addLine(leftoffset,
|
||||||
|
y,
|
||||||
|
leftoffset-ticklength,
|
||||||
|
y)
|
||||||
|
if grid:
|
||||||
|
ygrid = self.addLine(leftoffset,
|
||||||
|
y,
|
||||||
|
1-rightoffset,
|
||||||
|
y,pen=QPen(Qt.gray))
|
||||||
|
|
||||||
|
range_ = ylim[1]-ylim[0]
|
||||||
|
ytickval = i/(nyticks-1)*range_ + ylim[0]
|
||||||
|
yticklabel = f'{ytickval:2}'
|
||||||
|
txt = QGraphicsTextItem(yticklabel)
|
||||||
|
txtwidth = txt.boundingRect().width()
|
||||||
|
txtmaxwidth = max(txtmaxwidth, txtwidth)
|
||||||
|
txt.setPos((leftoffset-.03)*self.xscale-txtwidth,
|
||||||
|
(1-y-.022)*self.yscale)
|
||||||
|
self.addItem(txt)
|
||||||
|
self.yticks.append(ytick)
|
||||||
|
|
||||||
|
# Create the bars
|
||||||
|
for g in range(G):
|
||||||
|
bg = []
|
||||||
|
for n in range(N):
|
||||||
|
barrect = self.getBarRect(n, g, 0)
|
||||||
|
baritem = QGraphicsRectItem(barrect, brush=QBrush(Qt.blue))
|
||||||
|
|
||||||
|
self.addItem(baritem)
|
||||||
|
bg.append(baritem)
|
||||||
|
|
||||||
|
self.bgs.append(bg)
|
||||||
|
|
||||||
|
# Add x ticks and ticklabels
|
||||||
|
for n in range(N):
|
||||||
|
xticklabel = f'{xvals[n]}'
|
||||||
|
txt = QGraphicsTextItem(xticklabel)
|
||||||
|
txtxpos = self.getBarGroupMidPos(n)-0.01*self.xscale
|
||||||
|
txt.setPos(txtxpos,
|
||||||
|
self.yscale*(1-bottomoffset+.1))
|
||||||
|
txt.rotate(-90)
|
||||||
|
self.addItem(txt)
|
||||||
|
|
||||||
|
# Set xlabel
|
||||||
|
if xlabel is not None:
|
||||||
|
xlabel = QGraphicsTextItem(xlabel)
|
||||||
|
width = xlabel.boundingRect().width()
|
||||||
|
txtxpos = self.xscale/2-width/2
|
||||||
|
txtypos = .998*self.yscale
|
||||||
|
xlabel.setPos(txtxpos, txtypos)
|
||||||
|
self.addItem(xlabel)
|
||||||
|
|
||||||
|
# Set ylabel
|
||||||
|
if ylabel is not None:
|
||||||
|
ylabel = QGraphicsTextItem(ylabel)
|
||||||
|
ylabel.setPos((leftoffset-.01)*self.xscale-txtmaxwidth,
|
||||||
|
((1-topoffset-bottomoffset)/2+topoffset)*self.yscale)
|
||||||
|
ylabel.rotate(-90)
|
||||||
|
self.addItem(ylabel)
|
||||||
|
|
||||||
|
|
||||||
|
# Set title
|
||||||
|
if title is not None:
|
||||||
|
title = QGraphicsTextItem(title)
|
||||||
|
width = xlabel.boundingRect().width()
|
||||||
|
txtxpos = self.xscale/2-width/2
|
||||||
|
txtypos = (1-.998)*self.yscale
|
||||||
|
title.setPos(txtxpos, txtypos)
|
||||||
|
self.addItem(title)
|
||||||
|
|
||||||
|
legpos = (1-rightoffset-.3, 1-topoffset-.05)
|
||||||
|
|
||||||
|
dyleg = 0.03
|
||||||
|
dylegtxt = dyleg
|
||||||
|
Lyleg = .02
|
||||||
|
Lxleg = .05
|
||||||
|
legrectmarginpix = 5
|
||||||
|
boxtopleft = QPointF(legpos[0]*self.xscale-legrectmarginpix,
|
||||||
|
(1-legpos[1]-Lyleg)*self.yscale-legrectmarginpix)
|
||||||
|
|
||||||
|
|
||||||
|
if legend is not None:
|
||||||
|
nlegs = len(legend)
|
||||||
|
maxlegtxtwidth = 0
|
||||||
|
for i,leg in enumerate(legend):
|
||||||
|
leglabel = legend[i]
|
||||||
|
|
||||||
|
# The position of the legend, in our coordinates
|
||||||
|
pos = (legpos[0], legpos[1] - i*dyleg)
|
||||||
|
color = self.colors[i]
|
||||||
|
|
||||||
|
legrect = self.createRect(*pos,Lxleg,Lyleg)
|
||||||
|
|
||||||
|
legrectwidth = legrect.boundingRect().width()
|
||||||
|
|
||||||
|
legrect.setBrush(QBrush(color))
|
||||||
|
legtxt = QGraphicsTextItem(leglabel)
|
||||||
|
maxlegtxtwidth = max(maxlegtxtwidth,
|
||||||
|
legtxt.boundingRect().width())
|
||||||
|
|
||||||
|
self.addItem(legrect)
|
||||||
|
self.addItem(legtxt)
|
||||||
|
|
||||||
|
legtxt.setPos(legpos[0]*self.xscale+legrectwidth,
|
||||||
|
(1-pos[1]-dylegtxt)*self.yscale)
|
||||||
|
|
||||||
|
boxbottomright = legtxt.boundingRect().topRight()
|
||||||
|
|
||||||
|
legboxsize = QSize(maxlegtxtwidth+legrectwidth+2*legrectmarginpix,
|
||||||
|
(i+1)*dyleg*self.yscale+legrectmarginpix)
|
||||||
|
|
||||||
|
legboxrect = QRectF(boxtopleft,legboxsize)
|
||||||
|
legbox = self.addRect(legboxrect)
|
||||||
|
|
||||||
|
|
||||||
|
def saveAsPng(self, fn, force=False):
|
||||||
|
"""
|
||||||
|
Save bar image as a jpg file.
|
||||||
|
|
||||||
|
https://stackoverflow.com/questions/7451183/how-to-create-image-file\
|
||||||
|
-from-qgraphicsscene-qgraphicsview#11642517
|
||||||
|
|
||||||
|
"""
|
||||||
|
if os.path.exists(fn) and not force:
|
||||||
|
raise RuntimeError(f"File {fn} already exists in filesystem.")
|
||||||
|
|
||||||
|
# self.clearSelection()
|
||||||
|
image = QImage(*self.size,
|
||||||
|
QImage.Format_ARGB32_Premultiplied)
|
||||||
|
# image = QImage()
|
||||||
|
painter = QPainter(image)
|
||||||
|
# painter.begin()
|
||||||
|
painter.setRenderHint(QPainter.Antialiasing)
|
||||||
|
painter.setBrush(Qt.white)
|
||||||
|
painter.setPen(Qt.white)
|
||||||
|
painter.drawRect(QRect(0,0,*self.size))
|
||||||
|
|
||||||
|
targetrect = QRectF(0,0,*self.size)
|
||||||
|
sourcerect = QRectF(0,0,*self.size)
|
||||||
|
self.render(painter,targetrect,sourcerect)
|
||||||
|
painter.end()
|
||||||
|
# print('saving image')
|
||||||
|
image.save(fn)
|
||||||
|
|
||||||
|
def getBarGroupMidPos(self,n):
|
||||||
|
"""
|
||||||
|
Returns the mid x position below each bar group
|
||||||
|
"""
|
||||||
|
Lx = 1-rightoffset-leftoffset
|
||||||
|
Ly = 1 - topoffset - bottomoffset
|
||||||
|
|
||||||
|
start = .05
|
||||||
|
S = Lx - 2*start
|
||||||
|
L = S/(self.N*self.G+dxbars*(self.N-1))
|
||||||
|
xL = leftoffset+start
|
||||||
|
return (n*(self.G*L+dxbars*L) + xL + self.G*L/2)*self.xscale
|
||||||
|
|
||||||
|
def getBarRect(self, n, g, yval):
|
||||||
|
Lx = 1-rightoffset-leftoffset
|
||||||
|
Ly = 1 - topoffset - bottomoffset
|
||||||
|
|
||||||
|
start = .05
|
||||||
|
S = Lx - 2*start
|
||||||
|
L = S/(self.N*self.G+dxbars*(self.N-1))
|
||||||
|
xL = leftoffset+start
|
||||||
|
x = g*L + n*(self.G*L+dxbars*L) + xL
|
||||||
|
|
||||||
|
return QRectF(x*self.xscale,
|
||||||
|
(1-bottomoffset-yval*Ly)*self.yscale,
|
||||||
|
L*self.xscale,
|
||||||
|
yval*Ly*self.yscale)
|
||||||
|
|
||||||
|
def addLine(self, x1, y1, x2, y2, pen=QPen(), brush=QBrush()):
|
||||||
|
line = QLineF(x1*self.xscale,
|
||||||
|
(1-y1)*self.yscale,
|
||||||
|
(x2)*self.xscale,
|
||||||
|
(1-y2)*self.yscale)
|
||||||
|
return super().addLine(line, pen=pen, brush=brush)
|
||||||
|
|
||||||
|
def createRect(self, x, y, Lx, Ly, pen=QPen(), brush=QBrush()):
|
||||||
|
"""
|
||||||
|
Create a rectangle somewhere, in relative coordinates originating
|
||||||
|
from the lower left position.
|
||||||
|
"""
|
||||||
|
x1 = x
|
||||||
|
|
||||||
|
# Y-position from the top, these are the coordinates used to create a
|
||||||
|
# rect item.
|
||||||
|
y1 = 1-y-Ly
|
||||||
|
return QGraphicsRectItem(x1*self.xscale,
|
||||||
|
y1*self.yscale,
|
||||||
|
Lx*self.xscale,
|
||||||
|
Ly*self.yscale,
|
||||||
|
pen=pen,
|
||||||
|
brush=brush)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xscale(self):
|
||||||
|
return self.size[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def yscale(self):
|
||||||
|
return self.size[1]
|
||||||
|
|
||||||
|
def set_ydata(self, newydata):
|
||||||
|
G = len(self.bgs)
|
||||||
|
N = len(self.bgs[0])
|
||||||
|
|
||||||
|
assert newydata.shape[0] == N
|
||||||
|
assert newydata.shape[1] == G
|
||||||
|
|
||||||
|
# Crop values to be between 0 and 1
|
||||||
|
scalefac = self.ylim[1]-self.ylim[0]
|
||||||
|
yvals = np.clip(newydata, self.ylim[0], self.ylim[1])/scalefac
|
||||||
|
|
||||||
|
for g in range(G):
|
||||||
|
color = self.colors[g]
|
||||||
|
for n in range(N):
|
||||||
|
bar = self.bgs[g][n]
|
||||||
|
bar.setRect(self.getBarRect(n, g, yvals[n, g]))
|
||||||
|
bar.setBrush(color)
|
Loading…
Reference in New Issue
Block a user