Last active
June 20, 2020 19:12
-
-
Save kylerbrown/5688849 to your computer and use it in GitHub Desktop.
A very rough sketch of a hdf5 acoustic data viewer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""an alpha version of the plotter""" | |
import signal | |
import sys | |
import pyqtgraph as pg | |
import pyqtgraph.dockarea as pgd | |
import h5py | |
import numpy as np | |
from PyQt4 import QtGui, QtCore | |
from PyQt4.phonon import Phonon | |
from matplotlib.mlab import specgram | |
from scipy.io import wavfile | |
class MainWindow(QtGui.QMainWindow): | |
'''the main window of the program''' | |
def __init__(self): | |
super(MainWindow, self).__init__() | |
self.current_file = None | |
self.initUI() | |
def initUI(self): | |
""""Assembles the basic Gui layout, status bar, menubar | |
toolbar etc.""" | |
# status bar | |
self.statusBar().showMessage('Ready') | |
# actions | |
soundAction = QtGui.QAction(QtGui.QIcon('icons/sound.svg'), | |
'PlaySound', self) | |
soundAction.setShortcut('Ctrl+S') | |
soundAction.setStatusTip('Play data as sound') | |
soundAction.triggered.connect(self.playSound) | |
exitAction = QtGui.\ | |
QAction(QtGui.QIcon('icons/system-shutdown-panel.svg'), | |
'Exit', self) | |
exitAction.setShortcut('Ctrl+Q') | |
exitAction.setStatusTip('Exit application') | |
exitAction.triggered.connect(QtGui.qApp.quit) | |
openAction = QtGui.\ | |
QAction(QtGui.QIcon('icons/document-open.svg'), 'Open', self) | |
openAction.setShortcut('Ctrl+O') | |
openAction.setStatusTip('Open an arf file') | |
openAction.triggered.connect(self.showDialog) | |
# menubar | |
menubar = self.menuBar() | |
fileMenu = menubar.addMenu('&File') | |
fileMenu.addAction(exitAction) | |
fileMenu.addAction(openAction) | |
# toolbar | |
self.toolbar = self.addToolBar('Toolbar') | |
self.toolbar.addAction(exitAction) | |
self.toolbar.addAction(openAction) | |
self.toolbar.addAction(soundAction) | |
# file tree | |
self.tree_view = QtGui.QTreeWidget() | |
self.tree_view.currentItemChanged.connect(self.plotData) | |
if self.current_file: | |
self.populateTree() | |
#attribute table | |
self.attr_table = QtGui.QTableWidget(10, 2) | |
#plot region | |
self.data_layout = pg.GraphicsLayoutWidget() | |
# final steps | |
self.area = pgd.DockArea() | |
tree_dock = pgd.Dock("Tree", size=(1, 1)) | |
data_dock = pgd.Dock("Data", size=(500, 200)) | |
attr_table_dock = pgd.Dock("Attributes", size=(1, 1)) | |
self.area.addDock(tree_dock, 'left') | |
self.area.addDock(data_dock, 'right') | |
self.area.addDock(attr_table_dock, 'bottom', tree_dock) | |
tree_dock.addWidget(self.tree_view) | |
data_dock.addWidget(self.data_layout) | |
attr_table_dock.addWidget(self.attr_table) | |
self.setCentralWidget(self.area) | |
self.setWindowTitle('ooplot') | |
self.resize(1000, 500) | |
self.show() | |
def showDialog(self): | |
fname = QtGui.QFileDialog.\ | |
getOpenFileName(self, 'Open file', '.', | |
'*.arf ;; *.hdf5, *.h5 ;; *.mat') | |
print("%s opened" % (fname)) | |
self.statusBar().showMessage("%s opened" % (fname)) | |
self.current_file = h5py.File(str(fname)) | |
self.populateTree() | |
def populateTree(self): | |
f = self.current_file | |
def recursivePopulateTree(parent_node, data): | |
tree_node = QtGui.QTreeWidgetItem([data.name]) | |
tree_node.setData(0, QtCore.Qt.UserRole, data) | |
parent_node.addChild(tree_node) | |
if type(data) == h5py._hl.group.Group: | |
for item in data.itervalues(): | |
recursivePopulateTree(tree_node, item) | |
# add root | |
topnode = QtGui.QTreeWidgetItem([f.filename]) | |
root = f["/"] | |
topnode.setData(0, QtCore.Qt.UserRole, root) | |
self.tree_view.addTopLevelItem(topnode) | |
for item in root.itervalues(): | |
recursivePopulateTree(topnode, item) | |
def plotDataset(self, dataset, irow, kind='osc'): | |
#datatype = dataset.attrs['datatype'] | |
sr = float(dataset.attrs['sampling_rate']) | |
t = np.arange(0, len(dataset)/sr, 1/sr) | |
if kind == 'osc': | |
pl = self.data_layout.addPlot(title=dataset.name, | |
name=str(irow), row=irow, col=0) | |
pl.plot(t, dataset) | |
pl.showGrid(x=True, y=True) | |
if kind == 'spec': | |
Pxx, freqs, ts = specgram(dataset, Fs=sr, NFFT=512, noverlap=400) | |
img = pg.ImageItem(np.log(Pxx.T)) | |
img.setLevels((-5, 10)) | |
img.setScale(ts[-1] / Pxx.shape[1]) | |
vb = self.data_layout.addViewBox(name=str(irow), row=irow, col=0) | |
g = pg.GridItem() | |
vb.addItem(g) | |
vb.addItem(img) | |
vb.setMouseEnabled(x=True, y=False) | |
return vb, img | |
return pl | |
def plotData(self, treeItem): | |
item = treeItem.data(0, QtCore.Qt.UserRole).toPyObject() | |
populateAttrTable(self.attr_table, item) | |
self.data_layout.clear() | |
self.masterYLink = None | |
if type(item) == h5py._hl.group.Group: | |
for node in item.itervalues(): | |
if canPlot(node): | |
if node.attrs['datatype'] == 1: # acoustic | |
# plot both oscilogram and spectrogram | |
song_spec_vb, song_spec_img = \ | |
self.plotDataset(node, 0, kind='spec') | |
song_osc = self.plotDataset(node, 1, kind='osc') | |
self.masterXLink = song_osc | |
song_spec_vb.setXLink(self.masterXLink) | |
for node in item.itervalues(): | |
i = 2 # collumn for plot | |
if canPlot(node): | |
if node.attrs['datatype'] == 2: # neural | |
# plot both oscilogram and spectrogram | |
n_osc = self.plotDataset(node, i, kind='osc') | |
i += 1 | |
if self.masterXLink: | |
n_osc.setXLink(song_osc) | |
elif canPlot(item): | |
self.plotDataset(item, 0) | |
def playSound(self): | |
data = self.tree_view.currentItem()\ | |
.data(0, QtCore.Qt.UserRole).toPyObject() | |
print('writing wav file') | |
if canPlot(data): | |
wavfile.write('temp.wav', data.attrs['sampling_rate'], | |
np.array(data)) | |
if QtGui.QSound.isAvailable(): | |
QtGui.QSound.play('temp.wav') | |
else: | |
from soundplayer import SoundWindow | |
#soundapp = QtGui.QApplication([]) | |
#soundapp.setApplicationName('Phonon') | |
win = SoundWindow() | |
win.show() | |
#sys.exit(soundapp.exec_()) | |
print('trying Phonon') | |
m = Phonon.MediaSource("temp.wav") | |
obj = Phonon.createPlayer(Phonon.MusicCategory, m) | |
obj.play() | |
print(obj) | |
#source = Phonon.MediaSource("temp.wav") | |
#player = Phonon.createPlayer(Phonon.MusicCategory, source) | |
#player.play() | |
print('playSound done') | |
# music = pyglet.resource.media('temp.wav') | |
# music.play() | |
# pyglet.app.run() | |
# sound = QtGui.QSound('temp.wav') | |
# sound.play() | |
# output = Phonon.AudioOutput(Phonon.MusicCategory) | |
# m_media = Phonon.MediaObject() | |
# Phonon.createPath(m_media, output) | |
# m_media.setCurrentSource(Phonon.MediaSource("temp.wav")) | |
# m_media.play() | |
def populateAttrTable(table, item): | |
"""Populate QTableWidget with attribute values of hdf5 item ITEM""" | |
table.setRowCount(len(item.attrs.keys())) | |
for row, (key, value) in enumerate(item.attrs.iteritems()): | |
attribute = QtGui.QTableWidgetItem(str(key)) | |
attribute_value = QtGui.QTableWidgetItem(str(value)) | |
table.setItem(row, 0, attribute) | |
table.setItem(row, 1, attribute_value) | |
def canPlot(dataset): | |
try: | |
iter(dataset) | |
if len(dataset) > 0 and 'datatype' in dataset.attrs.keys(): | |
return True | |
except TypeError: | |
print(dataset, ' is not iterable') | |
return False | |
def sigint_handler(*args): | |
"""Handler for the SIGINT signal.""" | |
sys.stderr.write('\r') | |
QtGui.QApplication.quit() | |
def main(): | |
signal.signal(signal.SIGINT, sigint_handler) | |
app = QtGui.QApplication(sys.argv) | |
app.setApplicationName('ooplot') | |
timer = QtCore.QTimer() | |
timer.start(500) # You may change this if you wish. | |
timer.timeout.connect(lambda: None) # Let the interpreter run each 500 ms. | |
mainWin = MainWindow() | |
sys.exit(app.exec_()) | |
#if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): | |
# QtGui.QApplication.instance().exec_() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment