Baby Language Lab Scripts
A collection of data processing tools.
 All Classes Namespaces Files Functions Variables Pages
test_window.py
Go to the documentation of this file.
1 from gi.repository import Gtk as gtk
2 import re
3 import threading
4 import subprocess
5 
6 from utils.ui_utils import UIUtils
7 from utils.progress_dialog import ProgressDialog
8 from utils.handler_manager import HandlerManager
9 from parsers.reliability2_parser import Reliability2Parser
10 from parsers.reliability2_exporter import Reliability2Exporter
11 from parsers.wav_parser import WavParser
12 from db.bll_database import BLLDatabase, DBConstants
13 from data_structs.check2 import Check2
14 from data_structs.test2 import Test2
15 from utils.backend_utils import BackendUtils
16 from utils.enum import Enum
17 
18 class TestWindow():
19  def __init__(self, check2):
21 
22  self.window = gtk.Window(gtk.WindowType.TOPLEVEL)
23  self.window.set_title('Reliability Testing')
24  self.handler_man.add_handler(self.window, 'destroy', lambda w: self.save_and_exit())
25  self.window.set_border_width(10)
26  self.window.set_default_size(900, 500)
27 
28  self.check2 = check2
29 
30  vbox = gtk.VBox()
31 
33  grid = self._build_layout_grid(self.controls)
34 
35  self.controls.set(self.check2)
36  self.controls.hookup(self.check2)
37 
38  vbox.pack_start(grid, True, True, 0)
39  button_box = self._build_button_box()
40  vbox.pack_start(button_box, False, False, 0)
41  self.window.add(vbox)
42 
43  self.window.show_all()
44 
45  def _build_button_box(self):
46  button_box = gtk.HButtonBox()
47  button_box.set_layout(gtk.ButtonBoxStyle.EDGE)
48 
49  back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
50  button_box.add(back_button)
51  save_button = UIUtils.create_button('Save & Exit', UIUtils.BUTTON_ICONS.SAVE, UIUtils.BUTTON_ICON_SIZES.PX32)
52  button_box.add(save_button)
53  next_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
54  button_box.add(next_button)
55 
56  next_handler = lambda w: self.next_test2(back_button, next_button)
57 
58  if self.check2.test2_index >= len(self.check2.test2s) - 1:
59  next_button.set_label(gtk.STOCK_OK)
60  next_handler = lambda w: self.save_and_export()
61 
62  self.handler_man.add_handler(next_button, 'clicked', next_handler)
63  self.handler_man.add_handler(back_button, 'clicked', lambda w: self.back_test2(back_button, next_button))
64  self.handler_man.add_handler(save_button, 'clicked', lambda w: self.window.destroy()) #this will call save_and_exit() since the window 'destroy' signal is connected to that method (see constructor)
65 
66  back_button.set_sensitive(self.check2.test2_index > 0)
67  next_button.set_sensitive(self.check2.test2_index < len(self.check2.test2s))
68 
69  return button_box
70 
71  def _check_completion(self):
72  msgs = []
73  complete = True
74 
75  if self.controls.scale.get_fill_level() != 300:
76  msgs.append('- The segment has not been fully played.')
77 
78  start_iter = self.controls.trans_entry.get_buffer().get_start_iter()
79  end_iter = self.controls.trans_entry.get_buffer().get_end_iter()
80  if not self.controls.trans_entry.get_buffer().get_text(start_iter, end_iter).strip():
81  msgs.append('- The transcription box is empty.')
82 
83  if not self.controls.child_voc_spinner.get_value_as_int():
84  msgs.append('- No child vocs have been recorded.')
85 
86  if msgs:
87  dialog_text = 'The following issues have been detected:\n'
88  dialog_text += '\n'.join(msgs)
89  dialog_text += '\n\nProceed anyway?'
90 
91  complete = UIUtils.show_confirm_dialog(dialog_text)
92 
93  return complete
94 
95  def _save_to_cur_test2(self):
96  #save current UI data
97  self.check2.test2s[self.check2.test2_index].ui_save_data = self._get_ui_save_data()
98  self.check2.test2s[self.check2.test2_index].transcription = self.controls.trans_entry.get_buffer().get_text(self.controls.trans_entry.get_buffer().get_start_iter(),
99  self.controls.trans_entry.get_buffer().get_end_iter())
100  self.check2.test2s[self.check2.test2_index].child_vocs = self.controls.child_voc_spinner.get_value_as_int()
101 
102  def back_test2(self, back_button, next_button):
103  if self.check2.test2_index > 0:
104  self._save_to_cur_test2()
105 
106  self.check2.test2_index -= 1
107  self.controls.update_progress_labels(self.check2)
108  self.controls.unhook()
109  self.controls.set(self.check2)
110  self.controls.hookup(self.check2)
111 
112  if self.check2.test2_index == len(self.check2.test2s) - 2:
113  next_button.set_label(gtk.STOCK_GO_FORWARD)
114  self.handler_man.remove_handlers(next_button, ['clicked'])
115  self.handler_man.add_handler(next_button, 'clicked', lambda w: self.next_test2(back_button, next_button))
116 
117  back_button.set_sensitive(self.check2.test2_index > 0)
118  next_button.set_sensitive(self.check2.test2_index < len(self.check2.test2s))
119 
120  def next_test2(self, back_button, next_button):
121  #validate
122  if self._check_completion():
123  #save the data that's currently in the window
124  self._save_to_cur_test2()
125 
126  #if there are more test2s left, increment the index and update the UI for the next test2
127  num_tests = len(self.check2.test2s)
128  if self.check2.test2_index < num_tests - 1:
129  self.check2.test2_index += 1
130  self.controls.update_progress_labels(self.check2)
131  self.controls.unhook()
132  self.controls.set(self.check2)
133  self.controls.hookup(self.check2)
134 
135  back_button.set_sensitive(self.check2.test2_index > 0)
136  next_button.set_sensitive(self.check2.test2_index < num_tests) #note: next button must be sensitive for "finish" state (so no - 1 here)
137 
138  #swap next button for finish button if we're on the last test2
139  if self.check2.test2_index == num_tests - 1:
140  next_button.set_label(gtk.STOCK_OK)
141  self.handler_man.remove_handlers(next_button, ['clicked'])
142  self.handler_man.add_handler(next_button, 'clicked', lambda w: self.save_and_export())
143 
144  #completed is a boolean
145  def save(self, completed, progress_dialog=None):
146  self._save_to_cur_test2()
147 
148  db = BLLDatabase()
149  for i in range(len(self.check2.test2s)):
150  test2 = self.check2.test2s[i]
151  if test2.db_id != None:
152  test2.db_delete(db)
153  test2.db_insert(db)
154 
155  if progress_dialog:
156  progress_dialog.set_fraction(float(i + 1) / float(len(self.check2.test2s)))
157 
158  self.check2.update_test2_index(db)
159 
160  #update modification timestamp
161  self.check2.update_modified(db)
162 
163  #only update completed timestamp the first time the check2 is completed
164  if completed and self.check2.completed == None:
165  self.check2.update_completed(db)
166  db.close()
167 
168  def save_and_exit(self):
169  progress_dialog = ProgressDialog(title='Saving...', phases=['Saving records to DB'])
170  progress_dialog.show()
171  self.save(False, progress_dialog)
172  progress_dialog.ensure_finish()
173  self.window.destroy()
174 
175  def save_and_export(self):
176  if self._check_completion():
177  checkbuttons = []
178  include_trans_check = gtk.CheckButton('Include Transcription')
179  checkbuttons.append(include_trans_check)
180  open_now_check = gtk.CheckButton('Open Immediately')
181  open_now_check.set_active(True)
182  checkbuttons.append(open_now_check)
183 
184  filename, results = UIUtils.show_file_dialog_with_checks('Save File', [UIUtils.CSV_FILE_FILTER, UIUtils.ALL_FILE_FILTER], gtk.FileChooserAction.SAVE, gtk.STOCK_SAVE, checkbuttons, save_last_location=True)
185 
186  if filename:
187  progress_dialog = ProgressDialog(title='Saving...', phases=['Saving records to DB', 'Matching DB records to rows', 'Writing rows to file'])
188  progress_dialog.show()
189 
190  self.save(True, progress_dialog)
191  progress_dialog.next_phase()
192 
193  if not filename.lower().endswith('.csv'):
194  filename += '.csv'
195 
196  exporter = Reliability2Exporter(filename, self.check2)
197  exporter.export(results[0], progress_update_fcn=progress_dialog.set_fraction, progress_next_fcn=progress_dialog.next_phase)
198  exporter.close()
199  progress_dialog.ensure_finish()
200 
201  self.handler_man.block_handler(self.window, 'destroy')
202  self.window.destroy()
203  #note: there is no need for a corresponding self.handler_man.unblock_handler() call, since the object the handler is operating upon (the window) is destroyed)
204 
205  #show immediately, if requested
206  if results[1]:
207  subprocess.Popen(['%s' % DBConstants.SETTINGS.SPREADSHEET_PATH, filename])
208  else:
209  UIUtils.show_message_dialog('Results exported successfully!')
210 
211  def _build_layout_grid(self, controls):
212  #table = gtk.Table(10, 10)
213  grid = gtk.Grid()
214 
215  #table.attach(controls.status_table, 9, 10, 0, 1)
216  grid.attach(controls.status_grid, 9, 0, 1, 1)
217 
218  #table.attach(controls.scale, 0, 10, 1, 2)
219  grid.attach(controls.scale, 0, 1, 10, 1)
220 
221  snd_ctrl_hbox = gtk.HBox()
222  snd_ctrl_hbox.pack_start(controls.prev_button, True, True, 0)
223  snd_ctrl_hbox.pack_start(controls.play_button, True, True, 0)
224  snd_ctrl_hbox.pack_start(controls.next_button, True, True, 0)
225  #table.attach(snd_ctrl_hbox, 4, 6, 2, 3, gtk.EXPAND, gtk.EXPAND)
226  grid.attach(snd_ctrl_hbox, 4, 2, 2, 1)
227 
228  clip_len_hbox = gtk.HBox()
229  clip_label = gtk.Label('Clip Length:')
230  clip_label.set_justify(gtk.JUSTIFY_RIGHT)
231  clip_len_hbox.pack_start(clip_label, True, True, 0)
232  clip_len_hbox.pack_start(controls.clip_spinner, True, True, 0)
233  #table.attach(clip_len_hbox, 6, 7, 2, 3, gtk.EXPAND, gtk.EXPAND)
234  grid.attach(clip_len_hbox, 6, 2, 1, 1)
235 
236  child_voc_hbox = gtk.HBox()
237  child_voc_label = gtk.Label('Child Vocs:')
238  child_voc_label.set_justify(gtk.JUSTIFY_RIGHT)
239  child_voc_hbox.pack_start(child_voc_label, True, True, 0)
240  child_voc_hbox.pack_start(controls.child_voc_spinner, True, True, 0)
241  #table.attach(child_voc_hbox, 7, 8, 2, 3, gtk.EXPAND, gtk.EXPAND)
242  grid.attach(child_voc_hbox, 7, 2, 1, 1)
243 
244  trans_label = gtk.Label('Transcription:')
245  trans_label.set_justify(gtk.JUSTIFY_LEFT)
246  #table.attach(trans_label, 0, 1, 3, 4, gtk.EXPAND, gtk.EXPAND)
247  grid.attach(trans_label, 0, 3, 1, 1)
248 
249  scrolled_win = gtk.ScrolledWindow()
250  scrolled_win.set_policy(gtk.PolicyType.AUTOMATIC, gtk.PolicyType.AUTOMATIC)
251  scrolled_win.add(controls.trans_entry)
252  #table.attach(scrolled_win, 0, 10, 4, 9)
253  grid.attach(scrolled_win, 0, 4, 10, 5)
254 
255  word_count_hbox = gtk.HBox()
256  words_label = gtk.Label('Word Count:')
257  words_label.set_justify(gtk.JUSTIFY_RIGHT)
258  word_count_hbox.pack_start(words_label, True, True, 0)
259  word_count_hbox.pack_end(controls.words_entry, True, True, 0)
260  #table.attach(word_count_hbox, 0, 1, 9, 10, gtk.EXPAND, gtk.EXPAND)
261  grid.attach(word_count_hbox, 0, 9, 1, 1)
262 
263  return grid
264 
265  def _get_ui_save_data(self):
266  save_data = self.controls.get_ui_save_data()
267 
268  return save_data
269 
270  class Controls():
271  def __init__(self):
273  self.sm = None
274 
275  #self.status_table = gtk.Table(3, 2)
276  self.status_grid = gtk.Grid()
277 
278  name_label = gtk.Label('Environment: ')
279  name_label.set_justify(gtk.JUSTIFY_RIGHT)
280  #self.status_table.attach(name_label, 0, 1, 0, 1, gtk.EXPAND, gtk.EXPAND)
281  self.status_grid.attach(name_label, 0, 0, 1, 1)
282  self.env_label = gtk.Label('')
283  self.env_label.set_justify(gtk.JUSTIFY_LEFT)
284  #self.status_table.attach(self.env_label, 1, 2, 0, 1, gtk.EXPAND, gtk.EXPAND)
285  self.status_grid.attach(self.env_label, 1, 0, 1, 1)
286 
287  name_label = gtk.Label('Activity: ')
288  name_label.set_justify(gtk.JUSTIFY_RIGHT)
289  #self.status_table.attach(name_label, 0, 1, 1, 2, gtk.EXPAND, gtk.EXPAND)
290  self.status_grid.attach(name_label, 0, 1, 1, 1)
291  self.act_label = gtk.Label('')
292  self.act_label.set_justify(gtk.JUSTIFY_LEFT)
293  #self.status_table.attach(self.act_label, 1, 2, 1, 2, gtk.EXPAND, gtk.EXPAND)
294  self.status_grid.attach(self.act_label, 1, 1, 1, 1)
295 
296  name_label = gtk.Label('Block: ')
297  name_label.set_justify(gtk.JUSTIFY_RIGHT)
298  #self.status_table.attach(name_label, 0, 1, 2, 3, gtk.EXPAND, gtk.EXPAND)
299  self.status_grid.attach(name_label, 0, 2, 1, 1)
300  self.block_label = gtk.Label('')
301  self.block_label.set_justify(gtk.JUSTIFY_LEFT)
302  #self.status_table.attach(self.block_label, 1, 2, 2, 3, gtk.EXPAND, gtk.EXPAND)
303  self.status_grid.attach(self.block_label, 1, 2, 1, 1)
304 
305  self.prev_button = gtk.Button(stock=gtk.STOCK_MEDIA_PREVIOUS)
306  self.play_button = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
307  self.next_button = gtk.Button(stock=gtk.STOCK_MEDIA_NEXT)
308  self.next_button.set_use_stock(True)
309 
310  scale_adj = gtk.Adjustment(value=0, lower=0, upper=300, step_incr=10, page_incr=10, page_size=0)
311  self.scale = gtk.HScale(scale_adj)
312  self.scale.set_show_fill_level(True)
313  self.scale.set_restrict_to_fill_level(True)
314  self.scale.set_fill_level(0)
315  self.scale.set_draw_value(False)
316  self.scale.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
317 
318  clip_adj = gtk.Adjustment(value=10, lower=1, upper=300, step_incr=1, page_incr=10)
319  self.clip_spinner = gtk.SpinButton(clip_adj)
320 
321  child_voc_adj = gtk.Adjustment(value=0, lower=0, upper=1000000, step_incr=1, page_incr=10)
322  self.child_voc_spinner = gtk.SpinButton(child_voc_adj)
323 
324  self.trans_entry = gtk.TextView()
325  self.trans_entry.set_wrap_mode(gtk.WRAP_WORD)
326 
327  self.words_entry = gtk.Entry()
328  self.words_entry.set_sensitive(False)
329  self.words_entry.set_width_chars(4)
330  self.words_entry.set_text('0')
331 
333  text_buf = self.trans_entry.get_buffer()
334  text = text_buf.get_text( text_buf.get_start_iter(), text_buf.get_end_iter() )
335  num_words = BackendUtils.get_word_count(text)
336  self.words_entry.set_text(str(num_words))
337 
338  def _snap_scale(self, adj):
339  adj.set_value(self.sm.markers[self.sm.marker_index])
340 
341  def update_progress_labels(self, check2):
342  env_index = check2.test2_index / (len(check2.activities) * check2.blocks_per_activity)
343  act_index = (check2.test2_index / check2.blocks_per_activity) % len(check2.activities)
344  block_index = check2.test2_index % check2.blocks_per_activity
345 
346  self.env_label.set_text('%d of %d (%s)' % (env_index + 1, len(check2.environments), check2.environments[env_index]))
347  self.act_label.set_text('%d of %d (%s)' % (act_index + 1, len(check2.activities), check2.activities[act_index]))
348  self.block_label.set_text('%d of %d' % (block_index + 1, check2.blocks_per_activity))
349 
350  def set(self, check2):
351  test2 = check2.test2s[check2.test2_index]
352 
353  self.scale.clear_marks()
354 
355  scale_val = 0
356  fill_lev = 0
357  clip_len_val = 10
358  trans = ''
359  child_vocs = 0
360  markers = []
361  if test2.ui_save_data:
362  scale_val = test2.ui_save_data['markers'][test2.ui_save_data['marker_index']]
363  fill_lev = test2.ui_save_data['fill_lev']
364  clip_len_val = test2.ui_save_data['markers'][test2.ui_save_data['marker_index'] + 1] - test2.ui_save_data['markers'][test2.ui_save_data['marker_index']]
365  trans = test2.transcription
366  child_vocs = test2.child_vocs
367  markers = test2.ui_save_data['markers']
368 
369  self.scale.set_fill_level(fill_lev)
370  self.scale.set_value(scale_val)
371  self.clip_spinner.set_value(clip_len_val)
372  self.trans_entry.get_buffer().set_text(trans)
373  self._update_word_count()
374 
375  self.child_voc_spinner.set_value(child_vocs)
376  for pos in markers:
377  self.scale.add_mark(pos, gtk.POS_BOTTOM, BackendUtils.get_time_str(pos, pad_hour_min=False, show_hours=False, show_decimals=False))
378 
379  self.update_progress_labels(check2)
380 
381  def hookup(self, check2):
382  test2 = check2.test2s[check2.test2_index]
383 
384  self.sm = TestWindow.TestStateMachine(self, test2)
385 
386  self.handler_man.add_handler(self.next_button, 'clicked', lambda w: self.sm.drive(TestWindow.TestStateMachine.ACTIONS.NEXT))
387  self.handler_man.add_handler(self.prev_button, 'clicked', lambda w: self.sm.drive(TestWindow.TestStateMachine.ACTIONS.PREV))
388  self.handler_man.add_handler(self.play_button, 'clicked', lambda w: self.sm.drive(TestWindow.TestStateMachine.ACTIONS.PLAY))
389 
390  self.handler_man.add_handler(self.clip_spinner, 'value-changed', lambda w: self.sm.drive(TestWindow.TestStateMachine.ACTIONS.ADJUST))
391  self.handler_man.add_handler(self.scale.get_adjustment(), 'value-changed', self._snap_scale)
392 
393  self.handler_man.add_handler(self.trans_entry.get_buffer(), 'changed', lambda w: self._update_word_count())
394 
395  self.handler_man.add_handler(self.child_voc_spinner, 'value-changed', lambda w: self.trans_entry.grab_focus())
396 
397  def unhook(self):
398  self.handler_man.remove_handlers(self.next_button, ['clicked'])
399  self.handler_man.remove_handlers(self.prev_button, ['clicked'])
400  self.handler_man.remove_handlers(self.play_button, ['clicked'])
401 
402  self.handler_man.remove_handlers(self.clip_spinner, ['value-changed'])
403  self.handler_man.remove_handlers(self.scale.get_adjustment(), ['value-changed'])
404 
405  self.handler_man.remove_handlers(self.trans_entry.get_buffer(), ['changed'])
406 
407  self.handler_man.remove_handlers(self.child_voc_spinner, ['value-changed'])
408 
409  self.sm = None
410 
411  def get_ui_save_data(self):
412  save_data = self.sm.get_ui_save_data() if self.sm else {}
413  #note: clip length and word count don't need to be saved, as they can be calculated from info we are storing
414 
415  return save_data
416 
418  STATES = Enum('INITIAL MARKER_UNPLAYED MARKER_PLAYED'.split())
419  ACTIONS = Enum('NEXT PREV PLAY ADJUST'.split())
420 
421  def __init__(self, controls, test2):
422  self.controls = controls
423  self.test2 = test2
424 
425  self.seg_len = self.controls.scale.get_adjustment().get_upper()
426 
427  self.route_dict = {
428  TestWindow.TestStateMachine.STATES.INITIAL: lambda action: self._initial(),
429  TestWindow.TestStateMachine.STATES.MARKER_UNPLAYED: self._marker_unplayed,
430  TestWindow.TestStateMachine.STATES.MARKER_PLAYED: self._marker_played,
431  }
432 
433  if self.test2.ui_save_data:
434  self.markers = self.test2.ui_save_data['markers']
435  self.marker_index = self.test2.ui_save_data['marker_index']
436  self.state = self.test2.ui_save_data['sm_state']
437  else:
438  self.markers = []
439  self.marker_index = -1
440  self.state = TestWindow.TestStateMachine.STATES.INITIAL
441  self.drive(None)
442 
444  self.controls.handler_man.block_handler(self.controls.clip_spinner, 'value-changed')
445 
446  enabled = (self.marker_index == len(self.markers) - 2)
447  self.controls.clip_spinner.set_sensitive(enabled)
448 
449  val = self.markers[self.marker_index + 1] - self.markers[self.marker_index]
450  self.controls.clip_spinner.set_value(val)
451 
452  self.controls.handler_man.unblock_handler(self.controls.clip_spinner, 'value-changed')
453 
454  def _append_marker(self, pos, append_to_self=True):
455  if append_to_self:
456  self.markers.append(pos)
457  self.controls.scale.add_mark(pos, gtk.POS_BOTTOM, BackendUtils.get_time_str(pos, pad_hour_min=False, show_hours=False, show_decimals=False))
458 
459  def _update_scale_pos(self):
460  self.controls.handler_man.block_handler(self.controls.scale.get_adjustment(), 'value-changed')
461  self.controls.scale.set_value(self.markers[self.marker_index])
462  self.controls.handler_man.unblock_handler(self.controls.scale.get_adjustment(), 'value-changed')
463 
464  def _update_scale_fill_lev(self, lev):
465  if self.controls.scale.get_fill_level() < lev:
466  self.controls.scale.set_fill_level(lev)
467 
468  def drive(self, action):
469  #old_state = self.state
470  self.route_dict[self.state](action)
471  self.controls.trans_entry.grab_focus()
472  #new_state = self.state
473  #states = TestWindow.TestStateMachine.STATES.get_ordered_keys()
474  #print '%s -> %s' % (states[old_state], states[new_state])
475 
476  def get_ui_save_data(self):
477  return {
478  'markers': self.markers,
479  'marker_index': self.marker_index,
480  'sm_state': self.state,
481  'fill_lev': self.controls.scale.get_fill_level(),
482  }
483 
484  def _play_clip(self, start, end):
485  wav_parser = WavParser(self.test2.wav_filename)
486  wav_parser.play_clip(start, end)
487  wav_parser.close()
488 
489  def _initial(self):
490  self.marker_index = 0
491  self._append_marker(0)
492  next_pos = self.controls.clip_spinner.get_value_as_int()
493 
494  max_pos = self.controls.scale.get_adjustment().get_upper()
495  if next_pos >= max_pos:
496  next_pos = max_pos
497 
498  self._append_marker(next_pos)
499  self.state = TestWindow.TestStateMachine.STATES.MARKER_UNPLAYED
500 
501  def _marker_unplayed(self, action):
502  if action == TestWindow.TestStateMachine.ACTIONS.NEXT:
503  pass
504 
505  elif action == TestWindow.TestStateMachine.ACTIONS.PREV:
506  #go back if we're not at the initial mark
507  if self.marker_index > 0:
508  self.marker_index -= 1
509  self._update_scale_pos()
510  self.state = TestWindow.TestStateMachine.STATES.MARKER_PLAYED
511  self._update_clip_spinner()
512 
513  elif action == TestWindow.TestStateMachine.ACTIONS.PLAY:
514  fill_lev = self.markers[self.marker_index + 1]
515  if self.controls.scale.get_fill_level() < fill_lev:
516  self.controls.scale.set_fill_level(fill_lev)
517  self.state = TestWindow.TestStateMachine.STATES.MARKER_PLAYED
518 
519  wav_offset_sec = self.test2.get_start_time_offset()
520  start = wav_offset_sec + self.markers[self.marker_index]
521  end = wav_offset_sec + self.markers[self.marker_index + 1]
522  t = threading.Thread(target=self._play_clip, args=(start, end))
523  t.start()
524 
525  elif action == TestWindow.TestStateMachine.ACTIONS.ADJUST:
526  #assume we are at the second-last marker (this is enforced by setting clip_spinner to insensitive at all other times)
527  max_pos = self.controls.scale.get_adjustment().get_upper()
528  fill_lev = self.controls.scale.get_fill_level()
529  new_pos = self.markers[self.marker_index] + self.controls.clip_spinner.get_value_as_int()
530  if new_pos > max_pos:
531  new_pos = max_pos
532 
533  self.controls.scale.clear_marks()
534  self.markers.pop(-1)
535  for mark in self.markers:
536  self._append_marker(mark, False)
537  self._append_marker(new_pos)
538 
539  self.controls.scale.set_fill_level(fill_lev)
540  self._update_scale_pos()
541 
542  def _marker_played(self, action):
543  if action == TestWindow.TestStateMachine.ACTIONS.NEXT:
544  max_pos = self.controls.scale.get_adjustment().get_upper()
545 
546  if self.markers[self.marker_index + 1] != max_pos:
547  self.marker_index += 1
548  self._update_scale_pos()
549 
550  if self.marker_index == len(self.markers) - 1:
551  next_pos = self.markers[self.marker_index] + self.controls.clip_spinner.get_value_as_int()
552  if next_pos > max_pos:
553  next_pos = max_pos
554 
555  self._append_marker(next_pos)
556 
557  self._update_clip_spinner()
558  if self.controls.scale.get_fill_level() == self.markers[self.marker_index]:
559  self.state = TestWindow.TestStateMachine.STATES.MARKER_UNPLAYED
560 
561  elif action == TestWindow.TestStateMachine.ACTIONS.PREV:
562  #go back if we're not at the initial mark
563  if self.marker_index > 0:
564  self.marker_index -= 1
565  self._update_scale_pos()
566  self._update_clip_spinner()
567 
568  elif action == TestWindow.TestStateMachine.ACTIONS.PLAY:
569  self.controls.trans_entry.grab_focus()
570  wav_offset_sec = self.test2.get_start_time_offset()
571  start = wav_offset_sec + self.markers[self.marker_index]
572  end = wav_offset_sec + self.markers[self.marker_index + 1]
573  t = threading.Thread(target=self._play_clip, args=(start, end))
574  t.start()
575 
576  elif action == TestWindow.TestStateMachine.ACTIONS.ADJUST:
577  #assume we are at the second-last marker (this is enforced by setting clip_spinner to insensitive at all other times)
578  max_pos = self.controls.scale.get_adjustment().get_upper()
579  new_pos = self.markers[self.marker_index] + self.controls.clip_spinner.get_value_as_int()
580  if new_pos > max_pos:
581  new_pos = max_pos
582 
583  self.controls.scale.clear_marks()
584  self.markers.pop(-1)
585  for mark in self.markers:
586  self._append_marker(mark, False)
587  self._append_marker(new_pos)
588 
589  self.controls.scale.set_fill_level(self.markers[-2]) #last segment is now unplayed
590  self._update_scale_pos()
591 
592  if self.marker_index == len(self.markers) - 2:
593  self.state = TestWindow.TestStateMachine.STATES.MARKER_UNPLAYED