boris-behav-obs 9.3.1__py2.py3-none-any.whl → 9.3.2__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/analysis_plugins/_latency.py +59 -0
- boris/behav_coding_map_creator.py +2 -6
- boris/core.py +25 -59
- boris/dialog.py +8 -3
- boris/gui_utilities.py +30 -4
- boris/modifier_coding_map_creator.py +6 -21
- boris/observation.py +25 -20
- boris/observation_operations.py +2 -2
- boris/player_dock_widget.py +7 -5
- boris/plugins.py +4 -0
- boris/utilities.py +35 -54
- boris/version.py +2 -2
- {boris_behav_obs-9.3.1.dist-info → boris_behav_obs-9.3.2.dist-info}/METADATA +1 -1
- {boris_behav_obs-9.3.1.dist-info → boris_behav_obs-9.3.2.dist-info}/RECORD +18 -18
- boris/1.py +0 -45
- {boris_behav_obs-9.3.1.dist-info → boris_behav_obs-9.3.2.dist-info}/WHEEL +0 -0
- {boris_behav_obs-9.3.1.dist-info → boris_behav_obs-9.3.2.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.3.1.dist-info → boris_behav_obs-9.3.2.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.3.1.dist-info → boris_behav_obs-9.3.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS plugin
|
|
3
|
+
|
|
4
|
+
number of occurences of behaviors
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
__version__ = "0.0.1"
|
|
10
|
+
__version_date__ = "2025-04-10"
|
|
11
|
+
__plugin_name__ = "Behavior latency"
|
|
12
|
+
__author__ = "Olivier Friard - University of Torino - Italy"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
import itertools
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def run(df: pd.DataFrame):
|
|
19
|
+
"""
|
|
20
|
+
Latency of a behavior after another.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
df["start_time"] = pd.to_datetime(df["Start (s)"])
|
|
24
|
+
df["end_time"] = pd.to_datetime(df["Stop (s)"])
|
|
25
|
+
|
|
26
|
+
latency_by_subject: dict = {}
|
|
27
|
+
|
|
28
|
+
for subject, group in df.groupby("subject"):
|
|
29
|
+
behaviors = group["behavior"].tolist()
|
|
30
|
+
# combinations = []
|
|
31
|
+
# Utiliser itertools pour créer des combinaisons 2 à 2 des comportements
|
|
32
|
+
for comb in itertools.combinations(behaviors, 2):
|
|
33
|
+
# combinations.append(comb)
|
|
34
|
+
|
|
35
|
+
last_A_end_time = None
|
|
36
|
+
|
|
37
|
+
# Liste pour stocker les latences de chaque sujet
|
|
38
|
+
subject_latency = []
|
|
39
|
+
|
|
40
|
+
for index, row in group.iterrows():
|
|
41
|
+
if row["behavior"] == comb[0]:
|
|
42
|
+
# Si on rencontre un comportement A, on réinitialise le temps de fin du comportement A
|
|
43
|
+
last_A_end_time = row["end_time"]
|
|
44
|
+
subject_latency.append(None) # Pas de latence pour A
|
|
45
|
+
elif row["behavior"] == comb[1] and last_A_end_time is not None:
|
|
46
|
+
# Si on rencontre un comportement B et qu'on a déjà vu un A avant
|
|
47
|
+
latency_time = row["start_time"] - last_A_end_time
|
|
48
|
+
subject_latency.append(latency_time)
|
|
49
|
+
else:
|
|
50
|
+
# Si on rencontre un B mais sans A avant
|
|
51
|
+
subject_latency.append(None)
|
|
52
|
+
|
|
53
|
+
# Ajout des latences calculées au DataFrame
|
|
54
|
+
df.loc[group.index, f"latency {comb[1]} after {comb[0]}"] = subject_latency
|
|
55
|
+
|
|
56
|
+
# Calcul de la latence totale ou moyenne par sujet
|
|
57
|
+
latency_by_subject[(subject, comb)] = df.groupby("subject")["latency"].agg(["sum", "mean"])
|
|
58
|
+
|
|
59
|
+
return str(latency_by_subject)
|
|
@@ -24,6 +24,7 @@ import binascii
|
|
|
24
24
|
import io
|
|
25
25
|
import json
|
|
26
26
|
from pathlib import Path
|
|
27
|
+
import gui_utilities
|
|
27
28
|
|
|
28
29
|
from PySide6.QtCore import QBuffer, QByteArray, QIODevice, QLineF, QPoint, Qt, Signal
|
|
29
30
|
from PySide6.QtGui import QBrush, QColor, QIcon, QMouseEvent, QPen, QPixmap, QPolygonF, QAction
|
|
@@ -790,7 +791,6 @@ class BehaviorsMapCreatorWindow(QMainWindow):
|
|
|
790
791
|
|
|
791
792
|
if not self.fileName:
|
|
792
793
|
return
|
|
793
|
-
"""if os.path.splitext(self.fileName)[1] != ".behav_coding_map":"""
|
|
794
794
|
if Path(self.fileName).suffix != ".behav_coding_map":
|
|
795
795
|
self.fileName += ".behav_coding_map"
|
|
796
796
|
self.saveMap()
|
|
@@ -1106,10 +1106,6 @@ if __name__ == "__main__":
|
|
|
1106
1106
|
app = QApplication(sys.argv)
|
|
1107
1107
|
window = BehaviorsMapCreatorWindow(["North zone", "East zone", "South zone", "West zone"])
|
|
1108
1108
|
window.bcm_list = []
|
|
1109
|
-
|
|
1110
|
-
screen_geometry = app.primaryScreen().geometry()
|
|
1111
|
-
center_x = (screen_geometry.width() - window.width()) // 2
|
|
1112
|
-
center_y = (screen_geometry.height() - window.height()) // 2
|
|
1113
|
-
window.move(center_x, center_y)
|
|
1109
|
+
gui_utilities.resize_center(app, window, cfg.CODING_MAP_RESIZE_W, cfg.CODING_MAP_RESIZE_H)
|
|
1114
1110
|
window.show()
|
|
1115
1111
|
sys.exit(app.exec())
|
boris/core.py
CHANGED
|
@@ -135,8 +135,9 @@ __version__ = version.__version__
|
|
|
135
135
|
__version_date__ = version.__version_date__
|
|
136
136
|
|
|
137
137
|
# check minimal version of python
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
MIN_PYTHON_VERSION = "3.12"
|
|
139
|
+
if util.versiontuple(platform.python_version()) < util.versiontuple(MIN_PYTHON_VERSION):
|
|
140
|
+
msg = f"BORIS requires Python {MIN_PYTHON_VERSION}+! You are using Python v. {platform.python_version()}\n"
|
|
140
141
|
logging.critical(msg)
|
|
141
142
|
sys.exit()
|
|
142
143
|
|
|
@@ -237,19 +238,6 @@ class TableModel(QAbstractTableModel):
|
|
|
237
238
|
return self._data[row][event_idx]
|
|
238
239
|
|
|
239
240
|
|
|
240
|
-
"""
|
|
241
|
-
class ButtonEventFilter(QObject):
|
|
242
|
-
def eventFilter(self, obj, event):
|
|
243
|
-
print("event filter")
|
|
244
|
-
if isinstance(obj, QPushButton) and event.type() == QEvent.KeyPress:
|
|
245
|
-
print("keypress")
|
|
246
|
-
if event.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Space):
|
|
247
|
-
print("enter sapce")
|
|
248
|
-
return False # Block the event
|
|
249
|
-
return super().eventFilter(obj, event)
|
|
250
|
-
"""
|
|
251
|
-
|
|
252
|
-
|
|
253
241
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
|
254
242
|
"""
|
|
255
243
|
Main BORIS window
|
|
@@ -301,7 +289,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
301
289
|
ext_data_timer_list: list = []
|
|
302
290
|
projectFileName: str = ""
|
|
303
291
|
mediaTotalLength = None
|
|
304
|
-
beep_every = 0
|
|
292
|
+
beep_every: float = 0.0
|
|
305
293
|
|
|
306
294
|
plot_colors = cfg.BEHAVIORS_PLOT_COLORS
|
|
307
295
|
behav_category_colors = cfg.CATEGORY_COLORS_LIST
|
|
@@ -315,19 +303,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
315
303
|
fast = 10
|
|
316
304
|
|
|
317
305
|
currentStates: dict = {}
|
|
318
|
-
subject_name_index = {}
|
|
306
|
+
subject_name_index: dict = {}
|
|
319
307
|
flag_slow = False
|
|
320
308
|
play_rate: float = 1
|
|
321
309
|
play_rate_step: float = 0.1
|
|
322
310
|
currentSubject: str = "" # contains the current subject of observation
|
|
323
|
-
coding_map_window_geometry = 0
|
|
324
311
|
|
|
325
312
|
# FFmpeg
|
|
326
|
-
memx
|
|
313
|
+
memx = -1
|
|
314
|
+
memy = -1
|
|
315
|
+
mem_player = -1
|
|
327
316
|
|
|
328
317
|
# path for ffmpeg/ffmpeg.exe program
|
|
329
|
-
ffmpeg_bin = ""
|
|
330
|
-
ffmpeg_cache_dir = ""
|
|
318
|
+
ffmpeg_bin: str = ""
|
|
319
|
+
ffmpeg_cache_dir: str = ""
|
|
331
320
|
|
|
332
321
|
# dictionary for FPS storing
|
|
333
322
|
fps = 0
|
|
@@ -390,9 +379,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
390
379
|
super(MainWindow, self).__init__(parent)
|
|
391
380
|
self.setupUi(self)
|
|
392
381
|
|
|
393
|
-
# disable trigger with RETURN or SPACE keys
|
|
394
|
-
"""filter_obj = ButtonEventFilter()
|
|
395
|
-
self.pb_live_obs.installEventFilter(filter_obj)"""
|
|
396
382
|
self.pb_live_obs.setFocusPolicy(Qt.NoFocus)
|
|
397
383
|
|
|
398
384
|
self.ffmpeg_bin = ffmpeg_bin
|
|
@@ -418,25 +404,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
418
404
|
self.tb_export.setMenu(self.menu)
|
|
419
405
|
"""
|
|
420
406
|
|
|
421
|
-
gui_utilities.set_icons(self, theme_mode=
|
|
407
|
+
gui_utilities.set_icons(self, theme_mode=gui_utilities.theme_mode())
|
|
422
408
|
|
|
423
409
|
self.setWindowTitle(f"{cfg.programName} ({__version__})")
|
|
424
410
|
|
|
425
|
-
self.w_obs_info.setVisible(False)
|
|
426
|
-
|
|
427
411
|
self.lbLogoBoris.setPixmap(QPixmap(":/logo"))
|
|
428
|
-
|
|
429
412
|
self.lbLogoBoris.setScaledContents(False)
|
|
430
413
|
self.lbLogoBoris.setAlignment(Qt.AlignCenter)
|
|
431
414
|
|
|
432
|
-
# self.lbLogoUnito.setPixmap(QPixmap(":/dbios_unito"))
|
|
433
|
-
# self.lbLogoUnito.setScaledContents(False)
|
|
434
|
-
# self.lbLogoUnito.setAlignment(Qt.AlignCenter)
|
|
435
|
-
|
|
436
415
|
self.toolBar.setEnabled(True)
|
|
437
416
|
|
|
438
417
|
# start with dock widget invisible
|
|
439
|
-
for w in
|
|
418
|
+
for w in (self.w_obs_info, self.dwEvents, self.dwEthogram, self.dwSubjects):
|
|
440
419
|
w.setVisible(False)
|
|
441
420
|
w.keyPressEvent = self.keyPressEvent
|
|
442
421
|
|
|
@@ -475,15 +454,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
475
454
|
self.lbTimeOffset.setMinimumWidth(160)
|
|
476
455
|
self.statusbar.addPermanentWidget(self.lbTimeOffset)
|
|
477
456
|
|
|
478
|
-
# play rate are now displayed in the main info widget
|
|
479
|
-
"""
|
|
480
|
-
# SPEED
|
|
481
|
-
self.lbSpeed = QLabel()
|
|
482
|
-
self.lbSpeed.setFrameStyle(QFrame.StyledPanel)
|
|
483
|
-
self.lbSpeed.setMinimumWidth(40)
|
|
484
|
-
self.statusbar.addPermanentWidget(self.lbSpeed)
|
|
485
|
-
"""
|
|
486
|
-
|
|
487
457
|
# set painter for tv_events to highlight current row
|
|
488
458
|
delegate = self.CustomItemDelegate()
|
|
489
459
|
self.tv_events.setItemDelegate(delegate)
|
|
@@ -499,14 +469,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
499
469
|
plugins.load_plugins(self)
|
|
500
470
|
plugins.add_plugins_to_menu(self)
|
|
501
471
|
|
|
502
|
-
def theme_mode(self):
|
|
503
|
-
"""
|
|
504
|
-
return the theme mode (dark or light) of the OS
|
|
505
|
-
"""
|
|
506
|
-
palette = QApplication.instance().palette()
|
|
507
|
-
color = palette.window().color()
|
|
508
|
-
return "dark" if color.value() < 128 else "light" # Dark mode if the color value is less than 128
|
|
509
|
-
|
|
510
472
|
class CustomItemDelegate(QStyledItemDelegate):
|
|
511
473
|
def paint(self, painter, option, index):
|
|
512
474
|
# Custom drawing logic here (overriding paint)
|
|
@@ -1506,7 +1468,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1506
1468
|
# one media
|
|
1507
1469
|
if self.dw_player[player].player.playlist_count == 1:
|
|
1508
1470
|
if new_time < self.dw_player[player].player.duration:
|
|
1509
|
-
|
|
1471
|
+
new_time_float = round(float(new_time), 3)
|
|
1472
|
+
|
|
1473
|
+
self.dw_player[player].player.seek(new_time_float, "absolute+exact")
|
|
1510
1474
|
|
|
1511
1475
|
if player == 0 and not self.user_move_slider:
|
|
1512
1476
|
self.video_slider.setValue(
|
|
@@ -3835,7 +3799,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
3835
3799
|
if self.geometric_measurements_mode:
|
|
3836
3800
|
geometric_measurement.redraw_measurements(self)
|
|
3837
3801
|
|
|
3838
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
3802
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
3839
3803
|
|
|
3840
3804
|
def previous_frame(self) -> None:
|
|
3841
3805
|
"""
|
|
@@ -3859,7 +3823,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
3859
3823
|
if self.geometric_measurements_mode:
|
|
3860
3824
|
geometric_measurement.redraw_measurements(self)
|
|
3861
3825
|
|
|
3862
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
3826
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
3863
3827
|
|
|
3864
3828
|
def run_event_outside(self):
|
|
3865
3829
|
"""
|
|
@@ -4353,8 +4317,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4353
4317
|
if not sys.platform.startswith(cfg.MACOS_CODE):
|
|
4354
4318
|
if self.dw_player[0].player.time_pos is not None:
|
|
4355
4319
|
for n_player in range(1, len(self.dw_player)):
|
|
4320
|
+
print(f"{n_player=}")
|
|
4321
|
+
|
|
4356
4322
|
ct = self.getLaps(n_player=n_player)
|
|
4357
4323
|
|
|
4324
|
+
print(f"{ct=}")
|
|
4325
|
+
|
|
4358
4326
|
# sync players 2..8 if time diff >= 1 s
|
|
4359
4327
|
if (
|
|
4360
4328
|
abs(ct0 - (ct + dec(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO][cfg.OFFSET][str(n_player + 1)])))
|
|
@@ -4463,7 +4431,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4463
4431
|
for data_timer in self.ext_data_timer_list:
|
|
4464
4432
|
data_timer.stop()
|
|
4465
4433
|
|
|
4466
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
4434
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
4467
4435
|
|
|
4468
4436
|
if msg:
|
|
4469
4437
|
self.lb_current_media_time.setText(msg)
|
|
@@ -5634,7 +5602,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5634
5602
|
for data_timer in self.ext_data_timer_list:
|
|
5635
5603
|
data_timer.start()
|
|
5636
5604
|
|
|
5637
|
-
self.actionPlay.setIcon(QIcon(f":/pause_{
|
|
5605
|
+
self.actionPlay.setIcon(QIcon(f":/pause_{gui_utilities.theme_mode()}"))
|
|
5638
5606
|
self.actionPlay.setText("Pause")
|
|
5639
5607
|
|
|
5640
5608
|
return True
|
|
@@ -5668,7 +5636,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5668
5636
|
for idx in self.plot_data:
|
|
5669
5637
|
self.timer_plot_data_out(self.plot_data[idx])
|
|
5670
5638
|
|
|
5671
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
5639
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
5672
5640
|
self.actionPlay.setText("Play")
|
|
5673
5641
|
|
|
5674
5642
|
def play_activated(self):
|
|
@@ -5823,8 +5791,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5823
5791
|
|
|
5824
5792
|
|
|
5825
5793
|
def main():
|
|
5826
|
-
# QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
|
5827
|
-
|
|
5828
5794
|
app = QApplication(sys.argv)
|
|
5829
5795
|
app.setStyle("Fusion")
|
|
5830
5796
|
|
|
@@ -5917,7 +5883,7 @@ def main():
|
|
|
5917
5883
|
results.show()
|
|
5918
5884
|
|
|
5919
5885
|
window.show()
|
|
5920
|
-
window.raise_()
|
|
5886
|
+
window.raise_() # for overlapping widget (?)
|
|
5921
5887
|
|
|
5922
5888
|
if observation_to_open and "error" not in pj:
|
|
5923
5889
|
r = observation_operations.load_observation(window, obs_id=observation_to_open, mode=cfg.OBS_START)
|
boris/dialog.py
CHANGED
|
@@ -71,7 +71,12 @@ from . import utilities as util
|
|
|
71
71
|
|
|
72
72
|
def MessageDialog(title: str, text: str, buttons: tuple) -> str:
|
|
73
73
|
"""
|
|
74
|
-
generic message dialog
|
|
74
|
+
show a generic message dialog and returns the text of the clicked button
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
title (str): Title of the dialog box
|
|
78
|
+
text (str): text of the dialog box
|
|
79
|
+
buttons (tuple): text for buttons
|
|
75
80
|
|
|
76
81
|
Return
|
|
77
82
|
str: text of the clicked button
|
|
@@ -83,8 +88,8 @@ def MessageDialog(title: str, text: str, buttons: tuple) -> str:
|
|
|
83
88
|
for button in buttons:
|
|
84
89
|
message.addButton(button, QMessageBox.YesRole)
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
message.
|
|
91
|
+
message.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
92
|
+
message.exec()
|
|
88
93
|
return message.clickedButton().text()
|
|
89
94
|
|
|
90
95
|
|
boris/gui_utilities.py
CHANGED
|
@@ -22,10 +22,19 @@ Copyright 2012-2025 Olivier Friard
|
|
|
22
22
|
import pathlib as pl
|
|
23
23
|
import logging
|
|
24
24
|
from PySide6.QtCore import QSettings
|
|
25
|
-
from PySide6.QtWidgets import QWidget
|
|
25
|
+
from PySide6.QtWidgets import QWidget, QApplication
|
|
26
26
|
from PySide6.QtGui import QIcon
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def theme_mode() -> str:
|
|
30
|
+
"""
|
|
31
|
+
return the theme mode (dark or light) of the OS
|
|
32
|
+
"""
|
|
33
|
+
palette = QApplication.instance().palette()
|
|
34
|
+
color = palette.window().color()
|
|
35
|
+
return "dark" if color.value() < 128 else "light" # Dark mode if the color value is less than 128
|
|
36
|
+
|
|
37
|
+
|
|
29
38
|
def save_geometry(widget: QWidget, widget_name: str):
|
|
30
39
|
"""
|
|
31
40
|
save window geometry in ini file
|
|
@@ -44,6 +53,7 @@ def restore_geometry(widget: QWidget, widget_name: str, default_width_height):
|
|
|
44
53
|
"""
|
|
45
54
|
restore window geometry in ini file
|
|
46
55
|
"""
|
|
56
|
+
|
|
47
57
|
def default_resize(widget, default_width_height):
|
|
48
58
|
if default_width_height != (0, 0):
|
|
49
59
|
try:
|
|
@@ -51,15 +61,14 @@ def restore_geometry(widget: QWidget, widget_name: str, default_width_height):
|
|
|
51
61
|
except Exception:
|
|
52
62
|
logging.warning("Error during restoring default")
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
logging.debug(f'restore geometry function for {widget_name}')
|
|
64
|
+
logging.debug(f"restore geometry function for {widget_name}")
|
|
56
65
|
try:
|
|
57
66
|
ini_file_path = pl.Path.home() / pl.Path(".boris")
|
|
58
67
|
if ini_file_path.is_file():
|
|
59
68
|
settings = QSettings(str(ini_file_path), QSettings.IniFormat)
|
|
60
69
|
print(settings.value(f"{widget_name} geometry"))
|
|
61
70
|
widget.restoreGeometry(settings.value(f"{widget_name} geometry"))
|
|
62
|
-
logging.debug(f
|
|
71
|
+
logging.debug(f"geometry restored for {widget_name} {settings.value(f'{widget_name} geometry')}")
|
|
63
72
|
else:
|
|
64
73
|
default_resize(widget, default_width_height)
|
|
65
74
|
except Exception:
|
|
@@ -108,3 +117,20 @@ def set_icons(self, theme_mode: str) -> None:
|
|
|
108
117
|
self.action_geometric_measurements.setIcon(QIcon(f":/measurement_{theme_mode}"))
|
|
109
118
|
self.actionFind_in_current_obs.setIcon(QIcon(f":/find_{theme_mode}"))
|
|
110
119
|
self.actionExplore_project.setIcon(QIcon(f":/explore_{theme_mode}"))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def resize_center(app, window, width: int, height: int) -> None:
|
|
123
|
+
"""
|
|
124
|
+
resize and center window
|
|
125
|
+
"""
|
|
126
|
+
window.resize(width, height)
|
|
127
|
+
screen_geometry = app.primaryScreen().geometry()
|
|
128
|
+
if window.height() > screen_geometry.height():
|
|
129
|
+
window.resize(window.width(), int(screen_geometry.height() * 0.8))
|
|
130
|
+
if window.width() > screen_geometry.width():
|
|
131
|
+
window.resize(screen_geometry.width(), window.height())
|
|
132
|
+
# center
|
|
133
|
+
center_x = (screen_geometry.width() - window.width()) // 2
|
|
134
|
+
center_y = (screen_geometry.height() - window.height()) // 2
|
|
135
|
+
|
|
136
|
+
window.move(center_x, center_y)
|
|
@@ -26,8 +26,9 @@ This file is part of BORIS.
|
|
|
26
26
|
import binascii
|
|
27
27
|
import io
|
|
28
28
|
import json
|
|
29
|
-
import
|
|
29
|
+
from pathlib import Path
|
|
30
30
|
import re
|
|
31
|
+
import gui_utilities
|
|
31
32
|
|
|
32
33
|
from PySide6.QtCore import (
|
|
33
34
|
Qt,
|
|
@@ -727,7 +728,8 @@ class ModifiersMapCreatorWindow(QMainWindow):
|
|
|
727
728
|
self.fileName = fn
|
|
728
729
|
|
|
729
730
|
if self.fileName:
|
|
730
|
-
if os.path.splitext(self.fileName)[1] != ".boris_map":
|
|
731
|
+
# if os.path.splitext(self.fileName)[1] != ".boris_map":
|
|
732
|
+
if Path(self.fileName).suffix != ".boris_map":
|
|
731
733
|
self.fileName += ".boris_map"
|
|
732
734
|
self.saveMap()
|
|
733
735
|
|
|
@@ -744,7 +746,7 @@ class ModifiersMapCreatorWindow(QMainWindow):
|
|
|
744
746
|
else:
|
|
745
747
|
self.fileName = fn
|
|
746
748
|
|
|
747
|
-
if self.fileName and
|
|
749
|
+
if self.fileName and Path(self.fileName).suffix() != ".boris_map":
|
|
748
750
|
self.fileName += ".boris_map"
|
|
749
751
|
|
|
750
752
|
if self.fileName:
|
|
@@ -1006,25 +1008,8 @@ if __name__ == "__main__":
|
|
|
1006
1008
|
|
|
1007
1009
|
app = QApplication(sys.argv)
|
|
1008
1010
|
window = ModifiersMapCreatorWindow()
|
|
1009
|
-
window.resize(800, 700)
|
|
1010
1011
|
|
|
1011
|
-
|
|
1012
|
-
print(f"{window.height()=}")
|
|
1013
|
-
|
|
1014
|
-
# Get the screen geometry (screen size and position)
|
|
1015
|
-
screen_geometry = app.primaryScreen().geometry()
|
|
1016
|
-
|
|
1017
|
-
print(f"{screen_geometry=}")
|
|
1018
|
-
|
|
1019
|
-
# Calculate the center of the screen
|
|
1020
|
-
center_x = (screen_geometry.width() - window.width()) // 2
|
|
1021
|
-
center_y = (screen_geometry.height() - window.height()) // 2
|
|
1022
|
-
|
|
1023
|
-
print(f"{center_x=}")
|
|
1024
|
-
print(f"{center_y=}")
|
|
1025
|
-
|
|
1026
|
-
# Move the widget to the center of the screen
|
|
1027
|
-
window.move(center_x, center_y)
|
|
1012
|
+
gui_utilities.resize_center(app, window, cfg.CODING_MAP_RESIZE_W, cfg.CODING_MAP_RESIZE_H)
|
|
1028
1013
|
|
|
1029
1014
|
window.show()
|
|
1030
1015
|
sys.exit(app.exec())
|
boris/observation.py
CHANGED
|
@@ -1008,27 +1008,30 @@ class Observation(QDialog, Ui_Form):
|
|
|
1008
1008
|
|
|
1009
1009
|
# check if observation id not empty
|
|
1010
1010
|
if not self.leObservationId.text():
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1011
|
+
QMessageBox.critical(
|
|
1012
|
+
self,
|
|
1013
|
+
cfg.programName,
|
|
1014
|
+
"The <b>observation id</b> is mandatory and must be unique.",
|
|
1015
|
+
)
|
|
1015
1016
|
return False
|
|
1016
1017
|
|
|
1017
1018
|
# check if observation_type
|
|
1018
1019
|
if not any((self.rb_media_files.isChecked(), self.rb_live.isChecked(), self.rb_images.isChecked())):
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1020
|
+
QMessageBox.critical(
|
|
1021
|
+
self,
|
|
1022
|
+
cfg.programName,
|
|
1023
|
+
"Choose an observation type.",
|
|
1024
|
+
)
|
|
1023
1025
|
return False
|
|
1024
1026
|
|
|
1025
1027
|
# check if offset is correct
|
|
1026
1028
|
if self.cb_time_offset.isChecked():
|
|
1027
1029
|
if self.obs_time_offset.get_time() is None:
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1030
|
+
QMessageBox.critical(
|
|
1031
|
+
self,
|
|
1032
|
+
cfg.programName,
|
|
1033
|
+
"Check the time offset value.",
|
|
1034
|
+
)
|
|
1032
1035
|
return False
|
|
1033
1036
|
|
|
1034
1037
|
if self.rb_media_files.isChecked(): # observation based on media file(s)
|
|
@@ -1051,18 +1054,20 @@ class Observation(QDialog, Ui_Form):
|
|
|
1051
1054
|
|
|
1052
1055
|
# check if player #1 is used
|
|
1053
1056
|
if not players_list or min(players_list) > 1:
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1057
|
+
QMessageBox.critical(
|
|
1058
|
+
self,
|
|
1059
|
+
cfg.programName,
|
|
1060
|
+
"A media file must be loaded in player #1",
|
|
1061
|
+
)
|
|
1058
1062
|
return False
|
|
1059
1063
|
|
|
1060
1064
|
# check if players are used in crescent order
|
|
1061
1065
|
if set(list(range(min(players_list), max(players_list) + 1))) != set(players_list):
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
+
QMessageBox.critical(
|
|
1067
|
+
self,
|
|
1068
|
+
cfg.programName,
|
|
1069
|
+
"Some player are not used. Please reorganize your media files",
|
|
1070
|
+
)
|
|
1066
1071
|
return False
|
|
1067
1072
|
|
|
1068
1073
|
# check if more media in player #1 and media in other players
|
boris/observation_operations.py
CHANGED
|
@@ -888,7 +888,7 @@ def new_observation(self, mode: str = cfg.NEW, obsId: str = "") -> None:
|
|
|
888
888
|
self.pj[cfg.OBSERVATIONS][obsId][cfg.CLOSE_BEHAVIORS_BETWEEN_VIDEOS]
|
|
889
889
|
)
|
|
890
890
|
|
|
891
|
-
rv = observationWindow.
|
|
891
|
+
rv = observationWindow.exec()
|
|
892
892
|
|
|
893
893
|
# save geometry
|
|
894
894
|
gui_utilities.save_geometry(observationWindow, "new observation")
|
|
@@ -2012,7 +2012,7 @@ def initialize_new_media_observation(self) -> bool:
|
|
|
2012
2012
|
self.mpv_eof_reached_signal.connect(self.mpv_eof_reached)
|
|
2013
2013
|
self.video_click_signal.connect(self.player_clicked)
|
|
2014
2014
|
|
|
2015
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
2015
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
2016
2016
|
|
|
2017
2017
|
self.display_statusbar_info(self.observationId)
|
|
2018
2018
|
|
boris/player_dock_widget.py
CHANGED
|
@@ -23,8 +23,12 @@ This file is part of BORIS.
|
|
|
23
23
|
import sys
|
|
24
24
|
import logging
|
|
25
25
|
import functools
|
|
26
|
+
from . import mpv2 as mpv
|
|
27
|
+
import config as cfg
|
|
28
|
+
import gui_utilities
|
|
29
|
+
|
|
30
|
+
|
|
26
31
|
from PySide6.QtWidgets import (
|
|
27
|
-
QApplication,
|
|
28
32
|
QLabel,
|
|
29
33
|
QDockWidget,
|
|
30
34
|
QWidget,
|
|
@@ -38,8 +42,6 @@ from PySide6.QtWidgets import (
|
|
|
38
42
|
from PySide6.QtCore import Signal, QEvent, Qt
|
|
39
43
|
from PySide6.QtGui import QIcon, QAction
|
|
40
44
|
|
|
41
|
-
from . import mpv2 as mpv
|
|
42
|
-
import config as cfg
|
|
43
45
|
|
|
44
46
|
"""
|
|
45
47
|
try:
|
|
@@ -153,7 +155,7 @@ class DW_player(QDockWidget):
|
|
|
153
155
|
self.mute_button.setAutoRaise(True)
|
|
154
156
|
self.mute_action = QAction()
|
|
155
157
|
|
|
156
|
-
theme_mode =
|
|
158
|
+
theme_mode = gui_utilities.theme_mode()
|
|
157
159
|
|
|
158
160
|
self.mute_action.setIcon(QIcon(f":/volume_xmark_{theme_mode}"))
|
|
159
161
|
self.mute_action.triggered.connect(self.mute_action_triggered)
|
|
@@ -194,7 +196,7 @@ class DW_player(QDockWidget):
|
|
|
194
196
|
"""
|
|
195
197
|
emit signal when mute action is triggered
|
|
196
198
|
"""
|
|
197
|
-
theme_mode =
|
|
199
|
+
theme_mode = gui_utilities.theme_mode()
|
|
198
200
|
if self.player.mute:
|
|
199
201
|
self.mute_action.setIcon(QIcon(f":/volume_xmark_{theme_mode}"))
|
|
200
202
|
else:
|
boris/plugins.py
CHANGED
|
@@ -82,6 +82,8 @@ def load_plugins(self):
|
|
|
82
82
|
for file_ in sorted((Path(__file__).parent / "analysis_plugins").glob("*.py")):
|
|
83
83
|
if file_.name == "__init__.py":
|
|
84
84
|
continue
|
|
85
|
+
if file_.name.startswith("_"):
|
|
86
|
+
continue
|
|
85
87
|
plugin_name = get_plugin_name(file_)
|
|
86
88
|
if plugin_name is not None and plugin_name not in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
|
|
87
89
|
# check if plugin with same name already loaded
|
|
@@ -96,6 +98,8 @@ def load_plugins(self):
|
|
|
96
98
|
for file_ in sorted(Path(self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, "")).glob("*.py")):
|
|
97
99
|
if file_.name == "__init__.py":
|
|
98
100
|
continue
|
|
101
|
+
if file_.name.startswith("_"):
|
|
102
|
+
continue
|
|
99
103
|
plugin_name = get_plugin_name(file_)
|
|
100
104
|
if plugin_name is not None and plugin_name not in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
|
|
101
105
|
# check if plugin with same name already loaded
|
boris/utilities.py
CHANGED
|
@@ -28,7 +28,6 @@ import math
|
|
|
28
28
|
import os
|
|
29
29
|
import pathlib as pl
|
|
30
30
|
import re
|
|
31
|
-
import socket
|
|
32
31
|
import subprocess
|
|
33
32
|
import sys
|
|
34
33
|
import urllib.parse
|
|
@@ -292,27 +291,6 @@ def return_file_header_footer(file_name: str, file_row_number: int = 0, row_numb
|
|
|
292
291
|
return header, footer
|
|
293
292
|
|
|
294
293
|
|
|
295
|
-
def bytes_to_str(b: bytes) -> str:
|
|
296
|
-
"""
|
|
297
|
-
Translate bytes to string.
|
|
298
|
-
|
|
299
|
-
Args:
|
|
300
|
-
b (bytes): byte to convert
|
|
301
|
-
|
|
302
|
-
Returns:
|
|
303
|
-
str: converted byte
|
|
304
|
-
"""
|
|
305
|
-
|
|
306
|
-
if isinstance(b, bytes):
|
|
307
|
-
fileSystemEncoding = sys.getfilesystemencoding()
|
|
308
|
-
# hack for PyInstaller
|
|
309
|
-
if fileSystemEncoding is None:
|
|
310
|
-
fileSystemEncoding = "UTF-8"
|
|
311
|
-
return b.decode(fileSystemEncoding)
|
|
312
|
-
else:
|
|
313
|
-
return b
|
|
314
|
-
|
|
315
|
-
|
|
316
294
|
def convertTime(time_format: str, sec: Union[float, dec]) -> Union[str, None]:
|
|
317
295
|
"""
|
|
318
296
|
convert time in base at the current format (S or HHMMSS)
|
|
@@ -381,20 +359,6 @@ def count_media_file(media_files: dict) -> int:
|
|
|
381
359
|
return sum([len(media_files[idx]) for idx in media_files])
|
|
382
360
|
|
|
383
361
|
|
|
384
|
-
def file_content_md5(file_name: str) -> str:
|
|
385
|
-
"""
|
|
386
|
-
returns the MD5 sum of file content
|
|
387
|
-
"""
|
|
388
|
-
hash_md5 = hashlib.md5()
|
|
389
|
-
try:
|
|
390
|
-
with open(file_name, "rb") as f:
|
|
391
|
-
for chunk in iter(lambda: f.read(4096), b""):
|
|
392
|
-
hash_md5.update(chunk)
|
|
393
|
-
return hash_md5.hexdigest()
|
|
394
|
-
except FileNotFoundError:
|
|
395
|
-
return ""
|
|
396
|
-
|
|
397
|
-
|
|
398
362
|
def txt2np_array(
|
|
399
363
|
file_name: str, columns_str: str, substract_first_value: str, converters=None, column_converter=None
|
|
400
364
|
) -> Tuple[bool, str, np.array]:
|
|
@@ -760,19 +724,6 @@ def get_current_points_by_subject(
|
|
|
760
724
|
return current_points
|
|
761
725
|
|
|
762
726
|
|
|
763
|
-
def get_ip_address():
|
|
764
|
-
"""Get current IP address
|
|
765
|
-
|
|
766
|
-
Args:
|
|
767
|
-
|
|
768
|
-
Returns:
|
|
769
|
-
str: IP address
|
|
770
|
-
"""
|
|
771
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
772
|
-
s.connect(("8.8.8.8", 80))
|
|
773
|
-
return s.getsockname()[0]
|
|
774
|
-
|
|
775
|
-
|
|
776
727
|
def check_txt_file(file_name: str) -> dict:
|
|
777
728
|
"""
|
|
778
729
|
Extract parameters of txt file (test for tsv csv)
|
|
@@ -950,7 +901,7 @@ def intfloatstr(s: str) -> int:
|
|
|
950
901
|
return s
|
|
951
902
|
|
|
952
903
|
|
|
953
|
-
def distance(p1, p2):
|
|
904
|
+
def distance(p1: tuple, p2: tuple) -> float:
|
|
954
905
|
"""
|
|
955
906
|
euclidean distance between 2 points
|
|
956
907
|
"""
|
|
@@ -987,12 +938,12 @@ def oriented_angle(P1: tuple, P2: tuple, P3: tuple) -> float:
|
|
|
987
938
|
Calculate the oriented angle between two segments.
|
|
988
939
|
|
|
989
940
|
Args:
|
|
990
|
-
P1: Coordinates of the vertex
|
|
991
|
-
P2: Coordinates of the first point
|
|
992
|
-
P3: Coordinates of the second point
|
|
941
|
+
P1 (tuple): Coordinates of the vertex
|
|
942
|
+
P2 (tuple): Coordinates of the first point
|
|
943
|
+
P3 (tuple): Coordinates of the second point
|
|
993
944
|
|
|
994
945
|
Returns:
|
|
995
|
-
The oriented angle between the two segments in degrees.
|
|
946
|
+
float: The oriented angle between the two segments in degrees.
|
|
996
947
|
"""
|
|
997
948
|
|
|
998
949
|
x1, y1 = P1
|
|
@@ -1008,6 +959,36 @@ def oriented_angle(P1: tuple, P2: tuple, P3: tuple) -> float:
|
|
|
1008
959
|
return oriented_angle
|
|
1009
960
|
|
|
1010
961
|
|
|
962
|
+
def oriented_angle_trigo(B: Tuple[float, float], A: Tuple[float, float], C: Tuple[float, float]) -> float:
|
|
963
|
+
"""
|
|
964
|
+
Calculates the oriented angle between vectors BA and BC, in degrees.
|
|
965
|
+
The angle is positive in the counter-clockwise (trigonometric) direction.
|
|
966
|
+
|
|
967
|
+
Parameters:
|
|
968
|
+
B: The pivot point (the origin of the vectors BA and BC).
|
|
969
|
+
A, C: Points that define the vectors.
|
|
970
|
+
|
|
971
|
+
Returns:
|
|
972
|
+
Angle in degrees, between 0 and 360.
|
|
973
|
+
"""
|
|
974
|
+
# Vectors BA and BC
|
|
975
|
+
v1 = (A[0] - B[0], A[1] - B[1])
|
|
976
|
+
v2 = (C[0] - B[0], C[1] - B[1])
|
|
977
|
+
|
|
978
|
+
# Dot product and 2D cross product (determinant)
|
|
979
|
+
dot = v1[0] * v2[0] + v1[1] * v2[1]
|
|
980
|
+
det = v1[0] * v2[1] - v1[1] * v2[0]
|
|
981
|
+
|
|
982
|
+
# Signed angle in radians, then converted to degrees
|
|
983
|
+
angle_rad = math.atan2(det, dot)
|
|
984
|
+
angle_deg = math.degrees(angle_rad)
|
|
985
|
+
|
|
986
|
+
if angle_deg < 0:
|
|
987
|
+
angle_deg += 360
|
|
988
|
+
|
|
989
|
+
return angle_deg
|
|
990
|
+
|
|
991
|
+
|
|
1011
992
|
def mem_info():
|
|
1012
993
|
"""
|
|
1013
994
|
get info about total mem, used mem and available mem using:
|
boris/version.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
boris/1.py,sha256=rb6Nstw1vIHlBwMnzExVOlgU2F75Tuf1VYRXBTrZUFg,1082
|
|
2
1
|
boris/__init__.py,sha256=iAtmVMy22TJpMmxVTMSK_6-wXnCbx1ogvWgfYEcbHzU,773
|
|
3
2
|
boris/__main__.py,sha256=ANjTbXgXDoz2nB1tCtOIllfIVotCa602iebACX7rXaE,764
|
|
4
3
|
boris/about.py,sha256=KEUz6nryrg8FceVyFsf8sMz-xWd2cGwIUfuVydHxqC4,5366
|
|
5
4
|
boris/add_modifier.py,sha256=DWqxkKDBm21QH_kPvhpnltwLtFvPxne0VmZ1SY26hj8,26340
|
|
6
5
|
boris/add_modifier_ui.py,sha256=Y7TLO5uS6zW7zpjXmjA4V_VIp_bFDNtjOTbJ9Q6m-mQ,11601
|
|
7
6
|
boris/advanced_event_filtering.py,sha256=VlvU12mL6xYacZOvJAi5uLpHMcmAw5Pvuvmka-PN29c,15469
|
|
8
|
-
boris/behav_coding_map_creator.py,sha256=
|
|
7
|
+
boris/behav_coding_map_creator.py,sha256=_WmfWTYkKh_a7pZa49h2GtORCi6h8joZTWihud6YDBE,38826
|
|
9
8
|
boris/behavior_binary_table.py,sha256=bpmRDpEjq0rw3YOCoN_He3kfUe8A_R6E48kQR7KnkH8,12453
|
|
10
9
|
boris/behaviors_coding_map.py,sha256=xIGJxp2eghrpiGDmYH73eJPERuyc4A_54uT-Got3zTs,7302
|
|
11
10
|
boris/boris_cli.py,sha256=n0OiVvZM1gM6E7yKaff9wlgmpAGK4TK052VRi8AabJo,13196
|
|
@@ -17,12 +16,12 @@ boris/connections.py,sha256=rVI18AuXh8cEnnoCKJk0RMWAaiNOpiaS554Okgk3SBY,19383
|
|
|
17
16
|
boris/converters.py,sha256=c1Jps-URoglY5ILQHz-pCCf6-4DFUHZLtqr_ofsrFg0,11722
|
|
18
17
|
boris/converters_ui.py,sha256=uu7LOBV_fKv2DBdOqsqPwjGsjgONr5ODBoscAA-EP48,9900
|
|
19
18
|
boris/cooccurence.py,sha256=tVERC-V8MWjWHlGEfDuu08iS94qjt4do-38jwI62QaY,10367
|
|
20
|
-
boris/core.py,sha256=
|
|
19
|
+
boris/core.py,sha256=qO8ZQoPxr_jjdy7-oihjcPcNvWgHvs-IWd7JWmxy_M0,233608
|
|
21
20
|
boris/core_qrc.py,sha256=T3ki5e2Pj0I0QBGz63MPUgZzl8F_VHZwSq074mRNBDU,650669
|
|
22
21
|
boris/core_ui.py,sha256=SeC26uveDCjrCBLsRPuQ6FaapKfON_HIRcQStEDLhl4,76384
|
|
23
22
|
boris/db_functions.py,sha256=Uw9wWH_Pe-qNzpV1k21YG_jKsoOmfY_iiK_7ARZHGDc,13352
|
|
24
23
|
boris/dev.py,sha256=9pUElbjl9g17rFUJXX5aVSu55_iIKIuDxNdrB0DI_d0,3671
|
|
25
|
-
boris/dialog.py,sha256=
|
|
24
|
+
boris/dialog.py,sha256=0mWbOQu9QvkCr6AIa1Tue5EYaTxbHo7rm-tnGAqQ3nk,33047
|
|
26
25
|
boris/duration_widget.py,sha256=GjZgCAMGOcsNjoPiRImEVe6yMkH2vuNoh44ulpd5nlg,6924
|
|
27
26
|
boris/edit_event.py,sha256=2hpxn9DYuJW1CK02hF4iU0--J_0C_KTiN9gGZ1frJBc,7678
|
|
28
27
|
boris/edit_event_ui.py,sha256=vhglQrhkF9tt0HVlkXnkG7symW0eOFA7nhbk6l_qn3k,7772
|
|
@@ -34,7 +33,7 @@ boris/export_events.py,sha256=3B336WEA0g_8oW3VDo_kfq5D0ISu-e7z2f-_ROUvU9c,39756
|
|
|
34
33
|
boris/export_observation.py,sha256=SvKhuGa-Ag_kK3igL9DFdJ0TKoQLDneu54R_uiSHUyo,50813
|
|
35
34
|
boris/external_processes.py,sha256=vpmhA4Lj2GblBIrDD0YjesB8HPOgx4K9gSWVhTop4Cg,11927
|
|
36
35
|
boris/geometric_measurement.py,sha256=4pI-AYpBSFlJBqS-f8dnkgLtj_Z2E5kwwAdh6WwZ4kk,35049
|
|
37
|
-
boris/gui_utilities.py,sha256=
|
|
36
|
+
boris/gui_utilities.py,sha256=2HdWFxo2y0oxC29VJAA3R-TOMxVbOy3FuVwspjrTD6A,5519
|
|
38
37
|
boris/image_overlay.py,sha256=zZAL8MTt2i2s58CuX81Nym3rJ5pKiTeP4AO8WbIUonM,2527
|
|
39
38
|
boris/import_observations.py,sha256=hwEPIag741AXTFIuxDdZLDvLrsmvaqTkjyTjQu5M_RA,8798
|
|
40
39
|
boris/irr.py,sha256=o5QN3B2b-02AUkrklMJCitFGsfiUDtmI0MxUbPv2cBg,22472
|
|
@@ -42,25 +41,25 @@ boris/latency.py,sha256=48z9L_A582-wKCfD0M3h0uyYkeL2ezjlQAS_GzeoOe0,9739
|
|
|
42
41
|
boris/measurement_widget.py,sha256=lZV62KtK6TjdoNbKxj3uyNAuL5dfnQnn7mYwzMo-dOM,4480
|
|
43
42
|
boris/media_file.py,sha256=QzUC0mT905SzlONvcXUJB2OCxhj8kJ0h0W6PN1ssSIY,4722
|
|
44
43
|
boris/menu_options.py,sha256=UEB3GxRh6YKNCg67qbhOVhJW1ZOznuPe15bADc_CNTI,7062
|
|
45
|
-
boris/modifier_coding_map_creator.py,sha256=
|
|
44
|
+
boris/modifier_coding_map_creator.py,sha256=NQHy_txgxKZnGByXiro_Oy_cq4DrFaFiAYwVp1CWrTs,33281
|
|
46
45
|
boris/modifiers_coding_map.py,sha256=oT56ZY_PXhEJsMoblEsyNMAPbDpv7ZMOCnvmt7Ibx_Y,4554
|
|
47
46
|
boris/mpv-1.0.3.py,sha256=EXRtzQqFjOn4wMC6482Ilq3fNQ9N1GRP1VxwLzdeaBY,88077
|
|
48
47
|
boris/mpv.py,sha256=EfzIHjPbgewG4w3smEtqEUPZoVwYmMQkL4Q8ZyW-a58,76410
|
|
49
48
|
boris/mpv2.py,sha256=IUI4t4r9GYX7G5OXTjd3RhMMOkDdfal_15buBgksLsk,92152
|
|
50
|
-
boris/observation.py,sha256=
|
|
51
|
-
boris/observation_operations.py,sha256=
|
|
49
|
+
boris/observation.py,sha256=d-7q-RkMHuLDV87nF4yahvDFPYhlXp6GmE80vckn5zU,57073
|
|
50
|
+
boris/observation_operations.py,sha256=hYtwMk3dx9O1UURbxPUewlyL111jDThO5_b3CnNmI1o,105756
|
|
52
51
|
boris/observation_ui.py,sha256=DAnU94QBNvkLuHT6AxTwqSk_D_n6VUhSl8PexZv_dUk,33309
|
|
53
52
|
boris/observations_list.py,sha256=NqwECGHtHYmKhSe-qCfqPmJ24SSfzlXvIXS2i3op_zE,10591
|
|
54
53
|
boris/otx_parser.py,sha256=70QvilzFHXbjAHR88YH0aEXJ3xxheLS5fZGgHFHGpNE,16367
|
|
55
54
|
boris/param_panel.py,sha256=G0XzNmJIX89-n2OQTDccuY_wWMhr3p7GB4ZorbU6EWc,8786
|
|
56
55
|
boris/param_panel_ui.py,sha256=4emQDFmuL4_R7bKxosLjdUb-VSPWkDm7suy38F5EKcA,13260
|
|
57
|
-
boris/player_dock_widget.py,sha256=
|
|
56
|
+
boris/player_dock_widget.py,sha256=cJ2UB6qfdxrk22nLWgOzs5EomCfR7OcznJq67UjF8dg,6186
|
|
58
57
|
boris/plot_data_module.py,sha256=6QbLKfyGp4TYRyHnB9G45y5XrpeXLytcorltEAWfYak,16562
|
|
59
58
|
boris/plot_events.py,sha256=CF6gnsTeaPG-P1USwh4An2s31NoMJ1roHDImcQrQj3c,24060
|
|
60
59
|
boris/plot_events_rt.py,sha256=xig__Uea3mQqO5raMBVB3pm3vuQkjAbJpwSS7AwIob8,8327
|
|
61
60
|
boris/plot_spectrogram_rt.py,sha256=JV8N7T8133wGVhlPxmgOb426u1g1p21-LbTqgaeddkk,8361
|
|
62
61
|
boris/plot_waveform_rt.py,sha256=05JN_6HCq674ROore_6PNw93GQNZJQDlDxp2ODAFkkA,7474
|
|
63
|
-
boris/plugins.py,sha256=
|
|
62
|
+
boris/plugins.py,sha256=CCS1I44OMkGZqcfLGKNPGfEQXPgngocy5YhWveXQPKM,10029
|
|
64
63
|
boris/preferences.py,sha256=qPfd9Tyg7u30kXwVqMOgkdy2RXri9bItRa5U2-ZVQmg,16847
|
|
65
64
|
boris/preferences_ui.py,sha256=D2bFLb3E0m6IwSeqKoItRDiqvPmJGoeXLHF2K02n1Zo,26293
|
|
66
65
|
boris/project.py,sha256=hAeAb5pD0u_l0bezU9ePvbTOYQKfxrFGvYB2NAqSDHg,84377
|
|
@@ -78,8 +77,8 @@ boris/synthetic_time_budget.py,sha256=3Eb9onMLmgqCLd50CuxV9L8RV2ESzfaMWvPK_bXUMM
|
|
|
78
77
|
boris/time_budget_functions.py,sha256=y5He8crz0xsTxVfz0jATwFFQVnPAIrNHja_0sF6NtRE,52551
|
|
79
78
|
boris/time_budget_widget.py,sha256=z-tyITBtIz-KH1H2OdMB5a8x9QQLK7Wu96-zkC6NVDA,43213
|
|
80
79
|
boris/transitions.py,sha256=_aZJfJWv3EBrtmQ7qsdTCayQo6uWU7BXqtQQgflEhr4,12250
|
|
81
|
-
boris/utilities.py,sha256=
|
|
82
|
-
boris/version.py,sha256=
|
|
80
|
+
boris/utilities.py,sha256=SoVTDiFkidh-POwYcu_TVfJsFeXHYZV--s1tdxKH1VU,52654
|
|
81
|
+
boris/version.py,sha256=1P9L6ZnUsH7kDLTVpQPQLqCJMN5PAWXFyIRRUTkgDkA,787
|
|
83
82
|
boris/video_equalizer.py,sha256=FartoGghFK-T53zklP70rPKYqTuzL8qdvfGlsOF2wwc,5854
|
|
84
83
|
boris/video_equalizer_ui.py,sha256=1CG3s79eM4JAbaCx3i1ILZXLceb41_gGXlOLNfpBgnw,10142
|
|
85
84
|
boris/video_operations.py,sha256=mh3iR__Sm2KnV44L_sW2pOo3AgLwlM7wiTnnqQiAVs4,9381
|
|
@@ -87,6 +86,7 @@ boris/view_df.py,sha256=AKScLASX2Uatw7rqPbsnio83eVT4GZYCFhL091eMvlY,3370
|
|
|
87
86
|
boris/view_df_ui.py,sha256=CaMeRH_vQ00CTDDFQn73ZZaS-r8BSTWpL-dMCFqzJ_Q,2775
|
|
88
87
|
boris/write_event.py,sha256=Fsy_apFl7RLnRsBAwXqACr_URnE_QoAFiPMh0o95ANg,23852
|
|
89
88
|
boris/analysis_plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
|
+
boris/analysis_plugins/_latency.py,sha256=vLWCPh0cPpAEpdMboCkVIuYO1e3pJlP5tEICe27Sqms,2027
|
|
90
90
|
boris/analysis_plugins/number_of_occurences.py,sha256=IDyDrdezqvSKT3BlD8QWpSYk8X9nnBBLI80OUnFJ3bY,509
|
|
91
91
|
boris/analysis_plugins/number_of_occurences_by_independent_variable.py,sha256=t39bmmmZIDCSbcDvVeiKAhKNNP2SdpHp417JczHEnP4,793
|
|
92
92
|
boris/analysis_plugins/time_budget.py,sha256=C1wNYwd5Jugr8h5z2aXRUBY8dF8pD4n953dPwNHY5VY,2244
|
|
@@ -96,9 +96,9 @@ boris/portion/dict.py,sha256=SyHxc7PfDw2ufNLFQycwJtzmRfL48rDp4UrM2KN7IWc,11282
|
|
|
96
96
|
boris/portion/func.py,sha256=3TkQtFKLfsqntwd27HSGHceFhnXHmT-EbNMqktElC5Q,2174
|
|
97
97
|
boris/portion/interval.py,sha256=bAdUiJjGeUAPgsBAImwNeviiwfQq5odfhFZccAWzOTA,20299
|
|
98
98
|
boris/portion/io.py,sha256=ppNeRpiLNrocF1yzGeuEUIhYMf2LfsR-cji3d0nmvUs,6371
|
|
99
|
-
boris_behav_obs-9.3.
|
|
100
|
-
boris_behav_obs-9.3.
|
|
101
|
-
boris_behav_obs-9.3.
|
|
102
|
-
boris_behav_obs-9.3.
|
|
103
|
-
boris_behav_obs-9.3.
|
|
104
|
-
boris_behav_obs-9.3.
|
|
99
|
+
boris_behav_obs-9.3.2.dist-info/licenses/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
|
|
100
|
+
boris_behav_obs-9.3.2.dist-info/METADATA,sha256=ZUt0hNpFSkVJweQn-PndnIpkRpCN--vQaJOqYwfEZMg,4518
|
|
101
|
+
boris_behav_obs-9.3.2.dist-info/WHEEL,sha256=MAQBAzGbXNI3bUmkDsiV_duv8i-gcdnLzw7cfUFwqhU,109
|
|
102
|
+
boris_behav_obs-9.3.2.dist-info/entry_points.txt,sha256=k__8XvFi4vaA4QFvQehCZjYkKmZH34HSAJI2iYCWrMs,52
|
|
103
|
+
boris_behav_obs-9.3.2.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
|
|
104
|
+
boris_behav_obs-9.3.2.dist-info/RECORD,,
|
boris/1.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
from PIL import Image, ImageDraw, ImageFont
|
|
3
|
-
import mpv
|
|
4
|
-
|
|
5
|
-
player = mpv.MPV()
|
|
6
|
-
|
|
7
|
-
player.loop = True
|
|
8
|
-
player.play('/home/olivier/gdrive_sync/src/python/generate_video_test/video1.mp4')
|
|
9
|
-
player.wait_until_playing()
|
|
10
|
-
|
|
11
|
-
font = ImageFont.truetype('DejaVuSans.ttf', 40)
|
|
12
|
-
|
|
13
|
-
overlay = player.create_image_overlay()
|
|
14
|
-
|
|
15
|
-
img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
|
|
16
|
-
d = ImageDraw.Draw(img)
|
|
17
|
-
d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
|
|
18
|
-
#d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
|
|
19
|
-
|
|
20
|
-
pos = 100
|
|
21
|
-
|
|
22
|
-
overlay.update(img, pos=(2*pos, pos))
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
while not player.core_idle:
|
|
26
|
-
pass
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'''
|
|
30
|
-
for pos in range(0, 500, 5):
|
|
31
|
-
ts = player.time_pos
|
|
32
|
-
if ts is None:
|
|
33
|
-
break
|
|
34
|
-
|
|
35
|
-
img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
|
|
36
|
-
d = ImageDraw.Draw(img)
|
|
37
|
-
d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
|
|
38
|
-
d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
|
|
39
|
-
|
|
40
|
-
overlay.update(img, pos=(2*pos, pos))
|
|
41
|
-
time.sleep(0.05)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
overlay.remove()
|
|
45
|
-
'''
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|