def intersection(A, B, C, D):
        def mousePressEvent(self, event):
        def __init__(self, parent):
    def __init__(self, arg):
    def add_to_project(self, item):
    def area_list_item_click(self, item):
    def edit_area_code(self):
    def slAlpha_changed(self, val):
    def chooseColor(self):
    def closeEvent(self, event):
    def viewMousePressEvent(self, event):
    def mapName_clicked(self):
    def newMap(self):
    def openMap(self):
    def make_coding_map_dict(self):
    def saveMap(self):
    def saveAsMap_clicked(self):
    def saveMap_clicked(self):
    def newArea(self):
    def saveArea(self):
    def cancelAreaCreation(self):
    def update_area_list(self):
    def deleteArea(self):
    def cancelMap(self):
    def loadBitmap(self):
class BehaviorsMapCreatorWindow(QMainWindow):
    class View(QGraphicsView): class BehaviorsCodingMapWindowClass(QWidget):
    class View(QGraphicsView):
        def eventFilter(self, source, event):
        def __init__(self, parent):
    def __init__(self, behaviors_coding_map, idx=0):
    def closeEvent(self, event):
    def eventFilter(self, receiver, event):
    def mouse_move_event(self, event):
    def viewMousePressEvent(self, event):
    def loadMap(self): class Button(QWidget):
class CodingPad(QWidget):
    def __init__(self, parent=None):
    def __init__(self, pj, filtered_behaviors, parent=None):
    def config(self):
    def compose(self):
    def addWidget(self, behavior_code, i, j):
    def button_configuration(self):
    def resizeEvent(self, event):
    def click(self, behaviorCode):
    def eventFilter(self, receiver, event):
    def closeEvent(self, event): def load_events_in_db(pj: dict,
                      selected_subjects: list,
                      selected_observations: list,
                      selected_behaviors: list,
                      time_interval: str = TIME_FULL_OBS):
def load_aggregated_events_in_db(pj: dict, selected_subjects: list, selected_observations: list,
                                 selected_behaviors: list):
def load_aggregated_events_in_intervals(pj: dict, selected_subjects: list, selected_observations: list,
                                        selected_behaviors: list): def MessageDialog(title, text, buttons):
def error_message_box(task, error_type, error_file_name,
                      error_lineno):  # do NOT use this function directly, use error_message function
def error_message(task: str, exc_info: tuple) -> None:
    def __init__(self, parent=None):
    def __init__(self):
    def browse(self):
    def ok(self):
    def __init__(self, label_caption, elements_list):
    def __init__(self, text, codes_list):
    def getCode(self):
    def pbOK_clicked(self):
    def __init__(self, text, observations_list):
    def get_selected_observations(self):
    def __init__(self, time_format):
    def __init__(self):
    def rb_changed(self):
    def pbOK_clicked(self):
    def pbCancel_clicked(self):
    def __init__(self):
    def click(self, msg):
    def __init__(self):
    def click(self, msg):
    def __init__(self):
    def __init__(self):
    def save_results(self):
    def __init__(self):
    def save_results(self):
    def __init__(self):
    def __init__(self):
    def tw_cellDoubleClicked(self, r, c):
def choose_obs_subj_behav_category(pj: dict,
                                   selected_observations: list,
                                   maxTime=0,
                                   flagShowIncludeModifiers: bool = True,
                                   flagShowExcludeBehaviorsWoEvents: bool = True,
                                   by_category: bool = False,
                                   show_time: bool = False,
                                   timeFormat: str = HHMMSS):
class Info_widget(QWidget):
class Video_overlay_dialog(QDialog):
class Input_dialog(QDialog):
class DuplicateBehaviorCode(QDialog):
class ChooseObservationsToImport(QDialog):
class Ask_time(QDialog):
class EditSelectedEvents(QDialog):
class FindInEvents(QWidget):
class FindReplaceEvents(QWidget):
class explore_project_dialog(QDialog):
class Results_dialog(QDialog):
class ResultsWidget(QWidget):
class View_data_head(QDialog):
class View_explore_project_results(QWidget): def export_events_jwatcher(parameters: list, obsId: str, observation: list, ethogram: dict, file_name: str,
                           output_format: str):
def export_events(parameters, obsId, observation, ethogram, file_name, output_format):
def dataset_write(dataset, file_name, output_format):
def export_aggregated_events(pj: dict, parameters: dict, obsId: str):
def events_to_behavioral_sequences(pj, obs_id: str, subj: str, parameters: dict, behav_seq_separator: str) -> str:
def events_to_behavioral_sequences_all_subj(pj, obs_id: str, subjects_list: list, parameters: dict,
                                            behav_seq_separator: str) -> str:
def events_to_timed_behavioral_sequences(pj: dict, obs_id: str, subject: str, parameters: dict, precision: float,
                                         behav_seq_separator: str) -> str:
def observation_to_behavioral_sequences(pj, selected_observations, parameters, behaviors_separator, separated_subjects,
                                        timed, file_name): def save_geometry(widget: QWidget, widget_name: str):
def restore_geometry(widget: QWidget, widget_name: str, default_geometry): def behavior_binary_table(pj: dict):
    """
    ask user for parameters for behavior binary table
    call create_behavior_binary_table
    """

    _, 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, _ = 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)

    results_df = create_behavior_binary_table(pj, selected_observations, parameters, time_interval)

    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 subj_behav_modif(cursor, obsid, subject, time, interval, include_modifiers: bool):
def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: Decimal, selected_subjects: list, include_modifiers: bool):
def needleman_wunsch_identity(cursor, obsid1: str, obsid2: str, interval, selected_subjects: list,
                              include_modifiers: bool):
    def zeros(shape):
    def match_score(alpha, beta):
    def finalize(align1, align2):
    def needle(seq1, seq2):
        K = 1
            K = round((agreements - sum_ef) / (overall_total - sum_ef), 3)
            K = nan def intersection(A, B, C, D):
        def mousePressEvent(self, event):
        def __init__(self, parent):
    def __init__(self):
    def slAlpha_changed(self, val):
    def chooseColor(self):
    def closeEvent(self, event):
    def viewMousePressEvent(self, event):
    def mapName_clicked(self):
    def newMap(self):
    def openMap(self):
    def saveMap(self):
    def saveAsMap_clicked(self):
    def saveMap_clicked(self):
    def newArea(self):
    def saveArea(self):
    def cancelAreaCreation(self):
    def deleteArea(self):
    def cancelMap(self):
    def loadBitmap(self):
class ModifiersMapCreatorWindow(QMainWindow):
    class View(QGraphicsView): class wgMeasurement(QWidget):
    def __init__(self):
    def pbClear_clicked(self):
    def pbClose_clicked(self):
    def pbSave_clicked(self): class ModifiersCodingMapWindowClass(QDialog):
    class View(QGraphicsView):
        def mousePressEvent(self, event):
        def __init__(self, parent):
    def __init__(self, modifiers_coding_map):
    def viewMousePressEvent(self, event):
    def getCodes(self):
    def loadMap(self): class AssignConverter(QDialog):
class Observation(QDialog, Ui_Form):
    def __init__(self, columns, converters, col_conv):
    def __init__(self, tmp_dir, project_path="", converters={}, time_format=S, parent=None):
    def cb_start_from_current_time_changed(self):
    def limit_time_interval(self):
    def tw_data_files_cellDoubleClicked(self, row, column):
    def plot_data_file(self):
    def add_data_file(self, flag_path=True):
    def view_data_file_head(self):
    def extract_wav(self):
    def pbCancel_clicked(self):
    def check_parameters(self):
        def is_numeric(s):
    def pbLaunch_clicked(self):
    def pbSave_clicked(self):
    def check_media(self, file_path, flag_path):
    def add_media(self, flag_path):
    def add_media_from_dir(self, flag_path):
    def add_media_to_listview(self, file_name):
    def remove_data_file(self):
    def remove_media(self): class MyTableWidgetItem(QTableWidgetItem):
class observationsList_widget(QDialog):
    def __init__(self, text, sortKey):
    def __lt__(self, other):
    def __init__(self, data: list, header: list, column_type: list, not_paired: list = [], parent=None):
    def view_doubleClicked(self, index):
    def pbSelection_clicked(self, mode):
    def pbCancel_clicked(self):
    def pbOk_clicked(self):
    def pbOpen_clicked(self):
    def pbEdit_clicked(self):
    def pbView_clicked(self):
    def set_item(self, r, c):
    def view_filter(self):
        def str2float(s):
        def in_(s, lst):
        def not_in(s, lst):
        def equal(s, l):
        def not_equal(s, l):
        def gt(s, l):
        def lt(s, l):
        def gt_or_equal(s, l):
        def lt_or_equal(s, l):
        def between(s, l): def icc(i: list):
def ico(i: list):
def io(i: list):
    def __init__(self, events):
    def add_subj_behav(self):
    def add_logic(self):
    def filter(self):
    def save_results(self):
def event_filtering(pj: dict):
class Advanced_event_filtering_dialog(QDialog): def otx_to_boris(file_path: str) -> dict: class Param_panel(QDialog, Ui_Dialog):
    def __init__(self, parent=None):
    def rb_time(self, button):
    def subjects_button_clicked(self, command):
    def behaviors_button_clicked(self, command):
    def ok(self):
    def behavior_item_clicked(self, item):
    def extract_observed_behaviors(self, selected_observations, selected_subjects):
    def cb_changed(self): class MyMplCanvas(FigureCanvas):
class Plot_data(QWidget):
class Plotter(QObject):
    def __init__(self, parent=None):
    def __init__(self,
                 file_name,
                 interval,
                 time_offset,
                 plot_style,
                 plot_title,
                 y_label,
                 columns_to_plot,
                 substract_first_value,
                 converters,
                 column_converter,
                 log_level=""):
    def eventFilter(self, receiver, event):
    def zoom(self, z):
    def timer_plot_data_out(self, time_):
    def update_plot(self, time_):
    def close_plot(self):
    def plot(self, x, y, position_data, position_start, min_value, max_value, position_end):
    def replot(self, current_time):  # time_ in s
    def get_time(): def default_value(ethogram, behavior, parameter):
def init_behav_modif(ethogram: dict, selected_subjects: list, distinct_behav_modif, include_modifiers, parameters):
def init_behav(ethogram: dict, selected_subjects: list, distinct_behaviors, parameters):
def create_behaviors_bar_plot(pj: dict,
                              selected_observations: list,
                              param: dict,
                              plot_directory: str,
                              output_format: str,
                              plot_colors: list = BEHAVIORS_PLOT_COLORS):
def create_events_plot(pj,
                       selected_observations,
                       parameters,
                       plot_colors=BEHAVIORS_PLOT_COLORS,
                       plot_directory="",
                       file_format="png"): class Plot_spectrogram_RT(QWidget):
    def get_wav_info(self, wav_file: str):
    def __init__(self):
    def eventFilter(self, receiver, event):
    def time_interval_changed(self, action: int):
    def frequency_interval_changed(self):
    def load_wav(self, wav_file_path: str) -> dict:
    def plot_spectro(self, current_time: float, force_plot: bool = False): Y_MIN = 0
Y_MAX = 20000
VMIN = -40
class Plot_waveform_RT(QWidget):
    def get_wav_info(self, wav_file: str):
    def __init__(self):
    def eventFilter(self, receiver, event):
    def load_wav(self, wav_file_path: str) -> dict:
    def plot_waveform(self, current_time: float): class Preferences(QDialog, Ui_prefDialog):
    def __init__(self, parent=None):
    def rb_frames_mem_disk(self):
    def reset_frames_memory(self):
    def refresh_preferences(self):
    def browseFFmpegCacheDir(self):
    def reset_behav_colors(self):
    def reset_category_colors(self): def check_observation_exhaustivity(events: list, ethogram: list, state_events_list: list = []):
    def interval_len(interval):
    def interval_number(interval):
def behavior_category(ethogram: dict) -> dict:
def check_coded_behaviors(pj: dict) -> set:
def check_if_media_available(observation: dict, project_file_name: str) -> bool:
def check_state_events_obs(obsId: str, ethogram: dict, observation: dict, time_format: str = HHMMSS) -> tuple:
def check_project_integrity(pj: dict,
                            time_format: str,
                            project_file_name: str,
                            media_file_available: bool = True) -> str:
def create_subtitles(pj: dict, selected_observations: list, parameters: dict, export_dir: str) -> tuple:
    def subject_color(subject):
def export_observations_list(pj: dict, selected_observations: list, file_name: str, output_format: str) -> bool:
def remove_media_files_path(pj):
def media_full_path(media_file: str, project_file_name: str) -> str:
def observed_interval(observation: dict):
def observation_total_length(observation: dict):
def observation_length(pj, selected_observations: list) -> tuple:
def events_start_stop(ethogram, events):
def extract_observed_subjects(pj: dict, selected_observations: list) -> list:
def open_project_json(projectFileName: str) -> tuple:
def event_type(code: str, ethogram: dict) -> str:
def fix_unpaired_state_events(obsId, ethogram, observation, fix_at_time): def qInitResources():
def qCleanupResources(): class ModifiersList(QDialog):
    def __init__(self, code, modifiers_dict, currentModifier):
    def eventFilter(self, receiver, event):
    def get_modifiers(self):
    def pbOK_clicked(self): def select_observations(pj: dict, mode: str, windows_title: str = "") -> tuple:
    """
    allow user to select observations
    mode: accepted values: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1

    Args:
        pj (dict): BORIS project dictionary
        mode (str): mode for selection: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1
        windows_title (str): title for windows

    Returns:
        str: selected mode: OPEN, EDIT, VIEW
        list: list of selected observations
    """

    obsListFields = ["id", "date", "description", "subjects", "observation duration", "exhaustivity %", "media"]
    indepVarHeader, column_type = [], [TEXT, TEXT, TEXT, TEXT, NUMERIC, NUMERIC, TEXT]

    if INDEPENDENT_VARIABLES in pj:
        for idx in utilities.sorted_keys(pj[INDEPENDENT_VARIABLES]):
            indepVarHeader.append(pj[INDEPENDENT_VARIABLES][idx]["label"])
            column_type.append(pj[INDEPENDENT_VARIABLES][idx]["type"])

    data = []
    not_paired = []
    state_events_list = [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in pj[ETHOGRAM] if STATE in pj[ETHOGRAM][x][TYPE].upper()]
    for obs in sorted(list(pj[OBSERVATIONS].keys())):
        date = pj[OBSERVATIONS][obs]["date"].replace("T", " ")
        descr = utilities.eol2space(pj[OBSERVATIONS][obs][DESCRIPTION])

        # subjects
        observedSubjects = [
            NO_FOCAL_SUBJECT if x == "" else x for x in project_functions.extract_observed_subjects(pj, [obs])
        ]

        subjectsList = ", ".join(observedSubjects)

        # observed time interval
        interval = project_functions.observed_interval(pj[OBSERVATIONS][obs])
        observed_interval_str = str(interval[1] - interval[0])

        # media
        mediaList = []
        if pj[OBSERVATIONS][obs][TYPE] in [MEDIA]:
            if pj[OBSERVATIONS][obs][FILE]:
                for player in sorted(pj[OBSERVATIONS][obs][FILE].keys()):
                    for media in pj[OBSERVATIONS][obs][FILE][player]:
                        mediaList.append(f"#{player}: {media}")

            if len(mediaList) > 8:
                media = " ".join(mediaList)
            else:
                media = "\n".join(mediaList)

        elif pj[OBSERVATIONS][obs][TYPE] in [LIVE]:
            media = LIVE

        # independent variables
        indepvar = []
        if INDEPENDENT_VARIABLES in pj[OBSERVATIONS][obs]:
            for var_label in indepVarHeader:
                if var_label in pj[OBSERVATIONS][obs][INDEPENDENT_VARIABLES]:
                    indepvar.append(pj[OBSERVATIONS][obs][INDEPENDENT_VARIABLES][var_label])
                else:
                    indepvar.append("")

        # check unpaired events
        ok, _ = project_functions.check_state_events_obs(obs, pj[ETHOGRAM], pj[OBSERVATIONS][obs], HHMMSS)
        if not ok:
            not_paired.append(obs)

        # check exhaustivity of observation
        exhaustivity = project_functions.check_observation_exhaustivity(pj[OBSERVATIONS][obs][EVENTS], [],
                                                                        state_events_list)

        data.append([obs, date, descr, subjectsList, observed_interval_str, str(exhaustivity), media] + indepvar)

    obsList = observations_list.observationsList_widget(data,
                                                        header=obsListFields + indepVarHeader,
                                                        column_type=column_type,
                                                        not_paired=not_paired)
    if windows_title:
        obsList.setWindowTitle(windows_title)

    obsList.pbOpen.setVisible(False)
    obsList.pbView.setVisible(False)
    obsList.pbEdit.setVisible(False)
    obsList.pbOk.setVisible(False)
    obsList.pbSelectAll.setVisible(False)
    obsList.pbUnSelectAll.setVisible(False)
    obsList.mode = mode

    if mode == OPEN:
        obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
        obsList.pbOpen.setVisible(True)

    if mode == VIEW:
        obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
        obsList.pbView.setVisible(True)

    if mode == EDIT:
        obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
        obsList.pbEdit.setVisible(True)

    if mode == SINGLE:
        obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
        obsList.pbOpen.setVisible(True)
        obsList.pbView.setVisible(True)
        obsList.pbEdit.setVisible(True)

    if mode == MULTIPLE:
        obsList.view.setSelectionMode(QAbstractItemView.MultiSelection)
        obsList.pbOk.setVisible(True)
        obsList.pbSelectAll.setVisible(True)
        obsList.pbUnSelectAll.setVisible(True)

    if mode == SELECT1:
        obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
        obsList.pbOk.setVisible(True)

    # restore window geometry
    gui_utilities.restore_geometry(obsList, "observations list", (900, 600))

    obsList.view.sortItems(0, Qt.AscendingOrder)
    for row in range(obsList.view.rowCount()):
        obsList.view.resizeRowToContents(row)

    selected_observations = []

    result = obsList.exec_()

    # saving window geometry in ini file
    gui_utilities.save_geometry(obsList, "observations list")

    if result:
        if obsList.view.selectedIndexes():
            for idx in obsList.view.selectedIndexes():
                if idx.column() == 0:  # first column
                    selected_observations.append(idx.data())

    if result == 0:  # cancel
        resultStr = ""
    if result == 1:  # select
        resultStr = "ok"
    if result == 2:  # open
        resultStr = OPEN
    if result == 3:  # edit
        resultStr = EDIT
    if result == 4:  # view
        resultStr = VIEW

    return resultStr, selected_observations class SubjectsPad(QWidget):
class Button(QWidget):
    def __init__(self, pj, filtered_subjects, parent=None):
    def compose(self):
    def addWidget(self, subject, i, j):
    def click(self, subject):
    def eventFilter(self, receiver, event):
    def closeEvent(self, event):
    def __init__(self, parent=None): def default_value(ethogram, behav, param):
def init_behav_modif(ethogram, selected_subjects, distinct_behav_modif, include_modifiers, parameters):
def init_behav_modif_bin(ethogram, selected_subjects, distinct_behav_modif, include_modifiers, parameters):
    def __init__(self):
    def step(self, value):
    def finalize(self):
def synthetic_time_budget_bin(pj: dict, selected_observations: list, parameters_obs: dict):
    def interval_len(interval):
    def interval_number(interval):
    def interval_mean(interval):
    def interval_std_dev(interval):
def synthetic_time_budget(pj: dict, selected_observations: list, parameters_obs: dict):
def time_budget_analysis(ethogram: dict,
                         cursor,
                         selected_observations: list,
                         parameters: dict,
                         by_category: bool = False):
class StdevFunc: def behavioral_strings_analysis(strings, behaviouralStringsSeparator):
def observed_transitions_matrix(sequences, behaviours, mode="frequency"):
def create_transitions_gv_from_matrix(matrix, cutoff_all=0, cutoff_behavior=0, edge_label="percent_node"): def error_info(exc_info: tuple) -> tuple:
def return_file_header(file_name: str, row_number: int = 5) -> list:
def bytes_to_str(b: bytes) -> str:
def convert_time_to_decimal(pj: dict) -> dict:
def file_content_md5(file_name: str) -> str:
def txt2np_array(file_name: str, columns_str: str, substract_first_value: str, converters=None, column_converter=None):
def versiontuple(version_str: str) -> tuple:
def state_behavior_codes(ethogram: dict) -> list:
def point_behavior_codes(ethogram: dict) -> list:
def get_current_states_modifiers_by_subject(state_behaviors_codes: list,
                                            events: list,
                                            subjects: dict,
                                            time: Decimal,
                                            include_modifiers: bool = False) -> dict:
def get_current_states_modifiers_by_subject_2(state_behaviors_codes: list, events: list, subjects: dict,
                                              time: Decimal) -> dict:
def get_current_points_by_subject(point_behaviors_codes: list,
                                  events: list,
                                  subjects: dict,
                                  time: Decimal,
                                  tolerance: Decimal,
                                  include_modifiers: bool = False) -> dict:
def get_ip_address():
def check_txt_file(file_name: str) -> dict:
def extract_wav(ffmpeg_bin: str, media_file_path: str, tmp_dir: str) -> str:
def extract_frames(ffmpeg_bin: str, start_frame: int, second: float, current_media_path, fps, imageDir, md5_media_path,
                   extension, frame_resize, number_of_seconds):
def extract_frames_mem_old(ffmpeg_bin: str, start_frame: int, second: float, current_media_path, fps: float,
                           resolution: tuple, frame_resize: int, number_of_seconds: int) -> (list, tuple):
    def toQImage(frame, copy=False):
def decimal_default(obj):
def complete(l: list, max_: int):
def datetime_iso8601(dt) -> str:
def seconds_of_day(dt) -> Decimal:
def sorted_keys(d: dict) -> list:
def intfloatstr(s: str):
def distance(p1, p2):
def angle(vertex: tuple, side1: tuple, side2: tuple) -> float:
def mem_info():
def polygon_area(poly):
def url2path(url):
def float2decimal(f):
def time2seconds(time_: str) -> Decimal:
def seconds2time(sec):
def safeFileName(s: str) -> str:
def safe_xl_worksheet_title(title: str, output_format: str):
def eol2space(s: str) -> str:
def test_ffmpeg_path(FFmpegPath):
def check_ffmpeg_path():
def accurate_media_analysis(ffmpeg_bin, file_name):
def behavior_color(colors_list, idx): class DlgEditEvent(QDialog, Ui_Form):

    def __init__(self,
                 log_level,
                 time_value=0,
                 current_time=0,
                 time_format=S,
                 show_set_current_time=False,
                 parent=None):

        super().__init__(parent)
        self.setupUi(self)
        self.time_value = time_value

        self.pb_set_to_current_time.setVisible(show_set_current_time)
        self.current_time = current_time

        self.dsbTime.setVisible(False)
        self.teTime.setVisible(False)

        self.time_widget = duration_widget.Duration_widget(self.time_value)
        if time_format == S:
            self.time_widget.set_format_s()
        if time_format == HHMMSS:
            self.time_widget.set_format_hhmmss()

        self.horizontalLayout_2.insertWidget(0, self.time_widget)

        self.pb_set_to_current_time.clicked.connect(self.set_to_current_time)
        self.pbOK.clicked.connect(self.accept)
        self.pbCancel.clicked.connect(self.reject)

    def set_to_current_time(self):
        """
        set time to current media time
        """

        self.time_widget.set_time(float(self.current_time)) class EditSelectedEvents(QDialog):
    """
    "edit selected events" dialog box
    """

    def __init__(self):
        super(EditSelectedEvents, self).__init__()

        self.setWindowTitle("Edit selected events")

        hbox = QVBoxLayout(self)

        self.rbSubject = QRadioButton("Subject")
        self.rbSubject.setChecked(False)
        self.rbSubject.toggled.connect(self.rb_changed)
        hbox.addWidget(self.rbSubject)

        self.rbBehavior = QRadioButton("Behavior")
        self.rbBehavior.setChecked(False)
        self.rbBehavior.toggled.connect(self.rb_changed)
        hbox.addWidget(self.rbBehavior)

        self.lb = QLabel("New value")
        hbox.addWidget(self.lb)
        self.newText = QListWidget(self)
        hbox.addWidget(self.newText)

        self.rbComment = QRadioButton("Comment")
        self.rbComment.setChecked(False)
        self.rbComment.toggled.connect(self.rb_changed)
        hbox.addWidget(self.rbComment)

        self.lbComment = QLabel("New comment")
        hbox.addWidget(self.lbComment)

        self.commentText = QLineEdit()
        hbox.addWidget(self.commentText)

        hbox2 = QHBoxLayout(self)
        self.pbOK = QPushButton("OK")
        self.pbOK.clicked.connect(self.pbOK_clicked)
        self.pbCancel = QPushButton("Cancel")
        self.pbCancel.clicked.connect(self.pbCancel_clicked)
        hbox2.addWidget(self.pbCancel)
        hbox2.addWidget(self.pbOK)
        hbox.addLayout(hbox2)

        self.setLayout(hbox)

    def rb_changed(self):

        self.newText.setEnabled(not self.rbComment.isChecked())
        self.commentText.setEnabled(self.rbComment.isChecked())

        if self.rbSubject.isChecked():
            self.newText.clear()
            self.newText.addItems(self.all_subjects)

        if self.rbBehavior.isChecked():
            self.newText.clear()
            self.newText.addItems(self.all_behaviors)

        if self.rbComment.isChecked():
            self.newText.clear()

    def pbOK_clicked(self):
        if not self.rbSubject.isChecked() and not self.rbBehavior.isChecked() and not self.rbComment.isChecked():
            QMessageBox.warning(None, programName, "You must select a field to be edited",
                                QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
            return

        if (self.rbSubject.isChecked() or self.rbBehavior.isChecked()) and self.newText.selectedItems() == []:
            QMessageBox.warning(None, programName, "You must select a new value from the list",
                                QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
            return

        self.accept()

    def pbCancel_clicked(self):
        self.reject() class timeBudgetResults(QWidget):
    """
    class for displaying time budget results in new window
    a function for exporting data in TSV, CSV, XLS and ODS formats is implemented

    Args:
        pj (dict): BORIS project
    """

    def __init__(self, pj, config_param):
        super().__init__()

        self.pj = pj
        self.config_param = config_param

        hbox = QVBoxLayout(self)

        self.label = QLabel("")
        hbox.addWidget(self.label)

        self.lw = QListWidget()
        #self.lw.setEnabled(False)
        self.lw.setMaximumHeight(100)
        hbox.addWidget(self.lw)

        self.lbTotalObservedTime = QLabel("")
        hbox.addWidget(self.lbTotalObservedTime)

        # behaviors excluded from total time
        self.excluded_behaviors_list = QLabel("")
        hbox.addWidget(self.excluded_behaviors_list)

        self.twTB = QTableWidget()
        hbox.addWidget(self.twTB)

        hbox2 = QHBoxLayout()

        self.pbSave = QPushButton("Save results", clicked=self.pbSave_clicked)
        hbox2.addWidget(self.pbSave)

        spacerItem = QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        hbox2.addItem(spacerItem)

        self.pbClose = QPushButton("Close", clicked=self.close_clicked)
        hbox2.addWidget(self.pbClose)

        hbox.addLayout(hbox2)

        self.setWindowTitle("Time budget")

    def close_clicked(self):
        """
        save geometry of widget and close it
        """
        gui_utilities.save_geometry(self, "time budget")
        self.close()

    def pbSave_clicked(self):
        """
        save time budget analysis results in TSV, CSV, ODS, XLS format
        """

        def complete(l: list, max_: int) -> list:
            """
            complete list with empty string until len = max

            Args:
                l (list): list to complete
                max_ (int): length of the returned list

            Returns:
                list: completed list
            """

            while len(l) < max_:
                l.append("")
            return l

        logging.debug("save time budget results to file")

        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(self, "Save Time budget analysis", "",
                                                           ";;".join(extended_file_formats))

        if not file_name:
            return

        outputFormat = file_formats[extended_file_formats.index(filter_)]
        if pathlib.Path(file_name).suffix != "." + outputFormat:
            file_name = str(pathlib.Path(file_name)) + "." + outputFormat
            # 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

        rows = []

        # 1 observation
        if (self.lw.count() == 1 and
                self.config_param.get(TIME_BUDGET_FORMAT, DEFAULT_TIME_BUDGET_FORMAT) == COMPACT_TIME_BUDGET_FORMAT):
            col1, indep_var_label = [], []
            # add obs id
            col1.append(self.lw.item(0).text())
            # add obs date
            col1.append(self.pj[OBSERVATIONS][self.lw.item(0).text()].get("date", ""))

            # description
            col1.append(utilities.eol2space(self.pj[OBSERVATIONS][self.lw.item(0).text()].get(DESCRIPTION, "")))
            header = ["Observation id", "Observation date", "Description"]

            # indep var
            for var in self.pj[OBSERVATIONS][self.lw.item(0).text()].get(INDEPENDENT_VARIABLES, {}):
                indep_var_label.append(var)
                col1.append(self.pj[OBSERVATIONS][self.lw.item(0).text()][INDEPENDENT_VARIABLES][var])

            header.extend(indep_var_label)

            col1.extend([f"{self.min_time:0.3f}", f"{self.max_time:0.3f}", f"{self.max_time - self.min_time:0.3f}"])
            header.extend(["Time budget start", "Time budget stop", "Time budget duration"])

            for col_idx in range(self.twTB.columnCount()):
                header.append(self.twTB.horizontalHeaderItem(col_idx).text())
            rows.append(header)

            for row_idx in range(self.twTB.rowCount()):
                values = []
                for col_idx in range(self.twTB.columnCount()):
                    values.append(intfloatstr(self.twTB.item(row_idx, col_idx).text()))
                rows.append(col1 + values)

        else:
            # observations list
            rows.append(["Observations:"])
            for idx in range(self.lw.count()):
                rows.append([""])
                rows.append(["Observation id", self.lw.item(idx).text()])
                rows.append(["Observation date", self.pj[OBSERVATIONS][self.lw.item(idx).text()].get("date", "")])
                rows.append([
                    "Description",
                    utilities.eol2space(self.pj[OBSERVATIONS][self.lw.item(idx).text()].get(DESCRIPTION, ""))
                ])

                if INDEPENDENT_VARIABLES in self.pj[OBSERVATIONS][self.lw.item(idx).text()]:
                    rows.append(["Independent variables:"])
                    for var in self.pj[OBSERVATIONS][self.lw.item(idx).text()][INDEPENDENT_VARIABLES]:
                        rows.append([var, self.pj[OBSERVATIONS][self.lw.item(idx).text()][INDEPENDENT_VARIABLES][var]])

            if self.excluded_behaviors_list.text():
                s1, s2 = self.excluded_behaviors_list.text().split(": ")
                rows.extend([[""], [s1] + s2.split(", ")])

            rows.extend([[""], [""], ["Time budget:"]])

            # write header
            header = []
            for col_idx in range(self.twTB.columnCount()):
                header.append(self.twTB.horizontalHeaderItem(col_idx).text())

            rows.append(header)
            rows.append([""])

            for row in range(self.twTB.rowCount()):
                values = []
                for col_idx in range(self.twTB.columnCount()):
                    values.append(intfloatstr(self.twTB.item(row, col_idx).text()))

                rows.append(values)

        max_row_length = max([len(r) for r in rows])
        data = tablib.Dataset()
        data.title = "Time budget"

        for row in rows:
            data.append(complete(row, max_row_length))

        if outputFormat in ["tsv", "csv", "html"]:
            with open(file_name, "wb") as f:
                f.write(str.encode(data.export(outputFormat)))
            return

        if outputFormat in ["ods", "xlsx", "xls"]:
            with open(file_name, "wb") as f:
                f.write(data.export(outputFormat))
            return class Click_label(QLabel):
class Video_frame(QFrame):
class DW(QDockWidget):
    def __init__(self, id_, parent=None):
    def mousePressEvent(self, event):
    def sizeHint(self):
    def mousePressEvent(self, QMouseEvent):
    def resizeEvent(self, dummy):
    def __init__(self, id_, parent=None):
    def volume_slider_moved(self):
    def keyPressEvent(self, event):
    def view_signal_triggered(self, msg, button):
import datetime
import hashlib
import json
import logging
import os
import pathlib
import platform
import re
import socket
import subprocess
import sys
import tempfile
import time
import urllib.error
import urllib.parse
import urllib.request
import gzip
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
import numpy as np
import tablib
from decimal import *
from optparse import OptionParser
from matplotlib import dates
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtMultimedia import QSound
from PyQt5.QtWidgets import *
from boris import behav_coding_map_creator
from boris import behaviors_coding_map
from boris import coding_pad
from boris import db_functions
from boris import dialog
from boris import export_observation
from boris import gui_utilities
from boris import behavior_binary_table
from boris import irr
from boris import map_creator
from boris import measurement_widget
from boris import modifiers_coding_map
from boris import observation
from boris import observations_list
from boris import advanced_event_filtering
from boris import otx_parser
from boris import param_panel
from boris import plot_data_module
from boris import plot_events
from boris import plot_spectrogram_rt
from boris import plot_waveform_rt
from boris import preferences
from boris import project_functions
from boris import qrc_boris
from boris import select_modifiers
from boris import select_observations
from boris import subjects_pad
from boris import time_budget_functions
from boris import transitions
from boris import utilities
from boris import version
from boris.boris_ui import *
from boris.config import *
from boris.edit_event import DlgEditEvent, EditSelectedEvents
from boris.project import *
from boris.time_budget_widget import timeBudgetResults
from boris.utilities import *
from boris import player_dock_widget
        from boris import vlc
        from boris.vlc import libvlc_get_version, plugin_path

                    wav_file_path = pathlib.Path(tmp_dir) / pathlib.Path(
                        urllib.parse.unquote(url2path(self.dw_player[0].mediaplayer.get_media().get_mrl())) +
                        ".wav").name

                    self.spectro = plot_spectrogram_rt.Plot_spectrogram_RT()

                    self.spectro.setWindowFlags(Qt.WindowStaysOnTopHint)

                    self.spectro.interval = self.spectrogram_time_interval
                    self.spectro.cursor_color = "red"

                    r = self.spectro.load_wav(str(wav_file_path))
                    if "error" in r:
                        logging.warning(f"spectro_load_wav error: {r['error']}")
                        QMessageBox.warning(self, programName, f"Error in spectrogram generation: {r['error']}",
                                            QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
                        del self.spectro
                        return

                    self.pj[OBSERVATIONS][self.observationId][VISUALIZE_SPECTROGRAM] = True
                    self.spectro.sendEvent.connect(self.signal_from_widget)
                    self.spectro.sb_freq_min.setValue(0)
                    self.spectro.sb_freq_max.setValue(int(self.spectro.frame_rate / 2))
                    self.spectro.show()
                    self.timer_sound_signal.start()

                if self.playerType == VLC and self.playMode == VLC and not flagPaused:
                    self.play_video()

        if plot_type == "waveform":
            if hasattr(self, "waveform"):
                self.waveform.show()
            else:
                logging.debug("waveform not shown")

                # remember if player paused
                if self.playerType == VLC and self.playMode == VLC:
                    flagPaused = self.dw_player[0].mediaListPlayer.get_state() == self.vlc_paused

                self.pause_video()

                if dialog.MessageDialog(programName,
                                        ("You choose to visualize the waveform during this observation.<br>"
                                         "The waveform generation can take some time for long media, be patient"),
                                        [YES, NO]) == YES:

                    self.generate_wav_file_from_media()

                    tmp_dir = self.ffmpeg_cache_dir if self.ffmpeg_cache_dir and os.path.isdir(
                        self.ffmpeg_cache_dir) else tempfile.gettempdir()

                    wav_file_path = pathlib.Path(tmp_dir) / pathlib.Path(
                        urllib.parse.unquote(url2path(self.dw_player[0].mediaplayer.get_media().get_mrl())) +
                        ".wav").name

                    self.waveform = plot_waveform_rt.Plot_waveform_RT()

                    self.waveform.setWindowFlags(Qt.WindowStaysOnTopHint)

                    self.waveform.interval = self.spectrogram_time_interval
                    self.waveform.cursor_color = "red"

                    r = self.waveform.load_wav(str(wav_file_path))
                    if "error" in r:
                        logging.warning(f"waveform_load_wav error: {r['error']}")
                        QMessageBox.warning(self, programName, f"Error in waveform generation: {r['error']}",
                                            QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
                        del self.waveform
                        return

                    self.pj[OBSERVATIONS][self.observationId][VISUALIZE_WAVEFORM] = True
                    self.waveform.sendEvent.connect(self.signal_from_widget)
                    '''
                    self.waveform.sb_freq_min.setValue(0)
                    self.waveform.sb_freq_max.setValue(int(self.spectro.frame_rate / 2))
                    '''
                    self.waveform.show()
                    self.timer_sound_signal.start()

                if self.playerType == VLC and self.playMode == VLC and not flagPaused:
                    self.play_video()

    def show_waveform(self):
        """
        show waveform window if any
        """

        if self.playerType == LIVE:
            QMessageBox.warning(self, programName, "The waveform visualization is not available for live observations")
            return

        if self.playerType == VIEWER:
            QMessageBox.warning(self, programName, "The waveform visualization is not available in <b>VIEW</b> mode")
            return

        if hasattr(self, "waveform"):
            self.waveform.show()
        else:
            logging.debug("waveform not shown")

            # remember if player paused
            if self.playerType == VLC and self.playMode == VLC:
                flagPaused = self.dw_player[0].mediaListPlayer.get_state() == self.vlc_paused

            self.pause_video()

            if dialog.MessageDialog(programName,
                                    ("You choose to visualize the waveform during this observation.<br>"
                                     "Spectrogram generation can take some time for long media, be patient"),
                                    [YES, NO]) == YES:

                self.generate_wav_file_from_media()

                tmp_dir = self.ffmpeg_cache_dir if self.ffmpeg_cache_dir and os.path.isdir(
                    self.ffmpeg_cache_dir) else tempfile.gettempdir()

                wav_file_path = pathlib.Path(tmp_dir) / pathlib.Path(
                    urllib.parse.unquote(url2path(self.dw_player[0].mediaplayer.get_media().get_mrl())) + ".wav").name


                self.waveform = plot_waveform_rt.Plot_waveform_RT()