def test_error1(self): try: 1/0 except: r = utilities.error_info(sys.exc_info()) assert str(r[0]) == 'division by zero' assert r[1] == "test_utilities.py"
def error_message(task: str, exc_info: tuple) -> None: """ show details about the error """ error_type, error_file_name, error_lineno = utilities.error_info(exc_info) QMessageBox.critical(None, programName, (f"An error occured during {task}.<br>" f"BORIS version: {version.__version__}<br>" f"Error: {error_type}<br>" f"in {error_file_name} " f"at line # {error_lineno}<br><br>" "Please report this problem to improve the software at:<br>" '<a href="https://github.com/olivierfriard/BORIS/issues">https://github.com/olivierfriard/BORIS/issues</a>' ))
def behavior_binary_table(pj: dict): """ ask user for parameters for behavior binary table call create_behavior_binary_table """ result, selected_observations = select_observations.select_observations( pj, MULTIPLE, "Select observations for the behavior binary table") if not selected_observations: return # check if state events are paired out = "" not_paired_obs_list = [] for obs_id in selected_observations: r, msg = project_functions.check_state_events_obs( obs_id, pj[ETHOGRAM], pj[OBSERVATIONS][obs_id]) if not r: out += f"Observation: <strong>{obs_id}</strong><br>{msg}<br>" not_paired_obs_list.append(obs_id) if out: out = f"The observations with UNPAIRED state events will be removed from the analysis<br><br>{out}" results = dialog.Results_dialog() results.setWindowTitle(f"{programName} - Check selected observations") results.ptText.setReadOnly(True) results.ptText.appendHtml(out) results.pbSave.setVisible(False) results.pbCancel.setVisible(True) if not results.exec_(): return selected_observations = [ x for x in selected_observations if x not in not_paired_obs_list ] if not selected_observations: return max_obs_length, selectedObsTotalMediaLength = project_functions.observation_length( pj, selected_observations) if max_obs_length == -1: # media length not available, user choose to not use events return parameters = dialog.choose_obs_subj_behav_category( pj, selected_observations, maxTime=max_obs_length, flagShowIncludeModifiers=True, flagShowExcludeBehaviorsWoEvents=True, by_category=False) if not parameters[SELECTED_SUBJECTS] or not parameters[SELECTED_BEHAVIORS]: QMessageBox.warning(None, programName, "Select subject(s) and behavior(s) to analyze") return # ask for time interval i, ok = QInputDialog.getDouble(None, "Behavior binary table", "Time interval (in seconds):", 1.0, 0.001, 86400, 3) if not ok: return time_interval = utilities.float2decimal(i) ''' iw = dialog.Info_widget() iw.lwi.setVisible(False) iw.resize(350, 200) iw.setWindowFlags(Qt.WindowStaysOnTopHint) iw.setWindowTitle("Behavior binary table") iw.label.setText("Creating the behavior binary table...") iw.show() QApplication.processEvents() ''' results_df = create_behavior_binary_table(pj, selected_observations, parameters, time_interval) ''' iw.hide() ''' if "error" in results_df: QMessageBox.warning(None, programName, results_df["msg"]) return # save results if len(selected_observations) == 1: extended_file_formats = [ "Tab Separated Values (*.tsv)", "Comma Separated Values (*.csv)", "Open Document Spreadsheet ODS (*.ods)", "Microsoft Excel Spreadsheet XLSX (*.xlsx)", "Legacy Microsoft Excel Spreadsheet XLS (*.xls)", "HTML (*.html)" ] file_formats = ["tsv", "csv", "ods", "xlsx", "xls", "html"] file_name, filter_ = QFileDialog().getSaveFileName( None, "Save results", "", ";;".join(extended_file_formats)) if not file_name: return output_format = file_formats[extended_file_formats.index(filter_)] if pathlib.Path(file_name).suffix != "." + output_format: file_name = str(pathlib.Path(file_name)) + "." + output_format # check if file with new extension already exists if pathlib.Path(file_name).is_file(): if dialog.MessageDialog( programName, f"The file {file_name} already exists.", [CANCEL, OVERWRITE]) == CANCEL: return else: items = ("Tab Separated Values (*.tsv)", "Comma separated values (*.csv)", "Open Document Spreadsheet (*.ods)", "Microsoft Excel Spreadsheet XLSX (*.xlsx)", "Legacy Microsoft Excel Spreadsheet XLS (*.xls)", "HTML (*.html)") item, ok = QInputDialog.getItem(None, "Save results", "Available formats", items, 0, False) if not ok: return output_format = re.sub(".* \(\*\.", "", item)[:-1] export_dir = QFileDialog().getExistingDirectory( None, "Choose a directory to save results", os.path.expanduser("~"), options=QFileDialog.ShowDirsOnly) if not export_dir: return mem_command = "" for obs_id in results_df: for subject in results_df[obs_id]: if len(selected_observations) > 1: file_name_with_subject = str( pathlib.Path(export_dir) / utilities.safeFileName(obs_id + "_" + subject)) + "." + output_format else: file_name_with_subject = str( os.path.splitext(file_name)[0] + utilities.safeFileName("_" + subject)) + "." + output_format # check if file with new extension already exists if mem_command != OVERWRITE_ALL and pathlib.Path( file_name_with_subject).is_file(): if mem_command == "Skip all": continue mem_command = dialog.MessageDialog( programName, f"The file {file_name_with_subject} already exists.", [OVERWRITE, OVERWRITE_ALL, "Skip", "Skip all", CANCEL]) if mem_command == CANCEL: return if mem_command in ["Skip", "Skip all"]: continue try: if output_format in ["csv", "tsv", "html"]: with open(file_name_with_subject, "wb") as f: f.write( str.encode(results_df[obs_id][subject].export( output_format))) if output_format in ["ods", "xlsx", "xls"]: with open(file_name_with_subject, "wb") as f: f.write( results_df[obs_id][subject].export(output_format)) except Exception: error_type, error_file_name, error_lineno = utilities.error_info( sys.exc_info()) logging.critical( f"Error in behavior binary table function: {error_type} {error_file_name} {error_lineno}" ) QMessageBox.critical(None, programName, f"Error saving file: {error_type}") return
def observation_to_behavioral_sequences(pj, selected_observations, parameters, behaviors_separator, separated_subjects, timed, file_name): try: with open(file_name, "w", encoding="utf-8") as out_file: for obs_id in selected_observations: # observation id out_file.write("\n" + f"# observation id: {obs_id}" + "\n") # observation description descr = pj[OBSERVATIONS][obs_id]["description"] if "\r\n" in descr: descr = descr.replace("\r\n", "\n# ") elif "\r" in descr: descr = descr.replace("\r", "\n# ") out_file.write(f"# observation description: {descr}\n\n") # media file name if pj[OBSERVATIONS][obs_id][TYPE] in [MEDIA]: out_file.write( f"# Media file name: {', '.join([os.path.basename(x) for x in pj[OBSERVATIONS][obs_id][FILE][PLAYER1]])}\n\n" ) if pj[OBSERVATIONS][obs_id][TYPE] in [LIVE]: out_file.write( f"# Live observation{os.linesep}{os.linesep}") # independent variables if INDEPENDENT_VARIABLES in pj[OBSERVATIONS][obs_id]: out_file.write("# Independent variables\n") for variable in pj[OBSERVATIONS][obs_id][ INDEPENDENT_VARIABLES]: out_file.write( f"# {variable}: {pj[OBSERVATIONS][obs_id][INDEPENDENT_VARIABLES][variable]}\n" ) out_file.write("\n") # one sequence for all subjects if not separated_subjects: out = events_to_behavioral_sequences_all_subj( pj, obs_id, parameters[SELECTED_SUBJECTS], parameters, behaviors_separator) if out: out_file.write(out + "\n") # one sequence by subject if separated_subjects: # selected subjects for subject in parameters[SELECTED_SUBJECTS]: out_file.write( f"\n# {subject if subject else NO_FOCAL_SUBJECT}:\n" ) if not timed: out = events_to_behavioral_sequences( pj, obs_id, subject, parameters, behaviors_separator) if timed: out = events_to_timed_behavioral_sequences( pj, obs_id, subject, parameters, 0.001, behaviors_separator) if out: out_file.write(out + "\n") return True, "" except Exception: raise error_type, error_file_name, error_lineno = utilities.error_info( sys.exc_info()) return False, f"{error_type} {error_file_name} {error_lineno}"
def create_behaviors_bar_plot(pj: dict, selected_observations: list, param: dict, plot_directory: str, output_format: str, plot_colors:list=BEHAVIORS_PLOT_COLORS): """ time budget bar plot Args: pj (dict): project param (dict): parameters plot_directory (str): path of directory output_format (str): image format Returns: dict: """ selected_subjects = param[SELECTED_SUBJECTS] selected_behaviors = param[SELECTED_BEHAVIORS] time_interval = param["time"] start_time = param[START_TIME] end_time = param[END_TIME] parameters = ["duration", "number of occurences"] ok, msg, db_connector = db_functions.load_aggregated_events_in_db(pj, selected_subjects, selected_observations, selected_behaviors) if not ok: return {"error": True, "message": msg} try: # extract all behaviors from ethogram for colors in plot all_behaviors = [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM])] for obs_id in selected_observations: cursor = db_connector.cursor() # distinct behaviors cursor.execute("SELECT distinct behavior FROM aggregated_events WHERE observation = ?", (obs_id,)) distinct_behav = [rows["behavior"] for rows in cursor.fetchall()] # add selected behaviors that are not observed ''' if not param[EXCLUDE_BEHAVIORS]: for behavior in selected_behaviors: if [x for x in distinct_behav if x == behavior] == []: distinct_behav.append(behavior) ''' # distinct subjects cursor.execute("SELECT distinct subject FROM aggregated_events WHERE observation = ?", (obs_id,)) distinct_subjects = [rows["subject"] for rows in cursor.fetchall()] behaviors = init_behav(pj[ETHOGRAM], distinct_subjects, distinct_behav, parameters) # plot creation if len(distinct_subjects) > 1: fig, axs = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) fig2, axs2 = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) else: fig, ax = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) axs = np.ndarray(shape=(1), dtype=type(ax)) axs[0] = ax fig2, ax2 = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) axs2 = np.ndarray(shape=(1), dtype=type(ax2)) axs2[0] = ax2 fig.suptitle("Durations of behaviors") fig2.suptitle("Number of occurences of behaviors") # if modifiers not to be included set modifiers to "" cursor.execute("UPDATE aggregated_events SET modifiers = ''") # time obs_length = project_functions.observation_total_length(pj[OBSERVATIONS][obs_id]) if obs_length == -1: obs_length = 0 if param["time"] == TIME_FULL_OBS: min_time = float(0) max_time = float(obs_length) if param["time"] == TIME_EVENTS: try: min_time = float(pj[OBSERVATIONS][obs_id][EVENTS][0][0]) except Exception: min_time = float(0) try: max_time = float(pj[OBSERVATIONS][obs_id][EVENTS][-1][0]) except Exception: max_time = float(obs_length) if param["time"] == TIME_ARBITRARY_INTERVAL: min_time = float(start_time) max_time = float(end_time) cursor.execute("UPDATE aggregated_events SET start = ? WHERE observation = ? AND start < ? AND stop BETWEEN ? AND ?", (min_time, obs_id, min_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?", (max_time, obs_id, max_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?", (min_time, max_time, obs_id, min_time, max_time, )) for ax_idx, subject in enumerate(sorted(distinct_subjects)): for behavior in distinct_behav: # number of occurences cursor.execute(("SELECT COUNT(*) AS count FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ?"), (obs_id, subject, behavior, )) for row in cursor.fetchall(): behaviors[subject][behavior]["number of occurences"] = 0 if row["count"] is None else row["count"] # total duration if STATE in project_functions.event_type(behavior, pj[ETHOGRAM]): cursor.execute(("SELECT SUM(stop - start) AS duration FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ?"), (obs_id, subject, behavior, )) for row in cursor.fetchall(): behaviors[subject][behavior]["duration"] = 0 if row["duration"] is None else row["duration"] durations, n_occurences, colors, x_labels, colors_duration, x_labels_duration = [], [], [], [], [], [] for behavior in sorted(distinct_behav): if param[EXCLUDE_BEHAVIORS] and behaviors[subject][behavior]["number of occurences"] == 0: continue n_occurences.append(behaviors[subject][behavior]["number of occurences"]) x_labels.append(behavior) try: colors.append(utilities.behavior_color(plot_colors, all_behaviors.index(behavior))) except Exception: colors.append("darkgray") if STATE in project_functions.event_type(behavior, pj[ETHOGRAM]): durations.append(behaviors[subject][behavior]["duration"]) x_labels_duration.append(behavior) try: colors_duration.append(utilities.behavior_color(plot_colors, all_behaviors.index(behavior))) except Exception: colors_duration.append("darkgray") #width = 0.35 # the width of the bars: can also be len(x) sequence axs2[ax_idx].bar(np.arange(len(n_occurences)), n_occurences, #width, color=colors ) axs[ax_idx].bar(np.arange(len(durations)), durations, #width, color=colors_duration ) if ax_idx == 0: axs[ax_idx].set_ylabel("Duration (s)") axs[ax_idx].set_xlabel("Behaviors") axs[ax_idx].set_title(f"{subject}") axs[ax_idx].set_xticks(np.arange(len(durations))) axs[ax_idx].set_xticklabels(x_labels_duration, rotation='vertical', fontsize=8) if ax_idx == 0: axs2[ax_idx].set_ylabel("Number of occurences") axs2[ax_idx].set_xlabel("Behaviors") axs2[ax_idx].set_title(f"{subject}") axs2[ax_idx].set_xticks(np.arange(len(n_occurences))) axs2[ax_idx].set_xticklabels(x_labels, rotation='vertical', fontsize=8) fig.align_labels() fig.tight_layout(rect=[0, 0.03, 1, 0.95]) fig2.align_labels() fig2.tight_layout(rect=[0, 0.03, 1, 0.95]) if plot_directory: # output_file_name = f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.{output_format}" fig.savefig(f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.duration.{output_format}") fig2.savefig(f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.number_of_occurences.{output_format}") plt.close() else: fig.show() fig2.show() return {} except Exception: error_type, error_file_name, error_lineno = utilities.error_info(sys.exc_info()) logging.critical(f"Error in time budget bar plot: {error_type} {error_file_name} {error_lineno}") return {"error": True, "exception": sys.exc_info()}
def synthetic_time_budget(pj: dict, selected_observations: list, parameters_obs: dict): """ create a synthetic time budget Args: pj (dict): project dictionary selected_observations (list): list of observations to include in time budget parameters_obs (dict): Returns: bool: True if everything OK str: message tablib.Dataset: dataset containing synthetic time budget data """ try: selected_subjects = parameters_obs[SELECTED_SUBJECTS] selected_behaviors = parameters_obs[SELECTED_BEHAVIORS] include_modifiers = parameters_obs[INCLUDE_MODIFIERS] interval = parameters_obs["time"] start_time = parameters_obs["start time"] end_time = parameters_obs["end time"] parameters = [ ["duration", "Total duration"], ["number", "Number of occurrences"], ["duration mean", "Duration mean"], ["duration stdev", "Duration std dev"], ["proportion of time", "Proportion of time"], ] data_report = tablib.Dataset() data_report.title = "Synthetic time budget" ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, selected_observations, selected_behaviors) if not ok: return False, msg, None db_connector.create_aggregate("stdev", 1, StdevFunc) cursor = db_connector.cursor() # modifiers if include_modifiers: cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] else: cursor.execute("SELECT distinct behavior FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], ""] for rows in cursor.fetchall()] # add selected behaviors that are not observed for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, ""]) behaviors = init_behav_modif(pj[ETHOGRAM], selected_subjects, distinct_behav_modif, include_modifiers, parameters) param_header = ["", "Total length (s)"] subj_header, behav_header, modif_header = [""] * len(param_header), [ "" ] * len(param_header), [""] * len(param_header) for subj in selected_subjects: for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior for param in parameters: subj_header.append(subj) behav_header.append(behavior) modif_header.append(modifiers) param_header.append(param[1]) ''' if parameters_obs["group observations"]: cursor.execute("UPDATE aggregated_events SET observation = 'all' " ) #selected_observations = ["all"] ''' data_report.append(subj_header) data_report.append(behav_header) if include_modifiers: data_report.append(modif_header) data_report.append(param_header) # select time interval for obs_id in selected_observations: ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, [obs_id], selected_behaviors) if not ok: return False, msg, None db_connector.create_aggregate("stdev", 1, StdevFunc) cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") # time obs_length = project_functions.observation_total_length( pj[OBSERVATIONS][obs_id]) if obs_length == -1: obs_length = 0 if interval == TIME_FULL_OBS: min_time = float(0) max_time = float(obs_length) if interval == TIME_EVENTS: try: min_time = float(pj[OBSERVATIONS][obs_id][EVENTS][0][0]) except Exception: min_time = float(0) try: max_time = float(pj[OBSERVATIONS][obs_id][EVENTS][-1][0]) except Exception: max_time = float(obs_length) if interval == TIME_ARBITRARY_INTERVAL: min_time = float(start_time) max_time = float(end_time) # adapt start and stop to the selected time interval cursor.execute( "UPDATE aggregated_events SET start = ? WHERE observation = ? AND start < ? AND stop BETWEEN ? AND ?", ( min_time, obs_id, min_time, min_time, max_time, )) cursor.execute( "UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?", ( max_time, obs_id, max_time, min_time, max_time, )) cursor.execute( "UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?", ( min_time, max_time, obs_id, min_time, max_time, )) cursor.execute( "DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)", ( obs_id, min_time, min_time, max_time, max_time, )) for subject in selected_subjects: # check if behaviors are to exclude from total time time_to_subtract = 0 if EXCLUDED_BEHAVIORS in parameters_obs: for excluded_behav in parameters_obs[EXCLUDED_BEHAVIORS]: cursor.execute(( "SELECT SUM(stop-start) " "FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? " ), ( obs_id, subject, excluded_behav, )) for row in cursor.fetchall(): if row[0] is not None: time_to_subtract += row[0] for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior cursor.execute(( "SELECT SUM(stop-start), COUNT(*), AVG(stop-start), stdev(stop-start) " "FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? AND modifiers = ? " ), ( obs_id, subject, behavior, modifiers, )) for row in cursor.fetchall(): behaviors[subject][behavior_modifiers_str][ "duration"] = (0 if row[0] is None else f"{row[0]:.3f}") behaviors[subject][behavior_modifiers_str][ "number"] = 0 if row[1] is None else row[1] behaviors[subject][behavior_modifiers_str][ "duration mean"] = (0 if row[2] is None else f"{row[2]:.3f}") behaviors[subject][behavior_modifiers_str][ "duration stdev"] = (0 if row[3] is None else f"{row[3]:.3f}") if behavior not in parameters_obs[EXCLUDED_BEHAVIORS]: try: behaviors[subject][behavior_modifiers_str][ "proportion of time"] = ( 0 if row[0] is None else f"{row[0] / ((max_time - min_time) - time_to_subtract):.3f}" ) except ZeroDivisionError: behaviors[subject][behavior_modifiers_str][ "proportion of time"] = "-" else: # behavior subtracted behaviors[subject][behavior_modifiers_str][ "proportion of time"] = ( 0 if row[0] is None else f"{row[0] / (max_time - min_time):.3f}") columns = [obs_id, f"{max_time - min_time:0.3f}"] for subj in selected_subjects: for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior for param in parameters: columns.append( behaviors[subj][behavior_modifiers_str][param[0]]) data_report.append(columns) except Exception: error_type, error_file_name, error_lineno = utilities.error_info( sys.exc_info()) logging.critical( f"Error in edit_event function: {error_type} {error_file_name} {error_lineno}" ) msg = f"Error type: {error_type}\nError file name: {error_file_name}\nError line number: {error_lineno}" logging.critical(msg) return (False, msg, tablib.Dataset()) return True, msg, data_report
def filter(self): """ filter events """ if not self.logic.text(): return if self.logic.text().count('"') % 2: QMessageBox.warning(self, programName, f'Wrong number of double quotes (")') return sb_list = re.findall('"([^"]*)"', self.logic.text()) self.out = [] flag_error = False for obs_id in self.events: logic = self.logic.text() for sb in set(sb_list): logic = logic.replace(f'"{sb}"', f'self.events[obs_id]["{sb}"]') if sb not in self.events[obs_id]: self.events[obs_id][sb] = io([0, 0]) try: eval_result = eval(logic) for i in eval_result: if not i.is_empty(): self.out.append([ obs_id, "", f"{i.lower}", f"{i.upper}", f"{i.upper - i.lower:.3f}" ]) except KeyError: self.out.append( [obs_id, "subject / behavior not found", "NA", "NA", "NA"]) except Exception: # out += f"Error in {self.logic.text()}" + "\n" error_type, error_file_name, error_lineno = utilities.error_info( sys.exc_info()) self.out.append([ obs_id, f"Error in {self.logic.text()}: {error_type} ", "NA", "NA", "NA" ]) flag_error = True self.tw.clear() if flag_error or self.rb_details.isChecked(): self.lb_results.setText( f"Results ({len(self.out)} event{'s'*(len(self.out) > 1)})") self.tw.setRowCount(len(self.out)) self.tw.setColumnCount(len( self.details_header)) # obs_id, comment, start, stop, duration self.tw.setHorizontalHeaderLabels(self.details_header) if not flag_error and self.rb_summary.isChecked(): summary = {} for row in self.out: obs_id, _, start, stop, duration = row if obs_id not in summary: summary[obs_id] = [] summary[obs_id].append(float(duration)) self.out = [] for obs_id in summary: self.out.append([ obs_id, str(len(summary[obs_id])), str(round(sum(summary[obs_id]), 3)), str(round(statistics.mean(summary[obs_id]), 3)), str(round(statistics.stdev(summary[obs_id]), 3)) if len(summary[obs_id]) > 1 else "NA" ]) self.lb_results.setText( f"Results ({len(summary)} observation{'s'*(len(summary) > 1)})" ) self.tw.setRowCount(len(summary)) self.tw.setColumnCount(len( self.summary_header)) # obs_id, mean, stdev self.tw.setHorizontalHeaderLabels(self.summary_header) for r in range(len(self.out)): for c in range(self.tw.columnCount()): item = QTableWidgetItem() item.setText(self.out[r][c]) item.setFlags(Qt.ItemIsEnabled) self.tw.setItem(r, c, item)