def __init__(self): self.window = gtk.Window(gtk.WindowType.TOPLEVEL) self.window.set_title('Data Viewer') self.window.connect('destroy', lambda w: self._clean_up()) self.window.set_border_width(10) self.window.set_default_size(800, 600) self.logger = logging.getLogger(__name__) self.csv_filename = UIUtils.open_file('Select csv file', filters=[UIUtils.CSV_FILE_FILTER, UIUtils.ALL_FILE_FILTER]) self.window.set_title('%s - %s' % (self.window.get_title(), os.path.basename(self.csv_filename))) if not self.csv_filename: exit(0) self.wav_filename = self._get_wav_filename() self.wav_parser = None if self.wav_filename: self.wav_parser = WavParser(self.wav_filename) # if not self.wav_filename: # exit(0) self.sound_col_fcns = None col_datatypes, col_headers = self._get_col_info() db = self._build_db(col_datatypes, col_headers) model = self._get_model(col_datatypes) treeview = self._get_treeview(model, col_headers, db) self.filters = [] self.where_cond = None self.where_params = [] self.sort_order = None self.sort_col = None self._populate_model(db, model) toolbar = self._build_toolbar(db, treeview, col_headers) scrolled_win = gtk.ScrolledWindow() scrolled_win.set_policy(gtk.PolicyType.AUTOMATIC, gtk.PolicyType.AUTOMATIC) scrolled_win.add(treeview) vbox = gtk.VBox() vbox.pack_start(toolbar, False, False, 0) vbox.pack_start(scrolled_win, True, True, 0) self.window.add(vbox) self.window.show_all()
def __init__(self, check): self.logger = logging.getLogger(__name__) self.check = check self.wav_parser = WavParser(self.check.wav_filename) self.window = gtk.Window(gtk.WindowType.TOPLEVEL) self.window.set_title('Testing') self.window.connect( 'destroy', lambda w: self._exit()) #will save current input and exit self.window.set_border_width(10) self.window.set_default_size(350, 200) self.wo_form = Form() self.wo_form.handler_man = HandlerManager() self.w_form = Form() self.w_form.handler_man = HandlerManager() self.button_form = Form() self.button_form.handler_man = HandlerManager() self.progress_bar = self._build_progress_bar() self.w_context_frame = self._build_w_context_frame() button_box = self._build_button_box() self.wo_context_frame = self._build_wo_context_frame() self.wo_context_checkbox = self._build_wo_context_checkbox( self.wo_context_frame) vbox = gtk.VBox() vbox.pack_start(self.progress_bar, True, True, 0) vbox.pack_start(self.wo_context_checkbox, True, True, 0) vbox.pack_start(self.wo_context_frame, True, True, 0) vbox.pack_start(self.w_context_frame, True, True, 0) vbox.pack_end(button_box, True, True, 0) self.window.add(vbox) self.window.show_all() self._update_progress_bar() self._toggle_wo_context_frame(self.wo_context_checkbox.get_active()) self._set_ui_to_cur_test()
def play_selected_seg(self): (model, it) = self.treeview.get_selection().get_selected() if it: #if they've selected an error row, find the top level parent (the segment) and use it instead parent = model.iter_parent(it) while parent: it = parent parent = model.iter_parent(it) seg_num = model.get_value(it, 0) if it else None seg = self.trs_parser.parse()[seg_num] if not self.wav_parser: dialog = gtk.FileChooserDialog( title='Select WAV File', action=gtk.FileChooserAction.OPEN, buttons=(gtk.STOCK_CANCEL, gtk.ResponseType.CANCEL, gtk.STOCK_OPEN, gtk.ResponseType.OK)) dialog.set_default_response(gtk.ResponseType.OK) for filter_opt in (('wav Files', '*.wav'), ('All Files', '*')): file_filter = gtk.FileFilter() file_filter.set_name(filter_opt[0]) file_filter.add_pattern(filter_opt[1]) dialog.add_filter(file_filter) response = dialog.run() if response == gtk.ResponseType.OK: filename = dialog.get_filename() self.wav_parser = WavParser(filename) dialog.destroy() if self.wav_parser: self.wav_parser.play_seg(seg) else: UIUtils.show_no_sel_dialog() else: UIUtils.show_no_sel_dialog()
def _play_clip(self, db): map(lambda button: button.set_sensitive(False), self.scale_buttons) while gtk.events_pending(): gtk.main_iteration() time.sleep(self.props.inter_clip_sound_del) self._toggle_playing_icon(True) wav_parser = WavParser(self.filename) wav_parser.play_clip(0, wav_parser.get_sound_len()) wav_parser.close() map(lambda button: button.set_sensitive(True), self.scale_buttons) self._toggle_playing_icon(False)
class MainWindow(): #if none of these match, then string is used DATA_TYPE_REGEXS = { float: r'^-?\d+(\.\d+)?$', bool: r'^True|False$', } START_COL_NAMES = ['Start Time', 'Wav.Begin'] END_COL_NAMES = ['End Time', 'Wav.End'] DUR_COL_NAMES = ['Duration', 'Segment_Duration'] EL_TIME_COL_NAMES = ['Elapsed_Time'] def __init__(self): self.window = gtk.Window(gtk.WindowType.TOPLEVEL) self.window.set_title('Data Viewer') self.window.connect('destroy', lambda w: self._clean_up()) self.window.set_border_width(10) self.window.set_default_size(800, 600) self.logger = logging.getLogger(__name__) self.csv_filename = UIUtils.open_file('Select csv file', filters=[UIUtils.CSV_FILE_FILTER, UIUtils.ALL_FILE_FILTER]) self.window.set_title('%s - %s' % (self.window.get_title(), os.path.basename(self.csv_filename))) if not self.csv_filename: exit(0) self.wav_filename = self._get_wav_filename() self.wav_parser = None if self.wav_filename: self.wav_parser = WavParser(self.wav_filename) # if not self.wav_filename: # exit(0) self.sound_col_fcns = None col_datatypes, col_headers = self._get_col_info() db = self._build_db(col_datatypes, col_headers) model = self._get_model(col_datatypes) treeview = self._get_treeview(model, col_headers, db) self.filters = [] self.where_cond = None self.where_params = [] self.sort_order = None self.sort_col = None self._populate_model(db, model) toolbar = self._build_toolbar(db, treeview, col_headers) scrolled_win = gtk.ScrolledWindow() scrolled_win.set_policy(gtk.PolicyType.AUTOMATIC, gtk.PolicyType.AUTOMATIC) scrolled_win.add(treeview) vbox = gtk.VBox() vbox.pack_start(toolbar, False, False, 0) vbox.pack_start(scrolled_win, True, True, 0) self.window.add(vbox) self.window.show_all() def _get_wav_filename(self): wav_filename = None #try to find a wav file with the same name as the csv file if self.csv_filename.lower().endswith('.csv'): default_filename = self.csv_filename[:-3] + 'wav' if os.path.exists(default_filename): wav_filename = default_filename #if the above didn't succeed, prompt the user for a filename if not wav_filename: wav_filename = UIUtils.open_file(title='Select wav file', filters=[UIUtils.WAV_FILE_FILTER, UIUtils.ALL_FILE_FILTER]) return wav_filename def _get_csv_delim(self, csv_file): pos = csv_file.tell() delim = ',' if csv_file.next().find(',') > -1 else '\t' csv_file.seek(pos) return delim def _get_col_info(self): csv_file = open(self.csv_filename, 'rb') delim = self._get_csv_delim(csv_file) reader = csv.reader(csv_file, delimiter=delim) headers = reader.next() line = reader.next() datatypes = [int] #for hidden id column headers = ['id'] + headers for col in line: datatypes.append(self._get_datatype(col)) csv_file.close() #append marked column, if not already present if datatypes and not datatypes[-1] == bool: datatypes.append(bool) headers.append('Marked') return datatypes, headers def _get_datatype(self, col_val): col_type = None i = 0 regex_keys = MainWindow.DATA_TYPE_REGEXS.keys() while not col_type and i < len(regex_keys): if re.match(MainWindow.DATA_TYPE_REGEXS[regex_keys[i]], col_val): col_type = regex_keys[i] i += 1 if not col_type: col_type = str return col_type def _get_model(self, col_datatypes): #tell the treeview that all columns are strings (even those that are really floats). This way, floats and numbers will appear exactly as they do in the input spreadsheet, and not using gtk's default precision (i.e. we don't have to do any number formatting / rounding). Since the db has the actual types for its columns, and it is in change of filtering and sorting (NOT the UI), this works out fine. #The exception is the last column ('marked'); it requires type bool for the checkboxes to function properly in the ui col_types = [int] + [str] * (len(col_datatypes) - 2) + [bool] return gtk.ListStore(*col_types) def _get_treeview(self, model, headers, db): treeview = gtk.TreeView(model) #force the row selection colours to stay the same even when the treeview is deselected (otherwise the selection is hard to see - and seeing this at all times is important in this app's use cases) treeview.modify_base(gtk.StateFlags.ACTIVE, gdk.Color.parse('#3399FF')[1]) treeview.modify_text(gtk.StateFlags.ACTIVE, gdk.Color.parse('#FFFFFF')[1]) col_index = 0 col = gtk.TreeViewColumn(headers[0], gtk.CellRendererText(), text=col_index) col.set_visible(False) treeview.append_column(col) for col_index in range(1, len(headers) - 1): col = gtk.TreeViewColumn(headers[col_index], gtk.CellRendererText(), text=col_index) col.set_resizable(True) col.set_reorderable(True) col.set_visible(col.get_title() != '' and col.get_title() != '# segments') if col.get_visible(): col.set_clickable(True) col.connect('clicked', self._toggle_sort_column, treeview, db, col_index) treeview.append_column(col) toggleRenderer = gtk.CellRendererToggle() toggleRenderer.connect('toggled', self._toggle_renderer_callback, model, col_index + 1, db) mark_col = gtk.TreeViewColumn(headers[col_index + 1], toggleRenderer, active=col_index + 1) mark_col.set_resizable(True) mark_col.set_reorderable(True) mark_col.set_clickable(True) mark_col.connect('clicked', self._toggle_sort_column, treeview, db, col_index + 1) mark_col.set_alignment(0.5) #position the title in the middle, since this column's wider than the others (since it's the last visible column) treeview.append_column(mark_col) return treeview def _toggle_sort_column(self, treeview_col, treeview, db, treeview_col_index): #clear any previous sort indicators on all columns except the one we care about for col in treeview.get_columns(): if col != treeview_col: col.set_sort_indicator(False) if treeview_col.get_sort_indicator(): sort_order = treeview_col.get_sort_order() if sort_order == gtk.SortType.ASCENDING: self.sort_order = CSVDatabase.ORDER_DIRS.DESC self.sort_col = treeview_col_index treeview_col.set_sort_order(gtk.SortType.DESCENDING) elif sort_order == gtk.SortType.DESCENDING: self.sort_order = None self.sort_col = None treeview_col.set_sort_indicator(False) else: self.sort_order = CSVDatabase.ORDER_DIRS.ASC self.sort_col = treeview_col_index treeview_col.set_sort_indicator(True) treeview_col.set_sort_order(gtk.SortType.ASCENDING) self._populate_model(db, treeview.get_model()) #for debugging def _print_model(self, model): for row in model: s = '' for item in row: s += str(item) + ' ' print s def _toggle_renderer_callback(self, renderer, path, model, col_index, db): if path is not None: model[path][col_index] = not model[path][col_index] db.csv_update_by_index([model.get_n_columns() - 1], where_cond='id=?', params=[int(model[path][col_index]), model[path][0]]) def _build_db(self, col_datatypes, col_headers): csv_file = open(self.csv_filename, 'rb') delim = self._get_csv_delim(csv_file) lines = csv_file.readlines() reader = csv.reader(lines, delimiter=delim) header_row = reader.next() #skip headers row db = CSVDatabase(col_headers, col_datatypes) progress_dialog = ProgressDialog(title='Loading file', phases=['Loading file...']) progress_dialog.show() num_rows = len(lines) - 1 #subtract one for header done = False i = 0 while i < num_rows and not done: row = reader.next() #if this file already contains counts at the end (has already been exported), skip those rows done = row and (row[0].startswith('File Stats') or row[0].startswith('Count of')) if not done: #if we have a marked column, use it - otherwise, append one if re.match(MainWindow.DATA_TYPE_REGEXS[bool], row[-1]): row = row[:-1] + [int(bool(row[-1] == 'True'))] else: row = row + [0] db.csv_insert([i + 1] + row) if (i % 10): progress_dialog.set_fraction(float(i + 1) / float(num_rows)) i += 1 progress_dialog.ensure_finish() csv_file.close() return db def _populate_model(self, db, model): model.clear() #order_by_indices = None #order_by_dirs = None order_by = None if self.sort_col != None and self.sort_order != None: order_by = '%s%s %s' % (CSVDatabase.COL_PREFIX, self.sort_col, self.sort_order) # order_by_indices = [self.sort_col] # order_by_dirs = [self.sort_order] #print self.where_cond, self.where_params, model.get_n_columns() rows = db.select( CSVDatabase.TABLE_NAME, [('%s%d' % (CSVDatabase.COL_PREFIX, i)) for i in range(model.get_n_columns())], where_cond=self.where_cond, params=self.where_params, order_by=order_by ) #rows = db.csv_select_by_index(where_cond=self.where_cond, params=self.where_params, order_by_indices=order_by_indices, order_by_dirs=order_by_dirs) for cur_row in rows: cur_row = list(cur_row) for i in range(1, len(cur_row) - 1): cur_row[i] = str(cur_row[i]) cur_row[-1] = bool(cur_row[-1]) model.append(cur_row) def _filter_callback(self, filters, db, model, search_entry, col_headers): #Filters will be None if the user clicked cancel in the FilterWindow if filters != None: self.filters = filters where_cond, where_params = FilterWindow.get_sql_where_cond(self.filters) self.where_cond = where_cond self.where_params = where_params self._populate_model(db, model) desc = FilterWindow.get_filters_desc(filters, col_headers) search_entry.set_text(desc) def _update_filters(self, db, model, col_headers, search_entry): FilterWindow(self.filters, col_headers, lambda filters: self._filter_callback(filters, db, model, search_entry, col_headers)) def _clean_up(self): if self.wav_parser: self.wav_parser.close() gtk.main_quit() def _build_toolbar(self, db, treeview, col_headers): toolbar = gtk.Toolbar() clear_img_path = UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.CLEAR, UIUtils.BUTTON_ICON_SIZES.PX16) #clear_pixbuf = gtk.gdk.pixbuf_new_from_file(clear_img_path) clear_pixbuf = GdkPixbuf.Pixbuf.new_from_file(clear_img_path) search_entry = gtk.Entry() search_entry.set_sensitive(False) search_entry.set_text(FilterWindow.get_filters_desc(self.filters, col_headers)) filter_button = UIUtils.create_button('Filters', UIUtils.BUTTON_ICONS.FILTER, UIUtils.BUTTON_ICON_SIZES.PX16) filter_button.connect('clicked', lambda w: self._update_filters(db, treeview.get_model(), col_headers, search_entry)) play_button = UIUtils.create_button('Play', UIUtils.BUTTON_ICONS.PLAY, UIUtils.BUTTON_ICON_SIZES.PX16) play_button.connect('clicked', lambda w: self._play_selected_row(col_headers, treeview)) praat_button = UIUtils.create_button('Praat', UIUtils.BUTTON_ICONS.PRAAT, UIUtils.BUTTON_ICON_SIZES.PX16) praat_button.connect('clicked', lambda w: self._open_in_praat(col_headers, treeview)) export_button = UIUtils.create_button('Export', UIUtils.BUTTON_ICONS.EXPORT, UIUtils.BUTTON_ICON_SIZES.PX16) export_button.connect('clicked', lambda w: self._export(treeview, col_headers, db)) context_label = gtk.Label('Context') context_adj = gtk.Adjustment(value=0, lower=0, upper=99, step_increment=1) self.context_spinner = gtk.SpinButton() self.context_spinner.set_adjustment(context_adj) self.context_spinner.set_numeric(True) spacer = gtk.SeparatorToolItem() spacer.set_draw(False) #don't draw a vertical line spacer.set_expand(True) filter_label = gtk.Label('Filter state:') for widget in [filter_label, search_entry, filter_button, praat_button, play_button, self.context_spinner, context_label]: tool_item = gtk.ToolItem() tool_item.add(widget) if widget == search_entry: tool_item.set_expand(True) toolbar.insert(tool_item, -1) toolbar.insert(spacer, -1) tool_item = gtk.ToolItem() tool_item.add(export_button) toolbar.insert(tool_item, -1) return toolbar #Takes a time string in one of two formats, and returns a float set to the absolute number of seconds it represents. #time_str must be either a string in any of the formats accepted by BackendUtils.time_str_to_float(), or a stringified float. # (e.g. ''00:00:3.45 or '3.45') def _get_abs_time(self, time_str): sec = None if ':' in time_str: sec = BackendUtils.time_str_to_float(time_str) else: sec = float(time_str) return sec def _get_sel_row_clip_info(self, col_headers, treeview): start = None end = None model, it = treeview.get_selection().get_selected() if it: #find start and end column indices (if not already found) if self.sound_col_fcns == None: start_index, end_index, dur_index, el_time_index = self._get_sound_col_indices(col_headers) if start_index > -1 and end_index > -1: self.sound_col_fcns = ( lambda model, it: self._get_abs_time(model.get_value(it, start_index)), lambda model, it: self._get_abs_time(model.get_value(it, end_index)), ) elif dur_index > -1 and el_time_index > -1: self.sound_col_fcns = ( lambda model, it: self._get_abs_time(float(model.get_value(it, el_time_index))), lambda model, it: self._get_abs_time(float(model.get_value(it, el_time_index)) + float(model.get_value(it, dur_index))), ) else: error_msg = 'The program was unable to derive the sound clip start and end times from the columns.\n' error_msg += '\nColumn headers must include:\n' error_msg += '-One start name: %s\n' % (' or '.join(['"%s"' % (name) for name in MainWindow.START_COL_NAMES])) error_msg += '-One end name: %s\n' % (' or '.join(['"%s"' % (name) for name in MainWindow.END_COL_NAMES])) error_msg += '\nOr alternatively:\n' error_msg += '-One duration name: %s\n' % (' or '.join(['"%s"' % (name) for name in MainWindow.DUR_COL_NAMES])) error_msg += '-One elapsed time name: %s\n' % (' or '.join(['"%s"' % (name) for name in MainWindow.EL_TIME_COL_NAMES])) error_msg += '\nPlease make sure your input spreadsheet contains one of these pairs.' UIUtils.show_message_dialog(error_msg) if self.sound_col_fcns != None and self.wav_filename: try: start = float( self.sound_col_fcns[0](treeview.get_model(), it) ) end = float( self.sound_col_fcns[1](treeview.get_model(), it) ) except ValueError as err: UIUtils.show_message_dialog('The program was unable to determine start and end times for this row.') start = None end = None print err else: UIUtils.show_no_sel_dialog() return start, end def _open_in_praat(self, headers, treeview): start, end = self._get_sel_row_clip_info(headers, treeview) if self.wav_filename and start != None and end != None: PraatInterop.open_praat() PraatInterop.send_commands(PraatInterop.get_open_clip_script(start, end, self.wav_filename)) def _play_selected_row(self, col_headers, treeview): #this populates self.wav_parser and self.wav_filename, if the file exists start, end = self._get_sel_row_clip_info(col_headers, treeview) if self.wav_parser and start != None and end != None: start = max(0, start - self.context_spinner.get_value()) end = min(self.wav_parser.get_sound_len(), end + self.context_spinner.get_value()) self.wav_parser.play_clip(start, end) #return the focus to the currenly selected row in the treeview, so the user can immediately press the down arrow to move on to the next row treeview.grab_focus() def _find_col(self, header_dict, key_list): index = -1 i = 0 while index < 0 and i < len(key_list): if key_list[i] in header_dict: index = header_dict[ key_list[i] ] i += 1 return index def _get_sound_col_indices(self, headers): header_dict = dict( zip(headers, range(len(headers))) ) start = self._find_col(header_dict, MainWindow.START_COL_NAMES) end = self._find_col(header_dict, MainWindow.END_COL_NAMES) dur = self._find_col(header_dict, MainWindow.DUR_COL_NAMES) el_time = self._find_col(header_dict, MainWindow.EL_TIME_COL_NAMES) return (start, end, dur, el_time) def _export(self, treeview, col_headers, db): write_filename, open_now = UIUtils.save_file(filters=[UIUtils.CSV_FILE_FILTER, UIUtils.ALL_FILE_FILTER], open_now_opt=True) if write_filename: #if they didn't click cancel #lag_time_cutoff = float( UIUtils.show_entry_dialog(None, 'Lag time cutoff for counts: ', default_text='2', validate_regex=r'^-?\d+(\.\d+)?$', invalid_msg='Please enter a number.') ) lag_time_cutoff = 2.0 if write_filename and lag_time_cutoff != None: #if user did not click cancel (note: lag time cutoff could be 0) if not write_filename.lower().endswith('.csv'): write_filename += '.csv' try: csv_file = open(write_filename, 'wb') writer = csv.writer(csv_file, quoting=csv.QUOTE_ALL) cols = treeview.get_columns() visible_col_indices = filter(lambda i: cols[i].get_visible(), range(len(cols))) filtered_headers = [col_headers[i] for i in visible_col_indices] writer.writerow(filtered_headers) progress_dialog = ProgressDialog(title='Exporting to file', phases=['Exporting...']) progress_dialog.show() num_rows = len(treeview.get_model()) row_index = 1 #this is awkward, but there is no way to pull all the rows out of the model in one shot, so we have to use a non-indexed for loop and manually track the index for row in treeview.get_model(): filtered_row = [row[i] for i in visible_col_indices] writer.writerow(filtered_row) progress_dialog.set_fraction(float(row_index) / float(num_rows)) row_index += 1 # export_stats = self._get_export_stats(lag_time_cutoff, db, col_headers) # if export_stats: # for row in export_stats: # writer.writerow(row) progress_dialog.ensure_finish() csv_file.close() if open_now: subprocess.Popen(['%s' % DBConstants.SETTINGS.SPREADSHEET_PATH, write_filename]) else: UIUtils.show_message_dialog('Data exported successfully.') #UIUtils.show_message_dialog('Data export completed.') except Exception as err: UIUtils.show_message_dialog('Unable to export data - please make sure the destination file is not already open in another program.') raise err def _get_export_stats(self, lag_time_cutoff, db, col_headers): stats = [] cols = { 'Speaker': None, 'Sentence Type': None, 'Lag Time': None, 'Marked': None, } for i in range(1, len(col_headers)): if col_headers[i] in cols: cols[col_headers[i]] = '%s%d' % (CSVDatabase.COL_PREFIX, i - 1) have_all_cols = reduce(lambda accum, key: cols[key] != None and accum, cols, True) if have_all_cols: #Count of Marked MQ -> B #"SELECT count(d1.id) FROM data d1, data d2 WHERE d1.id = d2.id - 1 AND d1.sentence_type='Q' AND d2.speaker='B' AND d1.marked=1 AND d1.lag_time < cutoff" where_cond = "d1.id = d2.id - 1 AND d1.%s = 'M' AND d1.%s = 'Q' AND d2.%s = 'B' AND d1.%s = 1 AND d1.%s <= ?" % (cols['Speaker'], cols['Sentence Type'], cols['Speaker'], cols['Marked'], cols['Lag Time']) rows = db.select( '%s d1, %s d2' % (CSVDatabase.TABLE_NAME, CSVDatabase.TABLE_NAME), ['count(d1.id)'], where_cond=where_cond, params=[lag_time_cutoff], ) if rows: stats.append( ['Count of "Marked MQ -> B": %d' % (int(rows[0][0]))] ) #Avg lag time on mother utterance for (MQ or MD) -> B where_cond = "d1.id = d2.id - 1 AND d1.%s= 'M' AND (d1.%s = 'Q' OR d1.%s = 'D') AND d2.%s = 'B' AND d1.%s <= ?" % (cols['Speaker'], cols['Sentence Type'], cols['Sentence Type'], cols['Speaker'], cols['Lag Time']) rows = db.select( '%s d1, %s d2' % (CSVDatabase.TABLE_NAME, CSVDatabase.TABLE_NAME), ['avg(d1.%s)' % (cols['Lag Time'])], where_cond=where_cond, params=[lag_time_cutoff], ) if rows: val = str(None) if len(rows[0]) and rows[0][0] != None: val = '%0.3f' % (float(rows[0][0])) stats.append( ['Avg Lag Time for "(MQ or MD) -> B": %s' % (val)] ) #Avg of lag time (first in pair) on mother utterance for MQ -> MQ where_cond = "d1.id = d2.id - 1 AND d1.%s= 'M' AND d1.%s = 'Q' AND d2.%s = 'M' AND d2.%s = 'Q' AND d1.%s <= ?" % (cols['Speaker'], cols['Sentence Type'], cols['Speaker'], cols['Sentence Type'], cols['Lag Time']) rows = db.select( '%s d1, %s d2' % (CSVDatabase.TABLE_NAME, CSVDatabase.TABLE_NAME), ['avg(d1.%s)' % (cols['Lag Time'])], where_cond=where_cond, params=[lag_time_cutoff], ) if rows: val = str(None) if len(rows[0]) and rows[0][0] != None: val = '%0.3f' % (float(rows[0][0])) stats.append( ['Avg Lag Time for "MQ -> MQ": %s' % (val)] ) #Avg of lag time (first in pair) on mother utterance for MD -> MD where_cond = "d1.id = d2.id - 1 AND d1.%s= 'M' AND d1.%s = 'D' AND d2.%s = 'M' AND d2.%s = 'D' AND d1.%s <= ?" % (cols['Speaker'], cols['Sentence Type'], cols['Speaker'], cols['Sentence Type'], cols['Lag Time']) rows = db.select( '%s d1, %s d2' % (CSVDatabase.TABLE_NAME, CSVDatabase.TABLE_NAME), ['avg(d1.%s)' % (cols['Lag Time'])], where_cond=where_cond, params=[lag_time_cutoff], ) if rows: val = str(None) if len(rows[0]) and rows[0][0] != None: val = '%0.3f' % (float(rows[0][0])) stats.append( ['Avg Lag Time for "MD -> MD": %s' % (val)] ) #Avg of lag time (first in pair) on baby utterance for B -> (MQ or MD) where_cond = "d1.id = d2.id - 1 AND d1.%s = 'B' AND d2.%s = 'M' AND d1.%s <= ?" % (cols['Speaker'], cols['Speaker'], cols['Lag Time']) rows = db.select( '%s d1, %s d2' % (CSVDatabase.TABLE_NAME, CSVDatabase.TABLE_NAME), ['avg(d1.%s)' % (cols['Lag Time'])], where_cond=where_cond, params=[lag_time_cutoff], ) if rows: val = str(None) if len(rows[0]) and rows[0][0] != None: val = '%0.3f' % (float(rows[0][0])) stats.append( ['Avg Lag Time for "B -> (MQ or MD)": %s' % (val)] ) if stats: stats.insert(0, ['File Stats (lag time <= %0.3f)' % (lag_time_cutoff)]) return stats
def _play_clip(self, start, end): wav_parser = WavParser(self.test2.wav_filename) wav_parser.play_clip(start, end) wav_parser.close()
class VerificationWindow(): ERROR_STATES = Enum(['NONE', 'WARNING', 'ERROR']) def __init__(self, filename, progress_dialog): self.logger = logging.getLogger(__name__) self.window = gtk.Window(gtk.WindowType.TOPLEVEL) self.window.set_title('Transcription Verifier') self.window.connect('destroy', lambda x: self.window.destroy()) self.window.set_border_width(10) self.window.set_default_size(580, 500) self.trs_parser = TRSParser(filename) self.trs_parser.parse( progress_update_fcn=progress_dialog.set_fraction, progress_next_phase_fcn=progress_dialog.next_phase, remove_bad_trans_codes=False) self.wav_parser = None progress_dialog.next_phase() self.filter_errors = True self.toolbar = self.build_toolbar() self.treeview = self.build_treeview(progress_dialog.set_fraction) self.treeview.expand_all() scrolled_win = gtk.ScrolledWindow() scrolled_win.set_policy(gtk.PolicyType.AUTOMATIC, gtk.PolicyType.AUTOMATIC) scrolled_win.add(self.treeview) vbox = gtk.VBox(False, 2) vbox.pack_start(self.toolbar, False, False, 0) vbox.pack_start(scrolled_win, True, True, 0) self.window.add(vbox) self.window.show_all() def build_toolbar(self): toolbar = gtk.Toolbar() toolbar.set_orientation(gtk.Orientation.HORIZONTAL) filter_errors_button = gtk.ToggleToolButton() filter_errors_button.set_active( True ) #set this before the connecting the clicked handler so it doesn't cause trouble filter_errors_button.connect( 'toggled', lambda w: self.toggle_filter_errors(w.get_active())) filter_errors_icon = gtk.Image() filter_errors_icon.set_from_file( UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.FLAG)) filter_errors_button.set_label('Show Errors Only') filter_errors_button.set_icon_widget(filter_errors_icon) expand_button = gtk.ToolButton() expand_icon = gtk.Image() expand_icon.set_from_file( UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.EXPAND)) expand_button.set_label('Expand All') expand_button.set_icon_widget(expand_icon) expand_button.connect('clicked', lambda w: self.treeview.expand_all()) collapse_button = gtk.ToolButton() collapse_icon = gtk.Image() collapse_icon.set_from_file( UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.COLLAPSE)) collapse_button.set_label('Collapse All') collapse_button.set_icon_widget(collapse_icon) collapse_button.connect('clicked', lambda w: self.treeview.collapse_all()) rescan_button = gtk.ToolButton() rescan_icon = gtk.Image() rescan_icon.set_from_file( UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.REFRESH)) rescan_button.set_label('Rescan File') rescan_button.set_icon_widget(rescan_icon) rescan_button.connect('clicked', lambda w: self._rescan_file()) play_seg_button = gtk.ToolButton() play_icon = gtk.Image() play_icon.set_from_file( UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.PLAY)) play_seg_button.set_label('Play Seg') play_seg_button.set_icon_widget(play_icon) play_seg_button.connect('clicked', lambda w: self.play_selected_seg()) close_button = gtk.ToolButton() close_icon = gtk.Image() close_icon.set_from_file( UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.CLOSE)) close_button.set_label('Close') close_button.set_icon_widget(close_icon) close_button.connect('clicked', lambda w: self.window.destroy()) exit_button = gtk.ToolButton() exit_icon = gtk.Image() exit_icon.set_from_file( UIUtils.get_icon_path(UIUtils.BUTTON_ICONS.EXIT)) exit_button.set_label('Exit') exit_button.set_icon_widget(exit_icon) exit_button.connect('clicked', lambda w: gtk.main_quit()) toolbar.insert(filter_errors_button, -1) toolbar.insert(expand_button, -1) toolbar.insert(collapse_button, -1) toolbar.insert(gtk.SeparatorToolItem(), -1) toolbar.insert(play_seg_button, -1) toolbar.insert(rescan_button, -1) toolbar.insert(gtk.SeparatorToolItem(), -1) toolbar.insert(close_button, -1) toolbar.insert(exit_button, -1) return toolbar def _rescan_file(self): self.window.set_sensitive(False) progress_dialog = ProgressDialog( 'Processing File...', ['Parsing trs file...', 'Validating data...', 'Building UI...']) progress_dialog.show() #this causes the parser to invalidate all cache, re-open and re-parse the file self.trs_parser.re_parse( progress_update_fcn=progress_dialog.set_fraction, progress_next_phase_fcn=progress_dialog.next_phase) #build a new treeview model based on the new data progress_dialog.next_phase() filter_model = self._build_tree_store(progress_dialog.set_fraction) self.treeview.set_model(filter_model) #Presumably the most common cause for rescanning is to check if errors have been fixed. #If the error filter is on, automatically expand all rows to show any remaining errors. if self.filter_errors: self.treeview.expand_all() self.window.set_sensitive(True) def _build_tree_store(self, progress_update_fcn): #segment/utter id, description, error_state (0 = none, 1 = warning, 2 = error) tree_store = gtk.TreeStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_INT) #note: these may be errors or warnings cur_utter = 0 for seg in self.trs_parser.parse(): seg_speakers = '' if seg.speakers: for i in range(len(seg.speakers)): seg_speakers += seg.speakers[i].speaker_codeinfo.get_code() if i < len(seg.speakers) - 1: seg_speakers += ' + ' else: seg_speakers = ' - ' seg_iter = tree_store.append(None, [ seg.num, '%s [%s - %s]' % (seg_speakers, BackendUtils.get_time_str( seg.start), BackendUtils.get_time_str(seg.end)), VerificationWindow.ERROR_STATES.NONE ]) for utter in seg.utters: speaker_cd = '?' #question mark indicates an error occured - if we have utter.speaker, we should have an utter code. Errors occur if the utter code isn't in the DB lookup table (which means that utter.speaker != None, but utter.speaker.speaker_codeinfo == None. This is the condition that falls through the if-else blocks below). if utter.speaker: if utter.speaker.speaker_codeinfo: speaker_cd = utter.speaker.speaker_codeinfo.get_code() else: speaker_cd = ' - ' desc_str = '%s [%s - %s]' % ( speaker_cd, BackendUtils.get_time_str( utter.start), BackendUtils.get_time_str(utter.end)) if utter.lena_notes: desc_str += ' %s' % (utter.lena_notes) if utter.trans_phrase: desc_str += ' %s' % (utter.trans_phrase) if utter.lena_codes: desc_str += ' |%s|' % ('|'.join(utter.lena_codes)) if utter.trans_codes: if not utter.lena_codes: desc_str += ' |' desc_str += '%s|' % ('|'.join(utter.trans_codes)) utter_iter = tree_store.append( seg_iter, [utter.id, desc_str, VerificationWindow.ERROR_STATES.NONE]) cur_utter += 1 progress_update_fcn( float(cur_utter) / float(self.trs_parser.total_utters)) error_list = self.trs_parser.error_collector.get_errors_by_utter( utter) for error in error_list: error_type = VerificationWindow.ERROR_STATES.ERROR if isinstance(error, ParserWarning): error_type = VerificationWindow.ERROR_STATES.WARNING error_iter = tree_store.append( utter_iter, [-1, '%s' % (error.msg), error_type]) parent_it = utter_iter while parent_it: parent_error_type = tree_store.get_value(parent_it, 2) if parent_error_type < error_type: tree_store.set_value(parent_it, 2, error_type) parent_it = tree_store.iter_parent(parent_it) filter_model = tree_store.filter_new() filter_model.set_visible_func(self.filter) return filter_model def build_treeview(self, progress_update_fcn): filter_model = self._build_tree_store(progress_update_fcn) treeview = gtk.TreeView(filter_model) col = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0) col.set_visible(False) treeview.append_column(col) renderer = gtk.CellRendererText() col = gtk.TreeViewColumn('Description', renderer, text=1) col.set_cell_data_func(renderer, self.cell_render_fcn) treeview.append_column(col) col = gtk.TreeViewColumn('Error State', gtk.CellRendererText(), text=2) col.set_visible(False) treeview.append_column(col) return treeview def cell_render_fcn(self, col, cell_renderer, model, it, user_data=None): error_state = model.get_value(it, 2) if error_state == VerificationWindow.ERROR_STATES.WARNING: cell_renderer.set_property('foreground', 'orange') elif error_state == VerificationWindow.ERROR_STATES.ERROR: cell_renderer.set_property('foreground', 'red') else: cell_renderer.set_property('foreground', 'black') return #returns true if row pointed to by 'it' should be visible def filter(self, model, it, user_data): result = True if self.filter_errors: result = model.get_value(it, 2) > VerificationWindow.ERROR_STATES.NONE return result def toggle_filter_errors(self, filter_errors): self.filter_errors = not self.filter_errors self.treeview.get_model().refilter() def play_selected_seg(self): (model, it) = self.treeview.get_selection().get_selected() if it: #if they've selected an error row, find the top level parent (the segment) and use it instead parent = model.iter_parent(it) while parent: it = parent parent = model.iter_parent(it) seg_num = model.get_value(it, 0) if it else None seg = self.trs_parser.parse()[seg_num] if not self.wav_parser: dialog = gtk.FileChooserDialog( title='Select WAV File', action=gtk.FileChooserAction.OPEN, buttons=(gtk.STOCK_CANCEL, gtk.ResponseType.CANCEL, gtk.STOCK_OPEN, gtk.ResponseType.OK)) dialog.set_default_response(gtk.ResponseType.OK) for filter_opt in (('wav Files', '*.wav'), ('All Files', '*')): file_filter = gtk.FileFilter() file_filter.set_name(filter_opt[0]) file_filter.add_pattern(filter_opt[1]) dialog.add_filter(file_filter) response = dialog.run() if response == gtk.ResponseType.OK: filename = dialog.get_filename() self.wav_parser = WavParser(filename) dialog.destroy() if self.wav_parser: self.wav_parser.play_seg(seg) else: UIUtils.show_no_sel_dialog() else: UIUtils.show_no_sel_dialog()
class TestWindow(): def __init__(self, check): self.logger = logging.getLogger(__name__) self.check = check self.wav_parser = WavParser(self.check.wav_filename) self.window = gtk.Window(gtk.WindowType.TOPLEVEL) self.window.set_title('Testing') self.window.connect( 'destroy', lambda w: self._exit()) #will save current input and exit self.window.set_border_width(10) self.window.set_default_size(350, 200) self.wo_form = Form() self.wo_form.handler_man = HandlerManager() self.w_form = Form() self.w_form.handler_man = HandlerManager() self.button_form = Form() self.button_form.handler_man = HandlerManager() self.progress_bar = self._build_progress_bar() self.w_context_frame = self._build_w_context_frame() button_box = self._build_button_box() self.wo_context_frame = self._build_wo_context_frame() self.wo_context_checkbox = self._build_wo_context_checkbox( self.wo_context_frame) vbox = gtk.VBox() vbox.pack_start(self.progress_bar, True, True, 0) vbox.pack_start(self.wo_context_checkbox, True, True, 0) vbox.pack_start(self.wo_context_frame, True, True, 0) vbox.pack_start(self.w_context_frame, True, True, 0) vbox.pack_end(button_box, True, True, 0) self.window.add(vbox) self.window.show_all() self._update_progress_bar() self._toggle_wo_context_frame(self.wo_context_checkbox.get_active()) self._set_ui_to_cur_test() def _build_progress_bar(self): progress_bar = gtk.ProgressBar() progress_bar.set_orientation(gtk.Orientation.HORIZONTAL) return progress_bar def _set_ui_to_cur_test(self): cur_test = self.check.tests[self.check.test_index] cat_index = 0 if cur_test.category_input != None: cat_index = cur_test.category_input - DBConstants.COMBO_OPTIONS[ DBConstants.COMBO_GROUPS.RELIABILITY_CATEGORIES].EMPTY self.w_form.type_combo.set_active(cat_index) self.w_form.uncertain_checkbox.set_active(cur_test.is_uncertain or False) self.w_form.context_pad_spinner.set_value(cur_test.context_padding) self.w_form.syllables_spinner.set_value( cur_test. syllables_w_context if cur_test.syllables_w_context != None else 0) #these boundaries are displayed without context, but opening in praat or listening will play with context seg_start, seg_end = self._get_bounds() 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)) 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)) self.wo_form.syllables_spinner.set_value( cur_test.syllables_wo_context if cur_test.syllables_wo_context != None else 0) 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 def _build_w_context_frame(self): cur_test = self.check.tests[self.check.test_index] frame = gtk.Frame(label='With Context') #table = gtk.Table(6, 4, False) grid = gtk.Grid() play_button = UIUtils.create_button('', UIUtils.BUTTON_ICONS.PLAY, UIUtils.BUTTON_ICON_SIZES.PX32) #table.attach(play_button, 0, 1, 0, 1, gtk.EXPAND, gtk.EXPAND) grid.attach(play_button, 0, 0, 1, 1) type_label = gtk.Label('Seg Category:') #table.attach(type_label, 1, 2, 0, 1, gtk.EXPAND, gtk.EXPAND) grid.attach(type_label, 1, 0, 1, 1) self.w_form.type_combo = UIUtils.build_options_combo( DBConstants.COMBO_GROUPS.RELIABILITY_CATEGORIES) #table.attach(self.w_form.type_combo, 2, 3, 0, 1, gtk.EXPAND, gtk.EXPAND) grid.attach(self.w_form.type_combo, 2, 0, 1, 1) uncertain_label = gtk.Label('Other/Uncertain:') self.w_form.uncertain_checkbox = gtk.CheckButton() #table.attach(uncertain_label, 1, 2, 1, 2, gtk.EXPAND, gtk.EXPAND) grid.attach(uncertain_label, 1, 1, 1, 1) #table.attach(self.w_form.uncertain_checkbox, 2, 3, 1, 2, gtk.EXPAND|gtk.FILL, gtk.EXPAND|gtk.FILL) grid.attach(self.w_form.uncertain_checkbox, 2, 1, 1, 1) padding_label = gtk.Label('Padding:') context_pad_adj = gtk.Adjustment(value=0, lower=1, upper=1000, step_incr=1, page_incr=5) self.w_form.context_pad_spinner = gtk.SpinButton(context_pad_adj) #table.attach(padding_label, 1, 2, 2, 3, gtk.EXPAND, gtk.EXPAND) grid.attach(padding_label, 1, 2, 1, 1) #table.attach(self.w_form.context_pad_spinner, 2, 3, 2, 3, gtk.EXPAND, gtk.EXPAND) grid.attach(self.w_form.context_pad_spinner, 2, 2, 1, 1) self.w_form.handler_man.add_handler( play_button, 'clicked', lambda w: self.play_seg( int(self.w_form.context_pad_spinner.get_value()))) syllables_label = gtk.Label('Syllables:') syllables_adj = gtk.Adjustment(value=0, lower=0, upper=1000, step_incr=1, page_incr=5) self.w_form.syllables_spinner = gtk.SpinButton(syllables_adj) #table.attach(syllables_label, 1, 2, 3, 4, gtk.EXPAND, gtk.EXPAND) grid.attach(syllables_label, 1, 3, 1, 1) #table.attach(self.w_form.syllables_spinner, 2, 3, 3, 4, gtk.EXPAND, gtk.EXPAND) grid.attach(self.w_form.syllables_spinner, 2, 3, 1, 1) user_start_label = gtk.Label('User Start:') self.w_form.user_start_entry = gtk.Entry() self.w_form.user_start_entry.set_width_chars(10) #table.attach(user_start_label, 1, 2, 4, 5, gtk.EXPAND, gtk.EXPAND) grid.attach(user_start_label, 1, 4, 1, 1) #table.attach(self.w_form.user_start_entry, 2, 3, 4, 5, gtk.EXPAND, gtk.EXPAND) grid.attach(self.w_form.user_start_entry, 2, 5, 1, 1) user_end_label = gtk.Label('User End:') self.w_form.user_end_entry = gtk.Entry() self.w_form.user_end_entry.set_width_chars(10) #table.attach(user_end_label, 1, 2, 5, 6, gtk.EXPAND, gtk.EXPAND) grid.attach(user_end_label, 1, 5, 1, 1) #table.attach(self.w_form.user_end_entry, 2, 3, 5, 6, gtk.EXPAND, gtk.EXPAND) grid.attach(self.w_form.user_end_entry, 2, 5, 1, 1) open_praat_button = gtk.Button('Open Praat') #table.attach(open_praat_button, 3, 4, 4, 5, gtk.EXPAND, gtk.EXPAND) grid.attach(open_praat_button, 3, 4, 1, 1) self.w_form.handler_man.add_handler(open_praat_button, 'clicked', lambda w: self._open_praat()) close_praat_button = gtk.Button('Close Praat') #table.attach(close_praat_button, 3, 4, 5, 6, gtk.EXPAND, gtk.EXPAND) grid.attach(close_praat_button, 3, 5, 1, 1) self.w_form.handler_man.add_handler(close_praat_button, 'clicked', lambda w: self._close_praat()) frame.add(grid) return frame def _build_wo_context_frame(self): cur_test = self.check.tests[self.check.test_index] frame = gtk.Frame(label='Without Context') #table = gtk.Table(1, 3, False) grid = gtk.Grid() self.wo_form.play_button = UIUtils.create_button( '', UIUtils.BUTTON_ICONS.PLAY, UIUtils.BUTTON_ICON_SIZES.PX32) self.wo_form.handler_man.add_handler(self.wo_form.play_button, 'clicked', lambda w: self.play_seg(0)) #table.attach(self.wo_form.play_button, 0, 1, 0, 1, gtk.EXPAND, gtk.EXPAND) grid.attach(self.wo_form.play_button, 0, 0, 1, 1) syllables_label = gtk.Label('Syllables:') syllables_adj = gtk.Adjustment(value=0, lower=0, upper=1000, step_incr=1, page_incr=5) self.wo_form.syllables_spinner = gtk.SpinButton(syllables_adj) #table.attach(syllables_label, 1, 2, 0, 1, gtk.EXPAND, gtk.EXPAND) grid.attach(syllables_label, 1, 0, 1, 1) #table.attach(self.wo_form.syllables_spinner, 2, 3, 0, 1, gtk.EXPAND, gtk.EXPAND) grid.attach(self.wo_form.syllables_spinner, 2, 0, 1, 1) frame.add(grid) return frame def _toggle_wo_context_frame(self, visible): if visible: self.wo_context_frame.show() else: self.wo_context_frame.hide() self.check.tests[self.check.test_index].syllables_wo_context = None self.wo_form.syllables_spinner.get_adjustment().set_value(1) def _build_wo_context_checkbox(self, wo_context_frame): cur_test = self.check.tests[self.check.test_index] checkbox = gtk.CheckButton(label='Test Without Context') checkbox.connect( 'toggled', lambda w: self._toggle_wo_context_frame(w.get_active())) checkbox.set_active(cur_test.syllables_wo_context != None) return checkbox def _build_button_box(self): box = gtk.HButtonBox() box.set_layout(gtk.ButtonBoxStyle.EDGE) self.button_form.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK) self.button_form.handler_man.add_handler(self.button_form.back_button, 'clicked', lambda w: self._back()) self.button_form.save_button = UIUtils.create_button( 'Save & Exit', UIUtils.BUTTON_ICONS.SAVE, UIUtils.BUTTON_ICON_SIZES.PX32) self.button_form.handler_man.add_handler(self.button_form.save_button, 'clicked', lambda w: self._exit()) self.button_form.forward_button = gtk.Button( stock=gtk.STOCK_GO_FORWARD) self.button_form.handler_man.add_handler( self.button_form.forward_button, 'clicked', lambda w: self._forward()) self._update_step_buttons() box.pack_start(self.button_form.back_button, False, False, 0) box.pack_start(self.button_form.save_button, False, False, 0) box.pack_end(self.button_form.forward_button, False, False, 0) return box def _update_progress_bar(self): if self.check.num_segs == 1: #avoid dividing by zero self.progress_bar.set_fraction(1.0) else: #print self.check.test_index self.progress_bar.set_fraction( float(self.check.test_index) / float(self.check.num_segs - 1)) self.progress_bar.set_text( 'Segment %d of %d' % (self.check.test_index + 1, self.check.num_segs)) def _set_step_button(self, button, stock, label_text, clicked_handler): img = gtk.Image() img.set_from_stock(stock, gtk.ICON_SIZE_BUTTON) button.set_label(label_text) button.set_image(img) self.button_form.handler_man.remove_handlers(button, ['clicked']) self.button_form.handler_man.add_handler(button, 'clicked', clicked_handler) def _update_step_buttons(self): finish_text = 'Finish' forward_text = 'Forward' if self.check.test_index == self.check.num_segs - 1: self._set_step_button(self.button_form.forward_button, gtk.STOCK_OK, finish_text, lambda w: self._finish()) elif self.button_form.forward_button.get_label() == finish_text: self._set_step_button(self.button_form.forward_button, gtk.STOCK_GO_FORWARD, forward_text, lambda w: self._forward()) self.button_form.back_button.set_sensitive(self.check.test_index > 0) def _exit(self, save=True): if save: self.save_input(mark_last_run=True) self.wav_parser.close() self.window.destroy() def _finish(self): if self._validate_cur_test(): self.save_input(mark_last_run=True, mark_as_completed=True) filename, check_results = UIUtils.save_file( filters=[UIUtils.CSV_FILE_FILTER], open_now_opt=True, save_last_location=True) if filename: exporter = ReliabilityExporter(self.check, filename) if exporter.export(): if check_results: subprocess.Popen([ '%s' % DBConstants.SETTINGS.SPREADSHEET_PATH, filename ]) else: UIUtils.show_message_dialog( 'Results exported successfully.') self._exit(False) #we have already saved above else: 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.' ) def _validate_cur_test(self): #validate w_context form w_context_valid = ( self.w_form.type_combo.get_active() != 0 and int(self.w_form.syllables_spinner.get_adjustment().get_value()) > 0 and BackendUtils.is_float(self.w_form.user_start_entry.get_text()) and BackendUtils.is_float(self.w_form.user_end_entry.get_text())) #print 'w_context_valid: %s' % str(w_context_valid) wo_context_valid = (not self.wo_context_checkbox.get_active() or int( self.wo_form.syllables_spinner.get_adjustment().get_value()) > 0) #print 'wo_context_valid: %s' % str(wo_context_valid) is_valid = w_context_valid and wo_context_valid if is_valid: #make sure user-boundaries have been updated #note: they are guarenteed to contain floats at this point because of the w_context_valid condition user_start = float(self.w_form.user_start_entry.get_text()) user_end = float(self.w_form.user_end_entry.get_text()) cur_test = self.check.tests[self.check.test_index] is_valid = (cur_test.seg.start != user_start or cur_test.seg.end != user_end) if not is_valid: is_valid = UIUtils.show_confirm_dialog( 'Segment boundaries have not been adjusted. Continue anyway?' ) else: UIUtils.show_message_dialog( 'Please ensure that all of the inputs have a correct value.') return is_valid def _move(self, incr): self.save_input() self.check.test_index += incr self._update_progress_bar() self._update_step_buttons() self._set_ui_to_cur_test() def _forward(self): if self._validate_cur_test(): self._move(1) def _back(self): self._move(-1) def _get_bounds(self, include_context=False): start_time = self.check.tests[self.check.test_index].seg.start end_time = self.check.tests[self.check.test_index].seg.end if include_context: context_len = self.w_form.context_pad_spinner.get_value_as_int( ) if include_context else 0 start_time = max(start_time - context_len, 0) end_time = min(end_time + context_len, self.wav_parser.get_sound_len()) return start_time, end_time def _open_praat(self): start_time, end_time = self._get_bounds(include_context=True) PraatInterop.open_praat() PraatInterop.send_commands( PraatInterop.get_open_clip_script(start_time, end_time, self.check.wav_filename)) def _close_praat(self): socket = PraatInterop.create_serversocket() PraatInterop.send_commands( PraatInterop.get_sel_bounds_script(self.check.wav_filename)) start_time, end_time = PraatInterop.socket_receive(socket) socket.close() PraatInterop.close_praat() if start_time != end_time: #make sure something was selected #update the inputs in the UI start_time = str(round(float(start_time), 3)) end_time = str(round(float(end_time), 3)) self.w_form.user_start_entry.set_text(start_time) self.w_form.user_end_entry.set_text(end_time) def play_seg(self, context_len): self.wav_parser.play_seg(self.check.tests[self.check.test_index].seg, context_len) def save_input(self, mark_last_run=False, mark_as_completed=False): cur_test = self.check.tests[self.check.test_index] cur_test.category_input = self.w_form.type_combo.get_model()[ self.w_form.type_combo.get_active()][1] cur_test.is_uncertain = self.w_form.uncertain_checkbox.get_active() cur_test.context_padding = int( self.w_form.context_pad_spinner.get_adjustment().get_value()) cur_test.syllables_w_context = int( self.w_form.syllables_spinner.get_adjustment().get_value()) cur_test.seg.user_adj_start = float( self.w_form.user_start_entry.get_text()) cur_test.seg.user_adj_end = float( self.w_form.user_end_entry.get_text()) cur_test.syllables_wo_context = None if self.wo_context_checkbox.get_active(): cur_test.syllables_wo_context = int( self.wo_form.syllables_spinner.get_adjustment().get_value()) db = BLLDatabase() cur_test.db_update_user_inputs(db) self.check.db_update_test_index(db) if mark_last_run: self.check.mark_last_run(db) if mark_as_completed: self.check.mark_as_completed(db) db.close()