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
|
||||
"""
|
||||
|
||||
def __init__(self, fs, level):
|
||||
"""
|
||||
Initialize Reverberation time computer.
|
||||
|
@ -12,7 +12,7 @@ from .lasp_common import (FreqWeighting, sens, calfile,
|
||||
TimeWeighting, getTime, P_REF)
|
||||
from .lasp_weighcal import WeighCal
|
||||
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
|
||||
|
||||
__all__ = ['SLM', 'SlmWidget']
|
||||
@ -96,16 +96,25 @@ class SLM:
|
||||
|
||||
|
||||
class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
||||
def __init__(self):
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Initialize the SlmWidget.
|
||||
"""
|
||||
super().__init__()
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
FreqWeighting.fillComboBox(self.freqweighting)
|
||||
|
||||
FreqWeighting.fillComboBox(self.tfreqweighting)
|
||||
FreqWeighting.fillComboBox(self.eqfreqweighting)
|
||||
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):
|
||||
"""
|
||||
Set the current measurement for this widget.
|
||||
@ -119,27 +128,64 @@ class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
||||
else:
|
||||
self.setEnabled(True)
|
||||
rt = meas.recTime
|
||||
self.starttime.setRange(0, rt, 0)
|
||||
self.stoptime.setRange(0, rt, rt)
|
||||
self.tstarttime.setRange(0, rt, 0)
|
||||
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):
|
||||
self.channel.addItem(str(i))
|
||||
self.channel.setCurrentIndex(0)
|
||||
self.tchannel.addItem(str(i))
|
||||
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
|
||||
called whenever the Compute button is pushed in the SLM tab
|
||||
Compute equivalent levels for a piece of time
|
||||
"""
|
||||
meas = self.meas
|
||||
fs = meas.samplerate
|
||||
channel = self.channel.currentIndex()
|
||||
tw = TimeWeighting.getCurrent(self.timeweighting)
|
||||
fw = FreqWeighting.getCurrent(self.freqweighting)
|
||||
channel = self.eqchannel.currentIndex()
|
||||
fw = FreqWeighting.getCurrent(self.eqfreqweighting)
|
||||
|
||||
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
|
||||
dsf = self.downsampling.value()
|
||||
dsf = self.tdownsampling.value()
|
||||
# gb = self.slmFre
|
||||
|
||||
with wait_cursor():
|
||||
@ -162,8 +208,11 @@ class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
||||
pto = PlotOptions()
|
||||
pto.ylabel = f'L{fw[0]} [dB({fw[0]})]'
|
||||
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()
|
||||
|
||||
stats = f"""Statistical results:
|
||||
@ -175,3 +224,13 @@ Maximum level (L{fw[0]} max): {Lmax:4.4} [dB({fw[0]})]
|
||||
|
||||
"""
|
||||
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