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 datetime
3 import logging
4 import subprocess
5 
6 from data_structs.check import Check
7 from data_structs.segment import Segment
8 from data_structs.test import Test
9 from db.bll_database import BLLDatabase, DBConstants
10 from parsers.wav_parser import WavParser
11 from parsers.reliability_exporter import ReliabilityExporter
12 from utils.ui_utils import UIUtils
13 from utils.form import Form
14 from utils.handler_manager import HandlerManager
15 from utils.backend_utils import BackendUtils
16 from utils.praat_interop import PraatInterop
17 
18 class TestWindow():
19  def __init__(self, check):
20  self.logger = logging.getLogger(__name__)
21 
22  self.check = check
23 
24  self.wav_parser = WavParser(self.check.wav_filename)
25 
26  self.window = gtk.Window(gtk.WindowType.TOPLEVEL)
27  self.window.set_title('Testing')
28  self.window.connect('destroy', lambda w: self._exit()) #will save current input and exit
29  self.window.set_border_width(10)
30  self.window.set_default_size(350, 200)
31 
32  self.wo_form = Form()
33  self.wo_form.handler_man = HandlerManager()
34  self.w_form = Form()
35  self.w_form.handler_man = HandlerManager()
36 
37  self.button_form = Form()
38  self.button_form.handler_man = HandlerManager()
39 
42  button_box = self._build_button_box()
43 
46 
47  vbox = gtk.VBox()
48  vbox.pack_start(self.progress_bar, True, True, 0)
49  vbox.pack_start(self.wo_context_checkbox, True, True, 0)
50  vbox.pack_start(self.wo_context_frame, True, True, 0)
51  vbox.pack_start(self.w_context_frame, True, True, 0)
52  vbox.pack_end(button_box, True, True, 0)
53 
54  self.window.add(vbox)
55  self.window.show_all()
56 
58  self._toggle_wo_context_frame(self.wo_context_checkbox.get_active())
59  self._set_ui_to_cur_test()
60 
62  progress_bar = gtk.ProgressBar()
63  progress_bar.set_orientation(gtk.Orientation.HORIZONTAL)
64 
65  return progress_bar
66 
68  cur_test = self.check.tests[self.check.test_index]
69 
70  cat_index = 0
71  if cur_test.category_input != None:
72  cat_index = cur_test.category_input - DBConstants.COMBO_OPTIONS[DBConstants.COMBO_GROUPS.RELIABILITY_CATEGORIES].EMPTY
73  self.w_form.type_combo.set_active(cat_index)
74 
75  self.w_form.uncertain_checkbox.set_active(cur_test.is_uncertain or False)
76 
77  self.w_form.context_pad_spinner.set_value(cur_test.context_padding)
78 
79  self.w_form.syllables_spinner.set_value(cur_test.syllables_w_context if cur_test.syllables_w_context != None else 0)
80 
81  #these boundaries are displayed without context, but opening in praat or listening will play with context
82  seg_start, seg_end = self._get_bounds()
83  self.w_form.user_start_entry.set_text(str(cur_test.seg.user_adj_start) if cur_test.seg.user_adj_start != None else str(seg_start))
84  self.w_form.user_end_entry.set_text(str(cur_test.seg.user_adj_end) if cur_test.seg.user_adj_end != None else str(seg_end))
85 
86  self.wo_form.syllables_spinner.set_value(cur_test.syllables_wo_context if cur_test.syllables_wo_context != None else 0)
87  self.wo_context_checkbox.set_active(cur_test.syllables_wo_context != None) #this will toggle the visibility of the frame because of the handler attached to it
88 
90  cur_test = self.check.tests[self.check.test_index]
91  frame = gtk.Frame(label='With Context')
92 
93  #table = gtk.Table(6, 4, False)
94  grid = gtk.Grid()
95 
96  play_button = UIUtils.create_button('', UIUtils.BUTTON_ICONS.PLAY, UIUtils.BUTTON_ICON_SIZES.PX32)
97  #table.attach(play_button, 0, 1, 0, 1, gtk.EXPAND, gtk.EXPAND)
98  grid.attach(play_button, 0, 0, 1, 1)
99 
100  type_label = gtk.Label('Seg Category:')
101  #table.attach(type_label, 1, 2, 0, 1, gtk.EXPAND, gtk.EXPAND)
102  grid.attach(type_label, 1, 0, 1, 1)
103 
104  self.w_form.type_combo = UIUtils.build_options_combo(DBConstants.COMBO_GROUPS.RELIABILITY_CATEGORIES)
105  #table.attach(self.w_form.type_combo, 2, 3, 0, 1, gtk.EXPAND, gtk.EXPAND)
106  grid.attach(self.w_form.type_combo, 2, 0, 1, 1)
107 
108  uncertain_label = gtk.Label('Other/Uncertain:')
109  self.w_form.uncertain_checkbox = gtk.CheckButton()
110  #table.attach(uncertain_label, 1, 2, 1, 2, gtk.EXPAND, gtk.EXPAND)
111  grid.attach(uncertain_label, 1, 1, 1, 1)
112  #table.attach(self.w_form.uncertain_checkbox, 2, 3, 1, 2, gtk.EXPAND|gtk.FILL, gtk.EXPAND|gtk.FILL)
113  grid.attach(self.w_form.uncertain_checkbox, 2, 1, 1, 1)
114 
115  padding_label = gtk.Label('Padding:')
116  context_pad_adj = gtk.Adjustment(value=0, lower=1, upper=1000, step_incr=1, page_incr=5)
117  self.w_form.context_pad_spinner = gtk.SpinButton(context_pad_adj)
118  #table.attach(padding_label, 1, 2, 2, 3, gtk.EXPAND, gtk.EXPAND)
119  grid.attach(padding_label, 1, 2, 1, 1)
120  #table.attach(self.w_form.context_pad_spinner, 2, 3, 2, 3, gtk.EXPAND, gtk.EXPAND)
121  grid.attach(self.w_form.context_pad_spinner, 2, 2, 1, 1)
122 
123  self.w_form.handler_man.add_handler(play_button, 'clicked', lambda w: self.play_seg(int(self.w_form.context_pad_spinner.get_value())))
124 
125  syllables_label = gtk.Label('Syllables:')
126  syllables_adj = gtk.Adjustment(value=0, lower=0, upper=1000, step_incr=1, page_incr=5)
127  self.w_form.syllables_spinner = gtk.SpinButton(syllables_adj)
128  #table.attach(syllables_label, 1, 2, 3, 4, gtk.EXPAND, gtk.EXPAND)
129  grid.attach(syllables_label, 1, 3, 1, 1)
130  #table.attach(self.w_form.syllables_spinner, 2, 3, 3, 4, gtk.EXPAND, gtk.EXPAND)
131  grid.attach(self.w_form.syllables_spinner, 2, 3, 1, 1)
132 
133  user_start_label = gtk.Label('User Start:')
134  self.w_form.user_start_entry = gtk.Entry()
135  self.w_form.user_start_entry.set_width_chars(10)
136  #table.attach(user_start_label, 1, 2, 4, 5, gtk.EXPAND, gtk.EXPAND)
137  grid.attach(user_start_label, 1, 4, 1, 1)
138  #table.attach(self.w_form.user_start_entry, 2, 3, 4, 5, gtk.EXPAND, gtk.EXPAND)
139  grid.attach(self.w_form.user_start_entry, 2, 5, 1, 1)
140 
141  user_end_label = gtk.Label('User End:')
142  self.w_form.user_end_entry = gtk.Entry()
143  self.w_form.user_end_entry.set_width_chars(10)
144  #table.attach(user_end_label, 1, 2, 5, 6, gtk.EXPAND, gtk.EXPAND)
145  grid.attach(user_end_label, 1, 5, 1, 1)
146  #table.attach(self.w_form.user_end_entry, 2, 3, 5, 6, gtk.EXPAND, gtk.EXPAND)
147  grid.attach(self.w_form.user_end_entry, 2, 5, 1, 1)
148 
149  open_praat_button = gtk.Button('Open Praat')
150  #table.attach(open_praat_button, 3, 4, 4, 5, gtk.EXPAND, gtk.EXPAND)
151  grid.attach(open_praat_button, 3, 4, 1, 1)
152  self.w_form.handler_man.add_handler(open_praat_button, 'clicked', lambda w: self._open_praat())
153 
154  close_praat_button = gtk.Button('Close Praat')
155  #table.attach(close_praat_button, 3, 4, 5, 6, gtk.EXPAND, gtk.EXPAND)
156  grid.attach(close_praat_button, 3, 5, 1, 1)
157  self.w_form.handler_man.add_handler(close_praat_button, 'clicked', lambda w: self._close_praat())
158 
159  frame.add(grid)
160 
161  return frame
162 
164  cur_test = self.check.tests[self.check.test_index]
165  frame = gtk.Frame(label='Without Context')
166 
167  #table = gtk.Table(1, 3, False)
168  grid = gtk.Grid()
169 
170  self.wo_form.play_button = UIUtils.create_button('', UIUtils.BUTTON_ICONS.PLAY, UIUtils.BUTTON_ICON_SIZES.PX32)
171  self.wo_form.handler_man.add_handler(self.wo_form.play_button, 'clicked', lambda w: self.play_seg(0))
172  #table.attach(self.wo_form.play_button, 0, 1, 0, 1, gtk.EXPAND, gtk.EXPAND)
173  grid.attach(self.wo_form.play_button, 0, 0, 1, 1)
174 
175  syllables_label = gtk.Label('Syllables:')
176  syllables_adj = gtk.Adjustment(value=0, lower=0, upper=1000, step_incr=1, page_incr=5)
177  self.wo_form.syllables_spinner = gtk.SpinButton(syllables_adj)
178  #table.attach(syllables_label, 1, 2, 0, 1, gtk.EXPAND, gtk.EXPAND)
179  grid.attach(syllables_label, 1, 0, 1, 1)
180  #table.attach(self.wo_form.syllables_spinner, 2, 3, 0, 1, gtk.EXPAND, gtk.EXPAND)
181  grid.attach(self.wo_form.syllables_spinner, 2, 0, 1, 1)
182 
183  frame.add(grid)
184 
185  return frame
186 
187  def _toggle_wo_context_frame(self, visible):
188  if visible:
189  self.wo_context_frame.show()
190  else:
191  self.wo_context_frame.hide()
192  self.check.tests[self.check.test_index].syllables_wo_context = None
193  self.wo_form.syllables_spinner.get_adjustment().set_value(1)
194 
195 
196  def _build_wo_context_checkbox(self, wo_context_frame):
197  cur_test = self.check.tests[self.check.test_index]
198  checkbox = gtk.CheckButton(label='Test Without Context')
199  checkbox.connect('toggled', lambda w: self._toggle_wo_context_frame(w.get_active()))
200 
201  checkbox.set_active(cur_test.syllables_wo_context != None)
202 
203  return checkbox
204 
205  def _build_button_box(self):
206  box = gtk.HButtonBox()
207  box.set_layout(gtk.ButtonBoxStyle.EDGE)
208 
209  self.button_form.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
210  self.button_form.handler_man.add_handler(self.button_form.back_button, 'clicked', lambda w: self._back())
211 
212  self.button_form.save_button = UIUtils.create_button('Save & Exit', UIUtils.BUTTON_ICONS.SAVE, UIUtils.BUTTON_ICON_SIZES.PX32)
213  self.button_form.handler_man.add_handler(self.button_form.save_button, 'clicked', lambda w: self._exit())
214 
215  self.button_form.forward_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
216  self.button_form.handler_man.add_handler(self.button_form.forward_button, 'clicked', lambda w: self._forward())
217 
218  self._update_step_buttons()
219 
220  box.pack_start(self.button_form.back_button, False, False, 0)
221  box.pack_start(self.button_form.save_button, False, False, 0)
222  box.pack_end(self.button_form.forward_button, False, False, 0)
223 
224  return box
225 
227  if self.check.num_segs == 1: #avoid dividing by zero
228  self.progress_bar.set_fraction(1.0)
229  else:
230  #print self.check.test_index
231  self.progress_bar.set_fraction( float(self.check.test_index) / float(self.check.num_segs - 1) )
232 
233  self.progress_bar.set_text('Segment %d of %d' % (self.check.test_index + 1, self.check.num_segs))
234 
235  def _set_step_button(self, button, stock, label_text, clicked_handler):
236  img = gtk.Image()
237  img.set_from_stock(stock, gtk.ICON_SIZE_BUTTON)
238  button.set_label(label_text)
239  button.set_image(img)
240 
241  self.button_form.handler_man.remove_handlers(button, ['clicked'])
242  self.button_form.handler_man.add_handler(button, 'clicked', clicked_handler)
243 
245  finish_text = 'Finish'
246  forward_text = 'Forward'
247 
248  if self.check.test_index == self.check.num_segs - 1:
249  self._set_step_button(self.button_form.forward_button, gtk.STOCK_OK, finish_text, lambda w: self._finish())
250 
251  elif self.button_form.forward_button.get_label() == finish_text:
252  self._set_step_button(self.button_form.forward_button, gtk.STOCK_GO_FORWARD, forward_text, lambda w: self._forward())
253 
254  self.button_form.back_button.set_sensitive(self.check.test_index > 0)
255 
256  def _exit(self, save=True):
257  if save:
258  self.save_input(mark_last_run=True)
259 
260  self.wav_parser.close()
261  self.window.destroy()
262 
263  def _finish(self):
264  if self._validate_cur_test():
265  self.save_input(mark_last_run=True, mark_as_completed=True)
266 
267  filename, check_results = UIUtils.save_file(filters=[UIUtils.CSV_FILE_FILTER], open_now_opt=True, save_last_location=True)
268 
269  if filename:
270  exporter = ReliabilityExporter(self.check, filename)
271  if exporter.export():
272  if check_results:
273  subprocess.Popen(['%s' % DBConstants.SETTINGS.SPREADSHEET_PATH, filename])
274  else:
275  UIUtils.show_message_dialog('Results exported successfully.')
276 
277  self._exit(False) #we have already saved above
278 
279  else:
280  UIUtils.show_message_dialog('An error occurred while exporting the results. These results are still saved in the database, and can be exported at a later time, pending the correction of this problem. Please bother the programmer until this happens.')
281 
283  #validate w_context form
284  w_context_valid = (self.w_form.type_combo.get_active() != 0 and
285  int(self.w_form.syllables_spinner.get_adjustment().get_value()) > 0 and
286  BackendUtils.is_float(self.w_form.user_start_entry.get_text()) and
287  BackendUtils.is_float(self.w_form.user_end_entry.get_text()))
288 
289  #print 'w_context_valid: %s' % str(w_context_valid)
290 
291  wo_context_valid = (not self.wo_context_checkbox.get_active() or int(self.wo_form.syllables_spinner.get_adjustment().get_value()) > 0)
292 
293  #print 'wo_context_valid: %s' % str(wo_context_valid)
294 
295  is_valid = w_context_valid and wo_context_valid
296 
297  if is_valid:
298  #make sure user-boundaries have been updated
299  #note: they are guarenteed to contain floats at this point because of the w_context_valid condition
300  user_start = float(self.w_form.user_start_entry.get_text())
301  user_end = float(self.w_form.user_end_entry.get_text())
302  cur_test = self.check.tests[self.check.test_index]
303 
304  is_valid = (cur_test.seg.start != user_start or cur_test.seg.end != user_end)
305  if not is_valid:
306  is_valid = UIUtils.show_confirm_dialog('Segment boundaries have not been adjusted. Continue anyway?')
307 
308  else:
309  UIUtils.show_message_dialog('Please ensure that all of the inputs have a correct value.')
310 
311  return is_valid
312 
313  def _move(self, incr):
314  self.save_input()
315  self.check.test_index += incr
316  self._update_progress_bar()
317  self._update_step_buttons()
318  self._set_ui_to_cur_test()
319 
320  def _forward(self):
321  if self._validate_cur_test():
322  self._move(1)
323 
324  def _back(self):
325  self._move(-1)
326 
327  def _get_bounds(self, include_context=False):
328  start_time = self.check.tests[self.check.test_index].seg.start
329  end_time = self.check.tests[self.check.test_index].seg.end
330 
331  if include_context:
332  context_len = self.w_form.context_pad_spinner.get_value_as_int() if include_context else 0
333  start_time = max(start_time - context_len, 0)
334  end_time = min(end_time + context_len, self.wav_parser.get_sound_len())
335 
336  return start_time, end_time
337 
338  def _open_praat(self):
339  start_time, end_time = self._get_bounds(include_context=True)
340  PraatInterop.open_praat()
341  PraatInterop.send_commands( PraatInterop.get_open_clip_script(start_time, end_time, self.check.wav_filename) )
342 
343  def _close_praat(self):
344  socket = PraatInterop.create_serversocket()
345  PraatInterop.send_commands( PraatInterop.get_sel_bounds_script(self.check.wav_filename) )
346  start_time, end_time = PraatInterop.socket_receive(socket)
347  socket.close()
348  PraatInterop.close_praat()
349 
350  if start_time != end_time: #make sure something was selected
351  #update the inputs in the UI
352  start_time = str( round(float(start_time), 3) )
353  end_time = str( round(float(end_time), 3) )
354 
355  self.w_form.user_start_entry.set_text(start_time)
356  self.w_form.user_end_entry.set_text(end_time)
357 
358  def play_seg(self, context_len):
359  self.wav_parser.play_seg(self.check.tests[self.check.test_index].seg, context_len)
360 
361  def save_input(self, mark_last_run=False, mark_as_completed=False):
362  cur_test = self.check.tests[self.check.test_index]
363 
364  cur_test.category_input = self.w_form.type_combo.get_model()[self.w_form.type_combo.get_active()][1]
365  cur_test.is_uncertain = self.w_form.uncertain_checkbox.get_active()
366  cur_test.context_padding = int(self.w_form.context_pad_spinner.get_adjustment().get_value())
367  cur_test.syllables_w_context = int(self.w_form.syllables_spinner.get_adjustment().get_value())
368  cur_test.seg.user_adj_start = float(self.w_form.user_start_entry.get_text())
369  cur_test.seg.user_adj_end = float(self.w_form.user_end_entry.get_text())
370 
371  cur_test.syllables_wo_context = None
372  if self.wo_context_checkbox.get_active():
373  cur_test.syllables_wo_context = int(self.wo_form.syllables_spinner.get_adjustment().get_value())
374 
375  db = BLLDatabase()
376  cur_test.db_update_user_inputs(db)
377  self.check.db_update_test_index(db)
378 
379  if mark_last_run:
380  self.check.mark_last_run(db)
381 
382  if mark_as_completed:
383  self.check.mark_as_completed(db)
384 
385  db.close()
386