boris-behav-obs 9.7.15__py3-none-any.whl → 9.8.2__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/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +4 -3
- boris/add_modifier.py +1 -1
- boris/advanced_event_filtering.py +1 -1
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +2 -2
- boris/behav_coding_map_creator.py +1 -1
- boris/behavior_binary_table.py +1 -1
- boris/behaviors_coding_map.py +1 -1
- boris/boris_cli.py +1 -1
- boris/cmd_arguments.py +1 -1
- boris/coding_pad.py +1 -1
- boris/config.py +10 -1
- boris/config_file.py +18 -19
- boris/connections.py +12 -13
- boris/converters.py +1 -1
- boris/cooccurence.py +1 -1
- boris/core.py +41 -42
- boris/core_qrc.py +1830 -1967
- boris/core_ui.py +1 -1
- boris/db_functions.py +5 -14
- boris/dialog.py +24 -24
- boris/edit_event.py +1 -1
- boris/event_operations.py +1 -1
- boris/events_cursor.py +1 -1
- boris/events_snapshots.py +133 -78
- boris/exclusion_matrix.py +1 -1
- boris/export_events.py +49 -43
- boris/export_observation.py +1 -1
- boris/external_processes.py +1 -1
- boris/geometric_measurement.py +1 -1
- boris/gui_utilities.py +1 -1
- boris/image_overlay.py +1 -1
- boris/import_observations.py +1 -1
- boris/ipc_mpv.py +1 -1
- boris/irr.py +1 -1
- boris/latency.py +1 -1
- boris/measurement_widget.py +1 -1
- boris/media_file.py +1 -1
- boris/menu_options.py +14 -12
- boris/modifier_coding_map_creator.py +1 -1
- boris/modifiers_coding_map.py +1 -1
- boris/observation.py +13 -14
- boris/observation_operations.py +1 -1
- boris/observations_list.py +1 -1
- boris/otx_parser.py +1 -1
- boris/param_panel.py +1 -1
- boris/player_dock_widget.py +1 -1
- boris/plot_data_module.py +1 -1
- boris/plot_events.py +1 -1
- boris/plot_events_rt.py +1 -1
- boris/plot_spectrogram_rt.py +2 -2
- boris/plot_waveform_rt.py +1 -1
- boris/plugins.py +1 -1
- boris/preferences.py +1 -1
- boris/project.py +1 -1
- boris/project_functions.py +12 -12
- boris/project_import_export.py +1 -1
- boris/select_modifiers.py +1 -1
- boris/select_observations.py +22 -23
- boris/select_subj_behav.py +4 -4
- boris/state_events.py +1 -1
- boris/subjects_pad.py +1 -1
- boris/synthetic_time_budget.py +1 -1
- boris/time_budget_functions.py +1 -1
- boris/time_budget_widget.py +1 -1
- boris/transitions.py +1 -1
- boris/utilities.py +1 -1
- boris/version.py +3 -3
- boris/video_equalizer.py +1 -1
- boris/video_operations.py +1 -1
- boris/view_df.py +28 -4
- boris/write_event.py +1 -1
- {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/METADATA +2 -2
- boris_behav_obs-9.8.2.dist-info/RECORD +110 -0
- {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.15.dist-info/RECORD +0 -110
- {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/top_level.txt +0 -0
boris/core.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2026 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -25,8 +25,6 @@ import os
|
|
|
25
25
|
import sys
|
|
26
26
|
from pathlib import Path
|
|
27
27
|
|
|
28
|
-
# os.environ["PATH"] = os.path.dirname(__file__) + os.sep + "misc" + os.pathsep + os.environ["PATH"]
|
|
29
|
-
|
|
30
28
|
os.environ["PATH"] = str(Path(__file__).parent / "misc") + os.pathsep + os.environ["PATH"]
|
|
31
29
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".")))
|
|
32
30
|
|
|
@@ -431,7 +429,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
431
429
|
self.tv_events.setItemDelegate(events_cursor.StyledItemDelegateTriangle(self.events_current_row))
|
|
432
430
|
|
|
433
431
|
connections.connections(self)
|
|
434
|
-
self.config_param = cfg.INIT_PARAM
|
|
432
|
+
self.config_param = dict(cfg.INIT_PARAM)
|
|
435
433
|
config_file.read(self)
|
|
436
434
|
menu_options.update_menu(self)
|
|
437
435
|
|
|
@@ -1626,7 +1624,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1626
1624
|
self.currentSubject = subject
|
|
1627
1625
|
self.lbFocalSubject.setText(f" Focal subject: <b>{self.currentSubject}</b>")
|
|
1628
1626
|
|
|
1629
|
-
def getCurrentMediaByFrame(self, player: str, requiredFrame: int, fps: float):
|
|
1627
|
+
def getCurrentMediaByFrame(self, player: str, requiredFrame: int, fps: float) -> tuple[str, int]:
|
|
1630
1628
|
"""
|
|
1631
1629
|
Args:
|
|
1632
1630
|
player (str): player
|
|
@@ -1637,7 +1635,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1637
1635
|
currentMedia
|
|
1638
1636
|
frameCurrentMedia
|
|
1639
1637
|
"""
|
|
1640
|
-
currentMedia
|
|
1638
|
+
currentMedia: str = ""
|
|
1639
|
+
frameCurrentMedia: int = 0
|
|
1641
1640
|
frameMs = 1000 / fps
|
|
1642
1641
|
for idx, media in enumerate(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE][player]):
|
|
1643
1642
|
if requiredFrame * frameMs < sum(self.dw_player[int(player) - 1].media_durations[0 : idx + 1]):
|
|
@@ -1646,7 +1645,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1646
1645
|
break
|
|
1647
1646
|
return currentMedia, round(frameCurrentMedia)
|
|
1648
1647
|
|
|
1649
|
-
def extract_frame(self, dw):
|
|
1648
|
+
def extract_frame(self, dw) -> None:
|
|
1650
1649
|
"""
|
|
1651
1650
|
for MEDIA obs: extract frame from video and visualize it in frame_viewer
|
|
1652
1651
|
for IMAGES obs: load picture and visualize it in frame_viewer, extract EXIF Date/Time Original tag if available
|
|
@@ -1729,23 +1728,23 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1729
1728
|
# show current states in subjects table
|
|
1730
1729
|
self.show_current_states_in_subjects_table()
|
|
1731
1730
|
|
|
1732
|
-
def frame_image_clicked(self, n_player, event):
|
|
1731
|
+
def frame_image_clicked(self, n_player, event) -> None:
|
|
1733
1732
|
geometric_measurement.image_clicked(self, n_player, event)
|
|
1734
1733
|
|
|
1735
|
-
def timer_plot_data_out(self, w):
|
|
1734
|
+
def timer_plot_data_out(self, w) -> None:
|
|
1736
1735
|
"""
|
|
1737
1736
|
update plot in w (Plot_data class)
|
|
1738
1737
|
triggered by timers in self.ext_data_timer_list
|
|
1739
1738
|
"""
|
|
1740
1739
|
w.update_plot(self.getLaps())
|
|
1741
1740
|
|
|
1742
|
-
def signal_from_widget(self, event):
|
|
1741
|
+
def signal_from_widget(self, event) -> None:
|
|
1743
1742
|
"""
|
|
1744
1743
|
receive signal from widget
|
|
1745
1744
|
"""
|
|
1746
1745
|
self.keyPressEvent(event)
|
|
1747
1746
|
|
|
1748
|
-
def reload_frame(self):
|
|
1747
|
+
def reload_frame(self) -> None:
|
|
1749
1748
|
"""
|
|
1750
1749
|
receive signal to reload frames from geometric measurements
|
|
1751
1750
|
"""
|
|
@@ -1761,7 +1760,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1761
1760
|
|
|
1762
1761
|
geometric_measurement.redraw_measurements(self)
|
|
1763
1762
|
|
|
1764
|
-
def save_picture_with_measurements(self, mode: str):
|
|
1763
|
+
def save_picture_with_measurements(self, mode: str) -> None:
|
|
1765
1764
|
"""
|
|
1766
1765
|
receive signal to save picture from geometric measurements
|
|
1767
1766
|
"""
|
|
@@ -1946,7 +1945,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1946
1945
|
|
|
1947
1946
|
pixmap.save(str(Path(output_dir) / file_name.with_suffix(".jpg")), "JPG")
|
|
1948
1947
|
|
|
1949
|
-
def resize_dw(self, dw_id):
|
|
1948
|
+
def resize_dw(self, dw_id) -> None:
|
|
1950
1949
|
"""
|
|
1951
1950
|
dockwidget was resized. Adapt overlay if any
|
|
1952
1951
|
"""
|
|
@@ -4923,21 +4922,21 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4923
4922
|
self.update_project_zoom_pan_values()
|
|
4924
4923
|
|
|
4925
4924
|
# frame-by-frame mode
|
|
4926
|
-
if ek == 47 or (ek == Qt.Key_Left and modifier != cfg.CTRL_KEY): # / one frame back
|
|
4925
|
+
if ek == 47 or (ek == Qt.Key.Key_Left and modifier != cfg.CTRL_KEY): # / one frame back
|
|
4927
4926
|
self.previous_frame()
|
|
4928
4927
|
return
|
|
4929
4928
|
|
|
4930
|
-
if ek == 42 or (ek == Qt.Key_Right and modifier != cfg.CTRL_KEY): # * read next frame
|
|
4929
|
+
if ek == 42 or (ek == Qt.Key.Key_Right and modifier != cfg.CTRL_KEY): # * read next frame
|
|
4931
4930
|
self.next_frame()
|
|
4932
4931
|
return
|
|
4933
4932
|
|
|
4934
4933
|
if self.playerType in (cfg.MEDIA, cfg.IMAGES):
|
|
4935
4934
|
# next media file (page up)
|
|
4936
|
-
if ek == Qt.Key_PageUp:
|
|
4935
|
+
if ek == Qt.Key.Key_PageUp:
|
|
4937
4936
|
self.next_media_file()
|
|
4938
4937
|
|
|
4939
4938
|
# previous media file (page down)
|
|
4940
|
-
if ek == Qt.Key_PageDown:
|
|
4939
|
+
if ek == Qt.Key.Key_PageDown:
|
|
4941
4940
|
self.previous_media_file()
|
|
4942
4941
|
|
|
4943
4942
|
if not self.pj[cfg.ETHOGRAM]:
|
|
@@ -4980,20 +4979,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4980
4979
|
return
|
|
4981
4980
|
|
|
4982
4981
|
if (
|
|
4983
|
-
((ek in range(33, 256)) and (ek not in [Qt.Key_Plus, Qt.Key_Minus]))
|
|
4982
|
+
((ek in range(33, 256)) and (ek not in [Qt.Key.Key_Plus, Qt.Key.Key_Minus]))
|
|
4984
4983
|
or (ek in cfg.function_keys)
|
|
4985
|
-
or (ek == Qt.Key_Enter and event.text())
|
|
4984
|
+
or (ek == Qt.Key.Key_Enter and event.text())
|
|
4986
4985
|
): # click from coding pad or subjects pad
|
|
4987
4986
|
if ek in cfg.function_keys:
|
|
4988
4987
|
ek_unichr = cfg.function_keys[ek]
|
|
4989
|
-
elif ek != Qt.Key_Enter:
|
|
4988
|
+
elif ek != Qt.Key.Key_Enter:
|
|
4990
4989
|
ek_unichr = ek_text
|
|
4991
|
-
elif ek == Qt.Key_Enter and event.text(): # click from coding pad or subjects pad
|
|
4990
|
+
elif ek == Qt.Key.Key_Enter and event.text(): # click from coding pad or subjects pad
|
|
4992
4991
|
ek_unichr = ek_text
|
|
4993
4992
|
|
|
4994
4993
|
logging.debug(f"{ek_unichr = }")
|
|
4995
4994
|
|
|
4996
|
-
if ek == Qt.Key_Enter and event.text(): # click from coding pad or subjects pad
|
|
4995
|
+
if ek == Qt.Key.Key_Enter and event.text(): # click from coding pad or subjects pad
|
|
4997
4996
|
ek_unichr = ""
|
|
4998
4997
|
|
|
4999
4998
|
if "#subject#" in event.text():
|
|
@@ -5035,7 +5034,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5035
5034
|
r = dialog.MessageDialog(
|
|
5036
5035
|
cfg.programName,
|
|
5037
5036
|
"This key defines a behavior and a subject. Choose one",
|
|
5038
|
-
|
|
5037
|
+
("&Behavior", "&Subject", cfg.CANCEL),
|
|
5039
5038
|
)
|
|
5040
5039
|
if r == cfg.CANCEL:
|
|
5041
5040
|
return
|
|
@@ -5088,7 +5087,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5088
5087
|
"The focal subject is not defined. Do you want to continue?\n"
|
|
5089
5088
|
"Use Preferences menu option to modify this behaviour."
|
|
5090
5089
|
),
|
|
5091
|
-
|
|
5090
|
+
(cfg.YES, cfg.NO),
|
|
5092
5091
|
)
|
|
5093
5092
|
|
|
5094
5093
|
if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.MEDIA and flagPlayerPlaying:
|
|
@@ -5260,7 +5259,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5260
5259
|
dialog.MessageDialog(
|
|
5261
5260
|
cfg.programName,
|
|
5262
5261
|
f"<b>{self.find_dialog.findText.text()}</b> not found. Search from beginning?",
|
|
5263
|
-
|
|
5262
|
+
(cfg.YES, cfg.NO),
|
|
5264
5263
|
)
|
|
5265
5264
|
== cfg.YES
|
|
5266
5265
|
):
|
|
@@ -5281,15 +5280,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5281
5280
|
self.find_replace_dialog.close()
|
|
5282
5281
|
return
|
|
5283
5282
|
if self.find_replace_dialog.combo_fields.currentIndex() == 0: # choose a field
|
|
5284
|
-
dialog.MessageDialog(cfg.programName, "Choose a field.",
|
|
5283
|
+
dialog.MessageDialog(cfg.programName, "Choose a field.", (cfg.OK,))
|
|
5285
5284
|
return
|
|
5286
5285
|
|
|
5287
5286
|
if not self.find_replace_dialog.findText.text():
|
|
5288
|
-
dialog.MessageDialog(cfg.programName, "There is nothing to find.",
|
|
5287
|
+
dialog.MessageDialog(cfg.programName, "There is nothing to find.", (cfg.OK,))
|
|
5289
5288
|
return
|
|
5290
5289
|
|
|
5291
5290
|
if self.find_replace_dialog.cbFindInSelectedEvents.isChecked() and not len(self.find_replace_dialog.rowsToFind):
|
|
5292
|
-
dialog.MessageDialog(cfg.programName, "There are no selected events",
|
|
5291
|
+
dialog.MessageDialog(cfg.programName, "There are no selected events", (cfg.OK,))
|
|
5293
5292
|
return
|
|
5294
5293
|
|
|
5295
5294
|
fields_list: list = []
|
|
@@ -5297,7 +5296,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5297
5296
|
# check if find and replace contain valid behavior codes
|
|
5298
5297
|
for bh in (self.find_replace_dialog.findText.text(), self.find_replace_dialog.replaceText.text()):
|
|
5299
5298
|
if bh not in util.all_subjects(self.pj[cfg.SUBJECTS]):
|
|
5300
|
-
dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid subject name",
|
|
5299
|
+
dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid subject name", (cfg.OK,))
|
|
5301
5300
|
return
|
|
5302
5301
|
fields_list.append(cfg.PJ_OBS_FIELDS[self.playerType][cfg.SUBJECT])
|
|
5303
5302
|
|
|
@@ -5305,7 +5304,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5305
5304
|
# check if find and replace contain valid behavior codes
|
|
5306
5305
|
for bh in (self.find_replace_dialog.findText.text(), self.find_replace_dialog.replaceText.text()):
|
|
5307
5306
|
if bh not in util.all_behaviors(self.pj[cfg.ETHOGRAM]):
|
|
5308
|
-
dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid behavior code",
|
|
5307
|
+
dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid behavior code", (cfg.OK,))
|
|
5309
5308
|
return
|
|
5310
5309
|
fields_list.append(cfg.PJ_OBS_FIELDS[self.playerType][cfg.BEHAVIOR_CODE])
|
|
5311
5310
|
|
|
@@ -5403,7 +5402,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5403
5402
|
self.find_replace_dialog.close()
|
|
5404
5403
|
|
|
5405
5404
|
if msg == "FIND_REPLACE_ALL":
|
|
5406
|
-
dialog.MessageDialog(cfg.programName, f"{number_replacement} substitution(s).",
|
|
5405
|
+
dialog.MessageDialog(cfg.programName, f"{number_replacement} substitution(s).", (cfg.OK,))
|
|
5407
5406
|
self.find_replace_dialog.close()
|
|
5408
5407
|
|
|
5409
5408
|
def closeEvent(self, event):
|
|
@@ -5433,7 +5432,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5433
5432
|
dialog.MessageDialog(
|
|
5434
5433
|
cfg.programName,
|
|
5435
5434
|
"BORIS is doing some job. What do you want to do?",
|
|
5436
|
-
|
|
5435
|
+
("Wait", "Quit BORIS"),
|
|
5437
5436
|
)
|
|
5438
5437
|
== "Wait"
|
|
5439
5438
|
):
|
|
@@ -5452,7 +5451,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5452
5451
|
response = dialog.MessageDialog(
|
|
5453
5452
|
cfg.programName,
|
|
5454
5453
|
"What to do about the current unsaved project?",
|
|
5455
|
-
|
|
5454
|
+
(cfg.SAVE, cfg.DISCARD, cfg.CANCEL),
|
|
5456
5455
|
)
|
|
5457
5456
|
|
|
5458
5457
|
if response == cfg.SAVE:
|
|
@@ -5779,8 +5778,8 @@ def main():
|
|
|
5779
5778
|
None,
|
|
5780
5779
|
cfg.programName,
|
|
5781
5780
|
"FFmpeg is not available.<br>Go to http://www.ffmpeg.org to download it",
|
|
5782
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
5783
|
-
QMessageBox.NoButton,
|
|
5781
|
+
QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
|
|
5782
|
+
QMessageBox.StandardButton.NoButton,
|
|
5784
5783
|
)
|
|
5785
5784
|
sys.exit(3)
|
|
5786
5785
|
|
|
@@ -5833,7 +5832,7 @@ def main():
|
|
|
5833
5832
|
config_param: dict = {}
|
|
5834
5833
|
ini_file_path = Path.home() / Path(".boris")
|
|
5835
5834
|
if ini_file_path.is_file():
|
|
5836
|
-
settings = QSettings(str(ini_file_path), QSettings.IniFormat)
|
|
5835
|
+
settings = QSettings(str(ini_file_path), QSettings.Format.IniFormat)
|
|
5837
5836
|
try:
|
|
5838
5837
|
config_param = settings.value("config")
|
|
5839
5838
|
except Exception:
|
|
@@ -5866,8 +5865,8 @@ def main():
|
|
|
5866
5865
|
None,
|
|
5867
5866
|
cfg.programName,
|
|
5868
5867
|
("The mpv command is not available on the path"),
|
|
5869
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
5870
|
-
QMessageBox.NoButton,
|
|
5868
|
+
QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
|
|
5869
|
+
QMessageBox.StandardButton.NoButton,
|
|
5871
5870
|
)
|
|
5872
5871
|
sys.exit()
|
|
5873
5872
|
|
|
@@ -5876,8 +5875,8 @@ def main():
|
|
|
5876
5875
|
None,
|
|
5877
5876
|
cfg.programName,
|
|
5878
5877
|
("This version of BORIS for macOS is still EXPERIMENTAL and should be used at your own risk."),
|
|
5879
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
5880
|
-
QMessageBox.NoButton,
|
|
5878
|
+
QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
|
|
5879
|
+
QMessageBox.StandardButton.NoButton,
|
|
5881
5880
|
)
|
|
5882
5881
|
|
|
5883
5882
|
window.show()
|
|
@@ -5890,8 +5889,8 @@ def main():
|
|
|
5890
5889
|
None,
|
|
5891
5890
|
cfg.programName,
|
|
5892
5891
|
(f"Error opening observation: <b>{observation_to_open}</b><br>{r.split(':')[1]}"),
|
|
5893
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
5894
|
-
QMessageBox.NoButton,
|
|
5892
|
+
QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
|
|
5893
|
+
QMessageBox.StandardButton.NoButton,
|
|
5895
5894
|
)
|
|
5896
5895
|
|
|
5897
5896
|
if not options.nosplashscreen and (sys.platform != "darwin"):
|