-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathvisualizationTool.py
More file actions
415 lines (359 loc) · 18.5 KB
/
Copy pathvisualizationTool.py
File metadata and controls
415 lines (359 loc) · 18.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 19 14:18:53 2015
@author: Andrew
"""
import os
import sys
from PyQt4 import QtCore, QtGui
import qrc_resources
import dataController as DC
import copy
import fileNamingDlg as fnd
import numpy as np
import configDlg
__version__ = "2.0.0"
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
# """
# This represents the main window of the VisualizationTool (which doesn't yet have a real name).
# The main window holds several widgets, including: the data controller, specWidget, and
# sensorDataViewer. The dataController is the class governing the playing, pausing, fast-forwarding,
# rewinding, etc. The specWidget allows for a view of audio data's frequency content. The dataViewer
# shows the speed, accelaration and range for a given sensor, which is labeled at the top.
# See any of these for reference.
# Aside from the central dataController widget, there is a toolbar that allows for loading a file
# and changing the view of the dataController's figure. The views include, top, bottom, midsagittal
# and front.
# In version 1.5, there is another view, the 3D view, which allows for arbitrary views of the data to be
# seen. Using the mouse buttons and scroll buttons, you can look at the sensors from any angle. You can
# also zoom in and out using the scroll wheel on the mouse, and change the focus using by holding shift and
# moving the mouse in the desired direction.
# """
super(MainWindow, self).__init__(parent)
np.set_printoptions(precision=2, suppress=True)
self.audiofilename = None#"C:/Data/05_ENGL_F_words6.wav"
self.kinfilename = None#"C:/Data/05_ENGL_F_words6_BPC.tsv"
#We should go to QSettings to load the config list instead, but this will work for now.
#make qvariant to pyObject.. then make it a dict. When you save it, wrap it in a tuple container and unlock with [0]
settings = QtCore.QSettings()
self.configs = settings.value("savedConfig").toPyObject()
self.selectConfig()
# These are the parameters for the MU Speechlab
# {'RASS': (100, 5,('Upper Lip','Lower Lip', 'Tongue Blade','Tongue Dorsum','Medial Incisor'),('UL','LL','TB','TD','MI')),
# 'DDK': (100, 6,('Molar','Medial Incisor','Tongue Dorsum','Tongue Blade','Lower Lip','Upper Lip'),('MM','MI','TD','TB','LL','UL')),
# 'EMA-MAE': (400, 7,('Tongue Dorsum','Tongue Lateral','Tongue Blade','Upper Lip','Lower Lip','Lip Corner','Medial Incisor'),('TD','TL','TB','UL','LL','LC','MI'))}
self.dataController = DC.DataController(self.kinfilename,self.audiofilename, self.currentConfig)
self.dataController.setMinimumSize(700,600)
self.center()
# self.dataController.setAlignment(QtCore.Qt.AlignCenter)
# self.imageLabel.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setCentralWidget(self.dataController)
self.dataController.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
status = self.statusBar()
status.setSizeGripEnabled(False)
status.showMessage("Ready", 5000)
fileOpenAction = self.createAction("&Open...", self.fileOpen,
QtGui.QKeySequence.Open, "fileopen",
"Open a kinematic file")
fileQuitAction = self.createAction("&Quit", self.close,
"Ctrl+Q", "filequit", "Close the application")
self.fileMenu = self.menuBar().addMenu("&File")
self.fileMenuActions = (fileOpenAction, None, fileQuitAction)
self.addActions(self.fileMenu,self.fileMenuActions)
fileToolbar = self.addToolBar("File")
fileToolbar.setObjectName("FileToolBar")
self.addActions(fileToolbar, (fileOpenAction,None))
self.midsagittalView = True
self.frontView = False
self.topView = False
self.bottomView = False
self.showTrajectory = False
self.threeDView = False
viewGroup = QtGui.QActionGroup(self)
midSagittalViewAction = self.createAction("&Midsagittal View",
self.showMidsagittalView, "Ctrl+M", "mid","Show midsagittal view",True,"toggled(bool)")
viewGroup.addAction(midSagittalViewAction)
topViewAction = self.createAction("&Top View",
self.showTopView, "Ctrl+T", "top","Show top view",True,"toggled(bool)")
viewGroup.addAction(topViewAction)
bottomViewAction = self.createAction("&Bottom View",
self.showBottomView, "Ctrl+B", "bottom","Show bottom view",True,"toggled(bool)")
viewGroup.addAction(bottomViewAction)
frontViewAction = self.createAction("&Front View",
self.showFrontView, "Ctrl+F", "front","Show front view",True,"toggled(bool)")
viewGroup.addAction(frontViewAction)
threeDViewAction = self.createAction("&3D View", self.enable3D, "Ctrl+D", "3D", "Enable 3D View",
True, "toggled(bool)")
viewGroup.addAction(threeDViewAction)
viewTrajectoryAction = self.createAction("Show Trajectories", self.toggleTrajectories, None, "clock",
"Show Sensor Trajectories")
viewMenu = self.menuBar().addMenu("&View")
viewMenuActions = (midSagittalViewAction,topViewAction,bottomViewAction,frontViewAction,threeDViewAction,None,viewTrajectoryAction)
self.addActions(viewMenu,viewMenuActions)
viewToolBar = self.addToolBar("View")
viewToolBar.setObjectName("ViewToolBar")
self.addActions(viewToolBar,(viewMenuActions))
screenshotAction = self.createAction("Screenshot Figures", self.takeScreenshot, None, "camera",
"Take a screenshot of figures")
exportSensorDataAction = self.createAction("Export Sensor Data", self.exportIntervalData, None, "export",
"Export speed/accel data to file")
exportMenu = self.menuBar().addMenu("Export")
exportMenuActions = (screenshotAction,exportSensorDataAction)
self.addActions(exportMenu,exportMenuActions)
exportToolBar = self.addToolBar("Export")
exportToolBar.setObjectName("ExportToolBar")
self.addActions(exportToolBar,exportMenuActions)
self.resetableActions = ((midSagittalViewAction,True),(topViewAction,False),(bottomViewAction,False),(frontViewAction,False),(threeDViewAction,False), (viewTrajectoryAction, False))
helpHelpAction = self.createAction("&Help", self.helpHelp, QtGui.QKeySequence.HelpContents)
helpMenu = self.menuBar().addMenu("&Help")
self.addActions(helpMenu, (helpHelpAction,None))
self.setWindowTitle("Visualization Tool")
def selectConfig(self):
dlg = configDlg.ConfigDlg(self.configs)
if dlg.exec_():
self.configs = dlg.sendConfigs()
settings = QtCore.QSettings()
varConfigList = QtCore.QVariant(self.configs)
settings.setValue("savedConfig", varConfigList)
key, data = dlg.getData()
self.currentConfig = data
def createAction(self, text, slot=None, shortcut=None, icon=None,
tip=None, checkable=False, signal="triggered()"):
"""
Just a helper method to create an action and avoid a bunch of extra coding
"""
action = QtGui.QAction(text, self)
if icon is not None:
action.setIcon(QtGui.QIcon(":/%s.png" % icon))
if shortcut is not None:
action.setShortcut(shortcut)
if tip is not None:
action.setToolTip(tip)
action.setStatusTip(tip)
if slot is not None:
self.connect(action, QtCore.SIGNAL(signal), slot)
if checkable:
action.setCheckable(True)
return action
def addActions(self, target, actions):
"""
Adds actions to the menubar. Can put a spacer by passing None
"""
for action in actions:
if action is None:
target.addSeparator()
else:
target.addAction(action)
# def setConfig(self):
# dlg = configDlg.ConfigDlg(self.configList)
# if dlg.exec_():
# key, data = dlg.getData()
# print key
# print data[1]
# print data[2]
def fileOpen(self):
"""
The fileOpen method allows a user to choose any file from the file path
to be played. The kinfile is found by the user, and the audiofile
is found by walking backward through the file path (see: self.findAudioFile())
If no audiofile is found, then the user will be prompted to supply it.
The user has the option to cancel at any time.
When the file is chosen, the dataController's onFileLoaded is called to
actually load the data so it can be played back.
The actions are then reset to the midsagittal view, and the status bar at the
bottom of the program shows that the file was successfully loaded.
The title of the window is also changed to reflect the file name being played.
"""
if(self.dataController.status == self.dataController.playing):
self.showMidsagittalView()
self.dataController.stop()
dir = os.path.dirname(unicode(self.kinfilename)) \
if self.kinfilename is not None else "."
self.kinfilename = QtCore.QString(QtGui.QFileDialog.getOpenFileName(self,
"Visualization Tool - Choose Kinematic File", dir,
"TSV files (*.tsv)"))
if(self.kinfilename == QtCore.QString()):
return
newkinfilename = copy.deepcopy(self.kinfilename)
kinfileEnd = QtCore.QRegExp("_BPC.tsv")
self.audiofilename = newkinfilename.replace(kinfileEnd,'.wav')
self.audiofilename = self.findAudioFile(unicode(self.kinfilename))
if self.audiofilename is None:
QtGui.QMessageBox.warning(self,'Cannot Find Audio File',
"The corresponding audio file (*.wav) could not be found."
"<p>Please select the corresponding file.",
QtGui.QMessageBox.Ok, QtGui.QMessageBox.NoButton)
self.audiofilename = QtCore.QString(QtGui.QFileDialog.getOpenFileName(self,
"Visualization Tool - Choose Audio File", dir,
"WAV files (*.wav)"))
if (self.audiofilename):
self.dataController.onFileLoaded(unicode(self.kinfilename),unicode(self.audiofilename))
self.updateStatus("File %s loaded" % unicode(self.kinfilename))
self.showMidsagittalView()
self.showTrajectory = False
self.imageSavingDir = None
self.textSavingDir = None
# self.dataController.stop()
for action, check in self.resetableActions:
action.setChecked(check)
else:
return
def updateStatus(self, message):
"""
Simply updates the status bar with the message parameter
"""
self.statusBar().showMessage(message, 5000)
if self.kinfilename is not None:
self.setWindowTitle("Visualization Tool - %s" % \
os.path.basename(unicode(self.kinfilename)))
def showMidsagittalView(self):
"""
Shows the midsagittal view and resets the actions the way we want
"""
if(self.dataController.fileLoaded == True):
self.dataController.showMidsagittalView()
self.midsagittalView = True
self.frontView = False
self.topView = False
self.bottomView = False
def showTopView(self):
"""
Shows the topView and resets the actions the way we want
"""
if(self.dataController.fileLoaded == True):
self.dataController.showTopView()
self.midsagittalView = True
self.frontView = False
self.topView = False
self.bottomView = False
def showBottomView(self):
"""
Shows the bottom view and resets the actions
"""
if(self.dataController.fileLoaded == True):
self.dataController.showBottomView()
self.midsagittalView = False
self.frontView = False
self.topView = False
self.bottomView = True
def showFrontView(self):
"""
Shows the front view and resets the actions
"""
if(self.dataController.fileLoaded == True):
self.dataController.showFrontView()
# self.midsagittalView = False
# self.frontView = True
# self.topView = False
# self.bottomView = False
def enable3D(self):
"""
Allows for 3d interaction with the data.
Pretty nifty.
"""
if(self.dataController.fileLoaded==True):
self.dataController.toggleInteractiveMode()
self.midsagittalView = False
self.frontView = False
self.topView = False
self.bottomView = False
self.threeDView = True
def toggleTrajectories(self):
if(self.dataController.fileLoaded==True):
self.dataController.toggleTrajectoryMode()
self.showTrajectory = not self.showTrajectory
def takeScreenshot(self):
if(self.dataController.fileLoaded == True):
if(self.imageSavingDir is None):
fileNamingDialog = fnd.FileNamingDialog(self.audiofilename,'Images')
else:
fileNamingDialog = fnd.FileNamingDialog(self.audiofilename,'Images', self.imageSavingDir)
if fileNamingDialog.exec_():
filename = fileNamingDialog.getFile()
self.dataController.saveScreenshots(filename)
self.imageSavingDir, _ = os.path.split(str(filename))
QtGui.QMessageBox.information(self,'Save Screenshot',
"Screenshot successfully saved",
QtGui.QMessageBox.Ok, QtGui.QMessageBox.NoButton)
def exportIntervalData(self):
if(self.dataController.fileLoaded == True):
if(self.textSavingDir is None):
fileNamingDialog = fnd.FileNamingDialog(self.audiofilename,'Sensor Data')
else:
fileNamingDialog = fnd.FileNamingDialog(self.audiofilename,'Sensor Data', self.textSavingDir)
if fileNamingDialog.exec_():
filename = fileNamingDialog.getFile()
self.dataController.exportIntervalData(filename)
self.textSavingDir, _ = os.path.split(str(filename))
QtGui.QMessageBox.information(self,'Data Export',
"Data successfully exported",
QtGui.QMessageBox.Ok, QtGui.QMessageBox.NoButton)
def helpHelp(self):
"""
Shows a dialog (in HTML) that tells people to ask me for help if
they need it.
"""
QtGui.QMessageBox.about(self, "Help me!","""
<p> Program sucks and you need help?
<p>Email:
<p><b>andrew.kolb@marquette.edu</b>
<p>Or visit him in Room 230U!
""")
def center(self):
"""
Method to center the main window on the screen. Pulled it off
of stackoverflow, but pretty dope.
"""
frameGm = self.frameGeometry()
screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
frameGm.moveCenter(centerPoint)
self.move(frameGm.topLeft())
def findAudioFile(self,kinfilename):
"""
Funcition for walking backward through the file path to find the matching
audiofile to match the kinematic file. Try not to get too confused
by all the nested for loops...
"""
progress = QtGui.QProgressDialog("Searching for Audio File...",QtCore.QString(), 0,0,parent = self)
progress.setMinimumDuration(250)
(filename, _) = os.path.splitext(os.path.basename(kinfilename))
pattern = filename.split('_BPC')[0] + '.wav'
fullpath = os.path.dirname(kinfilename)
toSearch = []
audiofilename = None
for i in self.find(fullpath,'/'):
toSearch.append(fullpath[:i])
toSearch.reverse()
toSearch.append(fullpath)
for loc in toSearch:
for filenames in os.walk(loc):
for filename in filenames:
if pattern in filename:
desiredFile = filename
for files in desiredFile:
if pattern == files:
audiofilename = os.path.join(filenames[0],files)
progress.destroy()
return audiofilename
if audiofilename is None:
progress.destroy()
return audiofilename
def find(self,s, ch):
"""
Finds each of the instances of a given character, ch, in the string, s
"""
return [i for i, ltr in enumerate(s) if ltr == ch]
def main():
"""
Just sets up the application and runs it!
"""
qApp = QtGui.QApplication(sys.argv)
qApp.setApplicationName("Visualization Tool")
qApp.setOrganizationName("Marquette University Speechlab")
qApp.setWindowIcon(QtGui.QIcon(":/vtIcon.ico"))
form = MainWindow()
form.show()
sys.exit(qApp.exec_())
main()