boris-behav-obs 9.6.2__py2.py3-none-any.whl → 9.6.4__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- boris/1.py +45 -0
- boris/analysis_plugins/irr_cohen_kappa.py +42 -17
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +40 -16
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +36 -10
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +36 -10
- boris/analysis_plugins/time_budget.py +0 -4
- boris/behav_coding_map_creator.py +0 -1
- boris/coding_pad.py +0 -2
- boris/config_file.py +0 -2
- boris/core.py +9 -24
- boris/db_functions.py +4 -4
- boris/edit_event.py +0 -9
- boris/export_events.py +68 -76
- boris/gui_utilities.py +0 -1
- boris/modifier_coding_map_creator.py +0 -2
- boris/observation_operations.py +2 -4
- boris/param_panel.py +0 -4
- boris/plot_spectrogram_rt.py +2 -1
- boris/plot_waveform_rt.py +2 -1
- boris/plugins.py +41 -3
- boris/preferences.py +23 -7
- boris/time_budget_functions.py +0 -9
- boris/version.py +2 -2
- boris/video_equalizer.py +0 -2
- boris/view_df.py +0 -2
- boris/write_event.py +0 -9
- {boris_behav_obs-9.6.2.dist-info → boris_behav_obs-9.6.4.dist-info}/METADATA +22 -16
- {boris_behav_obs-9.6.2.dist-info → boris_behav_obs-9.6.4.dist-info}/RECORD +32 -31
- {boris_behav_obs-9.6.2.dist-info → boris_behav_obs-9.6.4.dist-info}/WHEEL +0 -0
- {boris_behav_obs-9.6.2.dist-info → boris_behav_obs-9.6.4.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.6.2.dist-info → boris_behav_obs-9.6.4.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.6.2.dist-info → boris_behav_obs-9.6.4.dist-info}/top_level.txt +0 -0
boris/export_events.py
CHANGED
|
@@ -702,7 +702,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
702
702
|
if not export_dir:
|
|
703
703
|
return
|
|
704
704
|
|
|
705
|
-
mem_command = ""
|
|
705
|
+
mem_command: str = ""
|
|
706
706
|
|
|
707
707
|
# see https://www.fon.hum.uva.nl/praat/manual/TextGrid_file_formats.html
|
|
708
708
|
|
|
@@ -795,6 +795,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
795
795
|
max_time = float(obs_interval[1]) + offset if obs_interval[1] != 0 else float(max_media_duration)
|
|
796
796
|
|
|
797
797
|
# delete events outside time interval
|
|
798
|
+
|
|
798
799
|
cursor.execute(
|
|
799
800
|
"DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)",
|
|
800
801
|
(
|
|
@@ -838,38 +839,30 @@ def export_events_as_textgrid(self) -> None:
|
|
|
838
839
|
|
|
839
840
|
next_obs: bool = False
|
|
840
841
|
|
|
841
|
-
|
|
842
|
-
total_media_duration = round(
|
|
843
|
-
observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id]), 3
|
|
844
|
-
)
|
|
845
|
-
"""
|
|
846
|
-
|
|
842
|
+
# number of items for size parameter
|
|
847
843
|
cursor.execute(
|
|
848
844
|
(
|
|
849
|
-
"SELECT COUNT(
|
|
850
|
-
"WHERE observation = ? AND subject IN ({})
|
|
851
|
-
",".join(["?"] * len(parameters[cfg.SELECTED_SUBJECTS]))
|
|
852
|
-
)
|
|
845
|
+
"SELECT COUNT(*) FROM (SELECT * FROM aggregated_events "
|
|
846
|
+
f"WHERE observation = ? AND subject IN ({','.join(['?'] * len(parameters[cfg.SELECTED_SUBJECTS]))}) GROUP BY subject, behavior) "
|
|
853
847
|
),
|
|
854
848
|
[obs_id] + parameters[cfg.SELECTED_SUBJECTS],
|
|
855
849
|
)
|
|
856
850
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
subjectsMax = max_time
|
|
851
|
+
subjects_num = int(cursor.fetchone()[0])
|
|
852
|
+
subjects_max = max_time
|
|
860
853
|
|
|
861
854
|
out = (
|
|
862
855
|
'File type = "ooTextFile"\n'
|
|
863
856
|
'Object class = "TextGrid"\n'
|
|
864
857
|
"\n"
|
|
865
858
|
f"xmin = 0.0\n"
|
|
866
|
-
f"xmax = {
|
|
859
|
+
f"xmax = {subjects_max}\n"
|
|
867
860
|
"tiers? <exists>\n"
|
|
868
|
-
f"size = {
|
|
861
|
+
f"size = {subjects_num}\n"
|
|
869
862
|
"item []:\n"
|
|
870
863
|
)
|
|
871
864
|
|
|
872
|
-
subject_index = 0
|
|
865
|
+
subject_index: int = 0
|
|
873
866
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
874
867
|
if subject not in [
|
|
875
868
|
x[cfg.EVENT_SUBJECT_FIELD_IDX] if x[cfg.EVENT_SUBJECT_FIELD_IDX] else cfg.NO_FOCAL_SUBJECT
|
|
@@ -877,7 +870,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
877
870
|
]:
|
|
878
871
|
continue
|
|
879
872
|
|
|
880
|
-
intervalsMin
|
|
873
|
+
intervalsMin = min_time
|
|
874
|
+
intervalsMax = max_time
|
|
881
875
|
|
|
882
876
|
# STATE events
|
|
883
877
|
cursor.execute(
|
|
@@ -892,68 +886,66 @@ def export_events_as_textgrid(self) -> None:
|
|
|
892
886
|
{"start": util.float2decimal(r["start"]), "stop": util.float2decimal(r["stop"]), "code": r["behavior"]}
|
|
893
887
|
for r in cursor.fetchall()
|
|
894
888
|
]
|
|
895
|
-
if
|
|
896
|
-
|
|
889
|
+
if rows:
|
|
890
|
+
out += interval_subject_header
|
|
897
891
|
|
|
898
|
-
|
|
892
|
+
count = 0
|
|
899
893
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
"It is not possible to create the Praat TextGrid file."
|
|
894
|
+
# check if 1st behavior starts at the beginning
|
|
895
|
+
if rows[0]["start"] > 0:
|
|
896
|
+
count += 1
|
|
897
|
+
out += interval_template.format(count=count, name="null", xmin=0.0, xmax=rows[0]["start"])
|
|
898
|
+
|
|
899
|
+
for idx, row in enumerate(rows):
|
|
900
|
+
# check if events are overlapping
|
|
901
|
+
if (idx + 1 < len(rows)) and (row["stop"] > rows[idx + 1]["start"]):
|
|
902
|
+
self.results.ptText.appendHtml(
|
|
903
|
+
(
|
|
904
|
+
f"The events overlap for subject <b>{subject}</b> in the observation <b>{obs_id}</b>. "
|
|
905
|
+
"It is not possible to create the Praat TextGrid file."
|
|
906
|
+
)
|
|
914
907
|
)
|
|
915
|
-
|
|
916
|
-
QApplication.processEvents()
|
|
908
|
+
QApplication.processEvents()
|
|
917
909
|
|
|
918
|
-
|
|
919
|
-
|
|
910
|
+
next_obs = True
|
|
911
|
+
break
|
|
920
912
|
|
|
921
|
-
|
|
913
|
+
count += 1
|
|
922
914
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
915
|
+
if (idx + 1 < len(rows)) and (rows[idx + 1]["start"] - dec("0.001") <= row["stop"] < rows[idx + 1]["start"]):
|
|
916
|
+
xmax = rows[idx + 1]["start"]
|
|
917
|
+
else:
|
|
918
|
+
xmax = row["stop"]
|
|
919
|
+
|
|
920
|
+
out += interval_template.format(count=count, name=row["code"], xmin=row["start"], xmax=xmax)
|
|
921
|
+
|
|
922
|
+
# check if no behavior
|
|
923
|
+
if (idx + 1 < len(rows)) and (row["stop"] < rows[idx + 1]["start"] - dec("0.001")):
|
|
924
|
+
count += 1
|
|
925
|
+
out += interval_template.format(
|
|
926
|
+
count=count,
|
|
927
|
+
name="null",
|
|
928
|
+
xmin=row["stop"],
|
|
929
|
+
xmax=rows[idx + 1]["start"],
|
|
930
|
+
)
|
|
927
931
|
|
|
928
|
-
|
|
932
|
+
if next_obs:
|
|
933
|
+
break
|
|
929
934
|
|
|
930
|
-
# check if
|
|
931
|
-
if
|
|
935
|
+
# check if last event ends at the end of media file
|
|
936
|
+
if rows[-1]["stop"] < max_time:
|
|
932
937
|
count += 1
|
|
933
|
-
out += interval_template.format(
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if rows[-1]["stop"] < max_time:
|
|
945
|
-
count += 1
|
|
946
|
-
out += interval_template.format(count=count, name="null", xmin=rows[-1]["stop"], xmax=max_time)
|
|
947
|
-
|
|
948
|
-
# add info
|
|
949
|
-
subject_index += 1
|
|
950
|
-
out = out.format(
|
|
951
|
-
subject_index=subject_index,
|
|
952
|
-
subject=subject,
|
|
953
|
-
intervalsSize=count,
|
|
954
|
-
intervalsMin=intervalsMin,
|
|
955
|
-
intervalsMax=intervalsMax,
|
|
956
|
-
)
|
|
938
|
+
out += interval_template.format(count=count, name="null", xmin=rows[-1]["stop"], xmax=max_time)
|
|
939
|
+
|
|
940
|
+
# add info
|
|
941
|
+
subject_index += 1
|
|
942
|
+
out = out.format(
|
|
943
|
+
subject_index=subject_index,
|
|
944
|
+
subject=subject,
|
|
945
|
+
intervalsSize=count,
|
|
946
|
+
intervalsMin=intervalsMin,
|
|
947
|
+
intervalsMax=intervalsMax,
|
|
948
|
+
)
|
|
957
949
|
|
|
958
950
|
# POINT events
|
|
959
951
|
cursor.execute(
|
|
@@ -987,12 +979,12 @@ def export_events_as_textgrid(self) -> None:
|
|
|
987
979
|
continue
|
|
988
980
|
|
|
989
981
|
# check if file already exists
|
|
990
|
-
if mem_command != cfg.OVERWRITE_ALL and pl.Path(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.
|
|
982
|
+
if mem_command != cfg.OVERWRITE_ALL and pl.Path(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid").is_file():
|
|
991
983
|
if mem_command == cfg.SKIP_ALL:
|
|
992
984
|
continue
|
|
993
985
|
mem_command = dialog.MessageDialog(
|
|
994
986
|
cfg.programName,
|
|
995
|
-
f"The file <b>{pl.Path(export_dir) / util.safeFileName(obs_id)}.
|
|
987
|
+
f"The file <b>{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid</b> already exists.",
|
|
996
988
|
[cfg.OVERWRITE, cfg.OVERWRITE_ALL, cfg.SKIP, cfg.SKIP_ALL, cfg.CANCEL],
|
|
997
989
|
)
|
|
998
990
|
if mem_command == cfg.CANCEL:
|
|
@@ -1001,13 +993,13 @@ def export_events_as_textgrid(self) -> None:
|
|
|
1001
993
|
continue
|
|
1002
994
|
|
|
1003
995
|
try:
|
|
1004
|
-
with open(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.
|
|
996
|
+
with open(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid", "w") as f:
|
|
1005
997
|
f.write(out)
|
|
1006
998
|
file_count += 1
|
|
1007
|
-
self.results.ptText.appendHtml(f"File {pl.Path(export_dir) / util.safeFileName(obs_id)}.
|
|
999
|
+
self.results.ptText.appendHtml(f"File {pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid was created.")
|
|
1008
1000
|
QApplication.processEvents()
|
|
1009
1001
|
except Exception:
|
|
1010
|
-
self.results.ptText.appendHtml(f"The file {pl.Path(export_dir) / util.safeFileName(obs_id)}.
|
|
1002
|
+
self.results.ptText.appendHtml(f"The file {pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid can not be created.")
|
|
1011
1003
|
QApplication.processEvents()
|
|
1012
1004
|
|
|
1013
1005
|
self.results.ptText.appendHtml(f"Done. {file_count} file(s) were created in {export_dir}.")
|
boris/gui_utilities.py
CHANGED
|
@@ -66,7 +66,6 @@ def restore_geometry(widget: QWidget, widget_name: str, default_width_height):
|
|
|
66
66
|
ini_file_path = pl.Path.home() / pl.Path(".boris")
|
|
67
67
|
if ini_file_path.is_file():
|
|
68
68
|
settings = QSettings(str(ini_file_path), QSettings.IniFormat)
|
|
69
|
-
print(settings.value(f"{widget_name} geometry"))
|
|
70
69
|
widget.restoreGeometry(settings.value(f"{widget_name} geometry"))
|
|
71
70
|
logging.debug(f"geometry restored for {widget_name} {settings.value(f'{widget_name} geometry')}")
|
|
72
71
|
else:
|
boris/observation_operations.py
CHANGED
|
@@ -1301,10 +1301,8 @@ def init_mpv(self):
|
|
|
1301
1301
|
|
|
1302
1302
|
logging.debug("function: init_mpv")
|
|
1303
1303
|
|
|
1304
|
-
""
|
|
1305
|
-
print(f"{self.winId()=}")
|
|
1306
|
-
print(f"{str(int(self.winId()))=}")
|
|
1307
|
-
"""
|
|
1304
|
+
# print(f"{self.winId()=}")
|
|
1305
|
+
# print(f"{str(int(self.winId()))=}")
|
|
1308
1306
|
|
|
1309
1307
|
subprocess.Popen(
|
|
1310
1308
|
[
|
boris/param_panel.py
CHANGED
|
@@ -96,12 +96,8 @@ class Param_panel(QDialog, Ui_Dialog):
|
|
|
96
96
|
# Set start_time and end_time widgets values even if it is not shown with
|
|
97
97
|
# more than 1 observation as some analyses might use it (eg: advanced event filtering)
|
|
98
98
|
|
|
99
|
-
print(f"{self.end_interval=}")
|
|
100
|
-
|
|
101
99
|
end_interval = self.end_interval if self.end_interval != 0 else self.media_duration
|
|
102
100
|
|
|
103
|
-
print(f"{end_interval=}")
|
|
104
|
-
|
|
105
101
|
self.start_time.set_time(self.start_interval)
|
|
106
102
|
self.end_time.set_time(end_interval)
|
|
107
103
|
self.frm_time_interval.setEnabled(False)
|
boris/plot_spectrogram_rt.py
CHANGED
|
@@ -129,7 +129,8 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
129
129
|
try:
|
|
130
130
|
wav = wave.open(wav_file, "r")
|
|
131
131
|
frames = wav.readframes(-1)
|
|
132
|
-
sound_info = np.fromstring(frames, dtype=np.int16)
|
|
132
|
+
# sound_info = np.fromstring(frames, dtype=np.int16)
|
|
133
|
+
sound_info = np.frombuffer(frames, dtype=np.int16)
|
|
133
134
|
frame_rate = wav.getframerate()
|
|
134
135
|
wav.close()
|
|
135
136
|
return sound_info, frame_rate
|
boris/plot_waveform_rt.py
CHANGED
|
@@ -109,7 +109,8 @@ class Plot_waveform_RT(QWidget):
|
|
|
109
109
|
try:
|
|
110
110
|
wav = wave.open(wav_file, "r")
|
|
111
111
|
frames = wav.readframes(-1)
|
|
112
|
-
signal = np.fromstring(frames, dtype=np.int16)
|
|
112
|
+
# signal = np.fromstring(frames, dtype=np.int16)
|
|
113
|
+
signal = np.frombuffer(frames, dtype=np.int16)
|
|
113
114
|
frame_rate = wav.getframerate()
|
|
114
115
|
wav.close()
|
|
115
116
|
return signal, frame_rate
|
boris/plugins.py
CHANGED
|
@@ -105,9 +105,11 @@ def get_r_plugin_description(plugin_path: str) -> str | None:
|
|
|
105
105
|
|
|
106
106
|
def load_plugins(self):
|
|
107
107
|
"""
|
|
108
|
-
load selected plugins in
|
|
108
|
+
load selected plugins in config_param
|
|
109
109
|
"""
|
|
110
110
|
|
|
111
|
+
logging.debug("Loading plugins")
|
|
112
|
+
|
|
111
113
|
def msg():
|
|
112
114
|
QMessageBox.warning(
|
|
113
115
|
self,
|
|
@@ -124,7 +126,25 @@ def load_plugins(self):
|
|
|
124
126
|
for file_ in sorted((Path(__file__).parent / "analysis_plugins").glob("*.py")):
|
|
125
127
|
if file_.name.startswith("_"):
|
|
126
128
|
continue
|
|
127
|
-
|
|
129
|
+
|
|
130
|
+
logging.debug(f"Loading plugin: {Path(file_).stem}")
|
|
131
|
+
|
|
132
|
+
# test module
|
|
133
|
+
module_name = Path(file_).stem
|
|
134
|
+
spec = importlib.util.spec_from_file_location(module_name, file_)
|
|
135
|
+
plugin_module = importlib.util.module_from_spec(spec)
|
|
136
|
+
spec.loader.exec_module(plugin_module)
|
|
137
|
+
attributes_list = dir(plugin_module)
|
|
138
|
+
|
|
139
|
+
if "__plugin_name__" in attributes_list:
|
|
140
|
+
plugin_name = plugin_module.__plugin_name__
|
|
141
|
+
else:
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
if "run" not in attributes_list:
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
# plugin_name = get_plugin_name(file_)
|
|
128
148
|
if plugin_name is not None and plugin_name not in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
|
|
129
149
|
# check if plugin with same name already loaded
|
|
130
150
|
if plugin_name in self.config_param[cfg.ANALYSIS_PLUGINS]:
|
|
@@ -138,7 +158,25 @@ def load_plugins(self):
|
|
|
138
158
|
for file_ in sorted(Path(self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, "")).glob("*.py")):
|
|
139
159
|
if file_.name.startswith("_"):
|
|
140
160
|
continue
|
|
141
|
-
|
|
161
|
+
|
|
162
|
+
logging.debug(f"Loading personal plugin: {Path(file_).stem}")
|
|
163
|
+
|
|
164
|
+
# test module
|
|
165
|
+
module_name = Path(file_).stem
|
|
166
|
+
spec = importlib.util.spec_from_file_location(module_name, file_)
|
|
167
|
+
plugin_module = importlib.util.module_from_spec(spec)
|
|
168
|
+
spec.loader.exec_module(plugin_module)
|
|
169
|
+
attributes_list = dir(plugin_module)
|
|
170
|
+
|
|
171
|
+
if "__plugin_name__" in attributes_list:
|
|
172
|
+
plugin_name = plugin_module.__plugin_name__
|
|
173
|
+
else:
|
|
174
|
+
continue
|
|
175
|
+
|
|
176
|
+
if "run" not in attributes_list:
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
# plugin_name = get_plugin_name(file_)
|
|
142
180
|
if plugin_name is not None and plugin_name not in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
|
|
143
181
|
# check if plugin with same name already loaded
|
|
144
182
|
if plugin_name in self.config_param[cfg.ANALYSIS_PLUGINS]:
|
boris/preferences.py
CHANGED
|
@@ -60,7 +60,7 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
60
60
|
# Create a monospace QFont
|
|
61
61
|
monospace_font = QFont("Courier New") # or "Monospace", "Consolas", "Liberation Mono", etc.
|
|
62
62
|
monospace_font.setStyleHint(QFont.Monospace)
|
|
63
|
-
monospace_font.setPointSize(
|
|
63
|
+
monospace_font.setPointSize(12)
|
|
64
64
|
self.pte_plugin_code.setFont(monospace_font)
|
|
65
65
|
|
|
66
66
|
def browse_plugins_dir(self):
|
|
@@ -83,7 +83,6 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
83
83
|
if plugin_name in [self.lv_all_plugins.item(i).text() for i in range(self.lv_all_plugins.count())]:
|
|
84
84
|
continue
|
|
85
85
|
item = QListWidgetItem(plugin_name)
|
|
86
|
-
# item = QListWidgetItem(file_.stem)
|
|
87
86
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
|
88
87
|
item.setCheckState(Qt.Checked)
|
|
89
88
|
item.setData(100, str(file_))
|
|
@@ -152,6 +151,7 @@ def preferences(self):
|
|
|
152
151
|
|
|
153
152
|
plugin_path = item.data(100)
|
|
154
153
|
|
|
154
|
+
# Python plugins
|
|
155
155
|
if Path(plugin_path).suffix == ".py":
|
|
156
156
|
import importlib
|
|
157
157
|
|
|
@@ -159,21 +159,35 @@ def preferences(self):
|
|
|
159
159
|
spec = importlib.util.spec_from_file_location(module_name, plugin_path)
|
|
160
160
|
plugin_module = importlib.util.module_from_spec(spec)
|
|
161
161
|
spec.loader.exec_module(plugin_module)
|
|
162
|
+
attributes_list = dir(plugin_module)
|
|
162
163
|
|
|
163
164
|
out: list = []
|
|
164
|
-
out.append(plugin_module.__plugin_name__ + "\n")
|
|
165
|
-
out.append(plugin_module.__author__)
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
out.append((plugin_module.__plugin_name__ + "\n") if "__plugin_name__" in attributes_list else "No plugin name provided")
|
|
166
|
+
out.append(plugin_module.__author__ if "__author__" in attributes_list else "No author provided")
|
|
167
|
+
version_str: str = ""
|
|
168
|
+
if "__version__" in attributes_list:
|
|
169
|
+
version_str += str(plugin_module.__version__)
|
|
170
|
+
if "__version_date__" in attributes_list:
|
|
171
|
+
version_str += " " if version_str else ""
|
|
172
|
+
version_str += f"({plugin_module.__version_date__})"
|
|
173
|
+
|
|
174
|
+
out.append(f"Version: {version_str}\n" if version_str else "No version provided")
|
|
175
|
+
|
|
176
|
+
# out.append(plugin_module.run.__doc__.strip())
|
|
177
|
+
# description
|
|
178
|
+
if "__description__" in attributes_list:
|
|
179
|
+
out.append("Description:\n")
|
|
180
|
+
out.append(plugin_module.__description__ if "__description__" in attributes_list else "No description provided")
|
|
168
181
|
|
|
169
182
|
preferencesWindow.pte_plugin_description.setPlainText("\n".join(out))
|
|
170
183
|
|
|
184
|
+
# R plugins
|
|
171
185
|
if Path(plugin_path).suffix == ".R":
|
|
172
186
|
plugin_description = plugins.get_r_plugin_description(plugin_path)
|
|
173
187
|
if plugin_description is not None:
|
|
174
188
|
preferencesWindow.pte_plugin_description.setPlainText("\n".join(plugin_description.split("\\n")))
|
|
175
189
|
else:
|
|
176
|
-
preferencesWindow.pte_plugin_description.setPlainText("
|
|
190
|
+
preferencesWindow.pte_plugin_description.setPlainText("No description provided")
|
|
177
191
|
|
|
178
192
|
# display plugin code
|
|
179
193
|
try:
|
|
@@ -254,6 +268,7 @@ def preferences(self):
|
|
|
254
268
|
|
|
255
269
|
preferencesWindow.lw_personal_plugins.clear()
|
|
256
270
|
if self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, ""):
|
|
271
|
+
# Python plugins
|
|
257
272
|
for file_ in Path(self.config_param[cfg.PERSONAL_PLUGINS_DIR]).glob("*.py"):
|
|
258
273
|
if file_.name.startswith("_"):
|
|
259
274
|
continue
|
|
@@ -272,6 +287,7 @@ def preferences(self):
|
|
|
272
287
|
item.setData(100, str(file_))
|
|
273
288
|
preferencesWindow.lw_personal_plugins.addItem(item)
|
|
274
289
|
|
|
290
|
+
# R plugins
|
|
275
291
|
for file_ in Path(self.config_param[cfg.PERSONAL_PLUGINS_DIR]).glob("*.R"):
|
|
276
292
|
plugin_name = plugins.get_r_plugin_name(file_)
|
|
277
293
|
if plugin_name is None:
|
boris/time_budget_functions.py
CHANGED
|
@@ -1021,15 +1021,6 @@ def time_budget_analysis(
|
|
|
1021
1021
|
continue
|
|
1022
1022
|
|
|
1023
1023
|
if len(rows) % 2: # unpaired events
|
|
1024
|
-
"""
|
|
1025
|
-
print()
|
|
1026
|
-
print(f"{subject=}")
|
|
1027
|
-
print(f"{behavior=}")
|
|
1028
|
-
print()
|
|
1029
|
-
for row in rows:
|
|
1030
|
-
print(f"{row['observation']=} {row['occurence']=}")
|
|
1031
|
-
print()
|
|
1032
|
-
"""
|
|
1033
1024
|
out_cat.append(
|
|
1034
1025
|
{
|
|
1035
1026
|
"subject": subject,
|
boris/version.py
CHANGED
boris/video_equalizer.py
CHANGED
|
@@ -60,8 +60,6 @@ class Video_equalizer(QDialog, Ui_Equalizer):
|
|
|
60
60
|
if n_player not in self.equalizer:
|
|
61
61
|
return
|
|
62
62
|
|
|
63
|
-
print(self.equalizer)
|
|
64
|
-
|
|
65
63
|
self.hs_brightness.setValue(self.equalizer[n_player]["hs_brightness"])
|
|
66
64
|
self.lb_brightness.setText(str(self.equalizer[n_player]["hs_brightness"]))
|
|
67
65
|
|
boris/view_df.py
CHANGED
|
@@ -49,8 +49,6 @@ class View_df(QWidget, Ui_Form):
|
|
|
49
49
|
self.lb_plugin_info.setText(f"{plugin_name} v. {plugin_version}")
|
|
50
50
|
self.setWindowTitle(f"{plugin_name} v. {plugin_version}")
|
|
51
51
|
|
|
52
|
-
# print(f"{self.df=}")
|
|
53
|
-
|
|
54
52
|
self.pb_close.clicked.connect(self.close)
|
|
55
53
|
self.pb_save.clicked.connect(self.save)
|
|
56
54
|
|
boris/write_event.py
CHANGED
|
@@ -68,8 +68,6 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
68
68
|
)
|
|
69
69
|
return 1
|
|
70
70
|
|
|
71
|
-
print(f"{mem_time=}")
|
|
72
|
-
|
|
73
71
|
if mem_time < self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[0]:
|
|
74
72
|
_ = dialog.MessageDialog(
|
|
75
73
|
cfg.programName,
|
|
@@ -104,12 +102,6 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
104
102
|
|
|
105
103
|
# add media creation date/time
|
|
106
104
|
|
|
107
|
-
"""
|
|
108
|
-
print(f"{media_file_name=}")
|
|
109
|
-
print(f"{mem_time=}")
|
|
110
|
-
"""
|
|
111
|
-
print(f"{self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO]=}")
|
|
112
|
-
|
|
113
105
|
mem_time += dec(
|
|
114
106
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO][cfg.MEDIA_CREATION_TIME][media_file_name_posix]
|
|
115
107
|
)
|
|
@@ -413,7 +405,6 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
413
405
|
r = modifiers_selector.exec_()
|
|
414
406
|
if r:
|
|
415
407
|
selected_modifiers = modifiers_selector.get_modifiers()
|
|
416
|
-
# print(f"{selected_modifiers=}")
|
|
417
408
|
|
|
418
409
|
behavior_to_stop_modifier_str: str = ""
|
|
419
410
|
for idx in util.sorted_keys(selected_modifiers):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: boris-behav-obs
|
|
3
|
-
Version: 9.6.
|
|
3
|
+
Version: 9.6.4
|
|
4
4
|
Summary: BORIS - Behavioral Observation Research Interactive Software
|
|
5
5
|
Author-email: Olivier Friard <olivier.friard@unito.it>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -18,16 +18,16 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
18
18
|
Requires-Python: >=3.12
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.TXT
|
|
21
|
-
Requires-Dist: exifread
|
|
22
|
-
Requires-Dist: numpy
|
|
23
|
-
Requires-Dist: matplotlib
|
|
24
|
-
Requires-Dist: pandas
|
|
25
|
-
Requires-Dist: tablib[cli,html,ods,pandas,xls,xlsx]
|
|
26
|
-
Requires-Dist: pyreadr
|
|
21
|
+
Requires-Dist: exifread==3.5.1
|
|
22
|
+
Requires-Dist: numpy==2.3.2
|
|
23
|
+
Requires-Dist: matplotlib==3.10.5
|
|
24
|
+
Requires-Dist: pandas==2.3.2
|
|
25
|
+
Requires-Dist: tablib[cli,html,ods,pandas,xls,xlsx]==3.8.0
|
|
26
|
+
Requires-Dist: pyreadr==0.5.3
|
|
27
27
|
Requires-Dist: pyside6==6.9
|
|
28
|
-
Requires-Dist: hachoir
|
|
29
|
-
Requires-Dist: scipy
|
|
30
|
-
Requires-Dist: scikit-learn
|
|
28
|
+
Requires-Dist: hachoir==3.3.0
|
|
29
|
+
Requires-Dist: scipy==1.16.1
|
|
30
|
+
Requires-Dist: scikit-learn==1.7.1
|
|
31
31
|
Provides-Extra: dev
|
|
32
32
|
Requires-Dist: ruff; extra == "dev"
|
|
33
33
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -49,7 +49,10 @@ You can not longer run BORIS natively on MacOS (since v.8). Some alternatives to
|
|
|
49
49
|
|
|
50
50
|
It provides also some analysis tools like time budget and some plotting functions.
|
|
51
51
|
|
|
52
|
-
The
|
|
52
|
+
<!-- The BO-RIS paper has more than [ citations](https://www.boris.unito.it/citations) in peer-reviewed scientific publications. -->
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
The BORIS paper has more than 2332 citations in peer-reviewed scientific publications.
|
|
53
56
|
|
|
54
57
|
|
|
55
58
|
|
|
@@ -59,13 +62,17 @@ See the official [BORIS web site](https://www.boris.unito.it).
|
|
|
59
62
|
[](https://www.python.org)
|
|
60
63
|

|
|
61
64
|

|
|
65
|
+
[](https://pypi.org/project/boris-behav-obs/)
|
|
66
|
+
|
|
62
67
|
[](https://pepy.tech/project/boris-behav-obs)
|
|
63
68
|

|
|
64
|
-
|
|
65
|
-

|
|
69
|
+

|
|
66
70
|
|
|
71
|
+

|
|
67
72
|
|
|
68
73
|
|
|
74
|
+

|
|
75
|
+
[](https://github.com/olivierfriard/BORIS/stargazers)
|
|
69
76
|
|
|
70
77
|
# Documentation
|
|
71
78
|
|
|
@@ -82,8 +89,7 @@ Some [video tutorials](https://www.boris.unito.it/video_tutorials/) are availabl
|
|
|
82
89
|
# Bug reports and feature requests
|
|
83
90
|
|
|
84
91
|
|
|
85
|
-
To search for bugs, report them or request a feature, please use the
|
|
86
|
-
https://github.com/olivierfriard/BORIS/issues
|
|
92
|
+
To search for bugs, report them or request a feature, please use the [GitHub issues tracker](https://github.com/olivierfriard/BORIS/issues)
|
|
87
93
|
|
|
88
94
|
|
|
89
95
|
|
|
@@ -129,7 +135,7 @@ GNU General Public License for more details.
|
|
|
129
135
|
|
|
130
136
|
Distributed with a [GPL v.3 license](LICENSE.TXT).
|
|
131
137
|
|
|
132
|
-
Copyright (C) 2012-
|
|
138
|
+
Copyright (C) 2012-2025 Olivier Friard
|
|
133
139
|
|
|
134
140
|
|
|
135
141
|
|