boris-behav-obs 8.27.9__py2.py3-none-any.whl → 9.0.1__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/about.py +7 -5
- boris/add_modifier.py +35 -35
- boris/add_modifier_ui.py +229 -129
- boris/advanced_event_filtering.py +3 -3
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/number_of_occurences.py +60 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +72 -0
- boris/analysis_plugins/time_budget.py +95 -0
- boris/behav_coding_map_creator.py +103 -108
- boris/behavior_binary_table.py +1 -1
- boris/behaviors_coding_map.py +8 -8
- boris/coding_pad.py +6 -6
- boris/config.py +6 -0
- boris/config_file.py +1 -1
- boris/connections.py +4 -2
- boris/converters.py +2 -3
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +2 -2
- boris/core.py +340 -94
- boris/core_qrc.py +16088 -13246
- boris/core_ui.py +922 -812
- boris/db_functions.py +3 -1
- boris/dialog.py +14 -13
- boris/duration_widget.py +5 -5
- boris/edit_event.py +1 -1
- boris/edit_event_ui.py +162 -88
- boris/event_operations.py +4 -25
- boris/events_cursor.py +17 -9
- boris/events_snapshots.py +5 -5
- boris/exclusion_matrix.py +1 -1
- boris/export_events.py +38 -28
- boris/export_observation.py +1 -1
- boris/external_processes.py +3 -5
- boris/geometric_measurement.py +49 -26
- boris/gui_utilities.py +31 -30
- boris/import_observations.py +2 -4
- boris/irr.py +1 -1
- boris/latency.py +1 -1
- boris/map_creator.py +77 -89
- boris/measurement_widget.py +4 -4
- boris/media_file.py +2 -4
- boris/menu_options.py +1 -3
- boris/modifiers_coding_map.py +4 -4
- boris/mpv2.py +0 -2
- boris/observation.py +124 -29
- boris/observation_operations.py +18 -40
- boris/observation_ui.py +566 -374
- boris/observations_list.py +6 -6
- boris/param_panel.py +2 -2
- boris/param_panel_ui.py +246 -141
- boris/player_dock_widget.py +16 -21
- boris/plot_data_module.py +6 -6
- boris/plot_events_rt.py +7 -8
- boris/plot_spectrogram_rt.py +7 -8
- boris/plot_waveform_rt.py +6 -7
- boris/plugins.py +79 -0
- boris/preferences.py +127 -17
- boris/preferences_ui.py +464 -240
- boris/project.py +69 -72
- boris/project_functions.py +233 -31
- boris/project_import_export.py +59 -67
- boris/project_ui.py +672 -440
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +2 -2
- boris/select_observations.py +2 -2
- boris/select_subj_behav.py +3 -3
- boris/state_events.py +1 -1
- boris/subjects_pad.py +5 -5
- boris/synthetic_time_budget.py +2 -2
- boris/time_budget_functions.py +15 -0
- boris/time_budget_widget.py +4 -4
- boris/transitions.py +34 -25
- boris/utilities.py +95 -2
- boris/version.py +2 -2
- boris/video_equalizer.py +4 -4
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +1 -1
- boris/view_df.py +106 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +9 -1
- {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/METADATA +5 -5
- boris_behav_obs-9.0.1.dist-info/RECORD +103 -0
- {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/WHEEL +1 -1
- boris/qdarkstyle/__init__.py +0 -479
- boris/qdarkstyle/__main__.py +0 -66
- boris/qdarkstyle/colorsystem.py +0 -38
- boris/qdarkstyle/dark/__init__.py +0 -1
- boris/qdarkstyle/dark/darkstyle_rc.py +0 -11379
- boris/qdarkstyle/dark/palette.py +0 -38
- boris/qdarkstyle/example/__init__.py +0 -4
- boris/qdarkstyle/example/__main__.py +0 -386
- boris/qdarkstyle/example/ui/__init__.py +0 -4
- boris/qdarkstyle/light/__init__.py +0 -1
- boris/qdarkstyle/light/lightstyle_rc.py +0 -11305
- boris/qdarkstyle/light/palette.py +0 -37
- boris/qdarkstyle/palette.py +0 -102
- boris/qdarkstyle/utils/__init__.py +0 -73
- boris/qdarkstyle/utils/__main__.py +0 -96
- boris/qdarkstyle/utils/images.py +0 -449
- boris/qdarkstyle/utils/scss.py +0 -318
- boris/vlc_local.py +0 -83
- boris_behav_obs-8.27.9.dist-info/RECORD +0 -114
- {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/top_level.txt +0 -0
boris/export_events.py
CHANGED
|
@@ -38,7 +38,7 @@ from . import project_functions
|
|
|
38
38
|
from . import dialog
|
|
39
39
|
from . import db_functions
|
|
40
40
|
|
|
41
|
-
from
|
|
41
|
+
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QInputDialog
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def export_events_as_behavioral_sequences(self, separated_subjects=False, timed=False):
|
|
@@ -93,28 +93,28 @@ def export_events_as_behavioral_sequences(self, separated_subjects=False, timed=
|
|
|
93
93
|
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
|
|
94
94
|
return
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
96
|
+
file_name, _ = QFileDialog.getSaveFileName(self, "Export events as behavioral sequences", "", "Text files (*.txt);;All files (*)")
|
|
97
|
+
|
|
98
|
+
if not file_name:
|
|
99
|
+
return
|
|
100
|
+
r, msg = export_observation.observation_to_behavioral_sequences(
|
|
101
|
+
pj=self.pj,
|
|
102
|
+
selected_observations=selected_observations,
|
|
103
|
+
parameters=parameters,
|
|
104
|
+
behaviors_separator=self.behav_seq_separator,
|
|
105
|
+
separated_subjects=separated_subjects,
|
|
106
|
+
timed=timed,
|
|
107
|
+
file_name=file_name,
|
|
108
|
+
)
|
|
109
|
+
if not r:
|
|
110
|
+
logging.critical(f"Error while exporting events as behavioral sequences: {msg}")
|
|
111
|
+
QMessageBox.critical(
|
|
112
|
+
None,
|
|
113
|
+
cfg.programName,
|
|
114
|
+
f"Error while exporting events as behavioral sequences:<br>{msg}",
|
|
115
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
116
|
+
QMessageBox.NoButton,
|
|
108
117
|
)
|
|
109
|
-
if not r:
|
|
110
|
-
logging.critical(f"Error while exporting events as behavioral sequences: {msg}")
|
|
111
|
-
QMessageBox.critical(
|
|
112
|
-
None,
|
|
113
|
-
cfg.programName,
|
|
114
|
-
f"Error while exporting events as behavioral sequences:<br>{msg}",
|
|
115
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
116
|
-
QMessageBox.NoButton,
|
|
117
|
-
)
|
|
118
118
|
|
|
119
119
|
|
|
120
120
|
def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
@@ -215,7 +215,12 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
|
215
215
|
return
|
|
216
216
|
|
|
217
217
|
if len(selected_observations) == 1:
|
|
218
|
-
|
|
218
|
+
file_dialog_options = QFileDialog.Options()
|
|
219
|
+
file_dialog_options |= QFileDialog.DontConfirmOverwrite
|
|
220
|
+
|
|
221
|
+
file_name, filter_ = QFileDialog().getSaveFileName(
|
|
222
|
+
self, "Export events", "", ";;".join(available_formats), options=file_dialog_options
|
|
223
|
+
)
|
|
219
224
|
if not file_name:
|
|
220
225
|
return
|
|
221
226
|
|
|
@@ -223,7 +228,7 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
|
223
228
|
if pl.Path(file_name).suffix != "." + output_format:
|
|
224
229
|
file_name = str(pl.Path(file_name)) + "." + output_format
|
|
225
230
|
# check if file with new extension already exists
|
|
226
|
-
if pl.Path(file_name).
|
|
231
|
+
if pl.Path(file_name).exists():
|
|
227
232
|
if (
|
|
228
233
|
dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE])
|
|
229
234
|
== cfg.CANCEL
|
|
@@ -398,7 +403,12 @@ def export_aggregated_events(self):
|
|
|
398
403
|
cfg.RDS,
|
|
399
404
|
)
|
|
400
405
|
|
|
401
|
-
|
|
406
|
+
file_dialog_options = QFileDialog.Options()
|
|
407
|
+
file_dialog_options |= QFileDialog.DontConfirmOverwrite
|
|
408
|
+
|
|
409
|
+
fileName, filter_ = QFileDialog().getSaveFileName(
|
|
410
|
+
self, "Export aggregated events", "", ";;".join(file_formats), options=file_dialog_options
|
|
411
|
+
)
|
|
402
412
|
|
|
403
413
|
if not fileName:
|
|
404
414
|
return
|
|
@@ -407,7 +417,7 @@ def export_aggregated_events(self):
|
|
|
407
417
|
if pl.Path(fileName).suffix != "." + outputFormat:
|
|
408
418
|
# check if file with new extension already exists
|
|
409
419
|
fileName = str(pl.Path(fileName)) + "." + outputFormat
|
|
410
|
-
if pl.Path(fileName).
|
|
420
|
+
if pl.Path(fileName).exists():
|
|
411
421
|
if dialog.MessageDialog(cfg.programName, f"The file {fileName} already exists.", [cfg.CANCEL, cfg.OVERWRITE]) == cfg.CANCEL:
|
|
412
422
|
return
|
|
413
423
|
|
|
@@ -671,8 +681,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
671
681
|
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to export")
|
|
672
682
|
return
|
|
673
683
|
|
|
674
|
-
export_dir = QFileDialog
|
|
675
|
-
self, "Export events as Praat TextGrid", os.path.expanduser("~"), options=QFileDialog
|
|
684
|
+
export_dir = QFileDialog.getExistingDirectory(
|
|
685
|
+
self, "Export events as Praat TextGrid", os.path.expanduser("~"), options=QFileDialog.ShowDirsOnly
|
|
676
686
|
)
|
|
677
687
|
if not export_dir:
|
|
678
688
|
return
|
boris/export_observation.py
CHANGED
|
@@ -601,7 +601,7 @@ def dataset_write(dataset: tablib.Dataset, file_name: str, output_format: str, d
|
|
|
601
601
|
if output_format == cfg.PANDAS_DF_EXT:
|
|
602
602
|
df.to_pickle(file_name)
|
|
603
603
|
|
|
604
|
-
if
|
|
604
|
+
if output_format == cfg.RDS_EXT and flag_pyreadr_loaded:
|
|
605
605
|
pyreadr.write_rds(file_name, df)
|
|
606
606
|
|
|
607
607
|
return True, ""
|
boris/external_processes.py
CHANGED
|
@@ -20,14 +20,13 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
import os
|
|
25
24
|
import tempfile
|
|
26
25
|
import pathlib as pl
|
|
27
26
|
import logging
|
|
28
27
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
28
|
+
from PySide6.QtWidgets import QFileDialog, QMessageBox, QInputDialog
|
|
29
|
+
from PySide6.QtCore import (
|
|
31
30
|
Qt,
|
|
32
31
|
QProcess,
|
|
33
32
|
)
|
|
@@ -101,8 +100,7 @@ def ffmpeg_process(self, action: str):
|
|
|
101
100
|
else:
|
|
102
101
|
msg = f"Select one or more video files to {action.replace('_', ' and ')}"
|
|
103
102
|
file_type = "Video files (*)"
|
|
104
|
-
|
|
105
|
-
file_names = fn[0] if type(fn) is tuple else fn
|
|
103
|
+
file_names, _ = QFileDialog().getOpenFileNames(self, msg, "", file_type)
|
|
106
104
|
|
|
107
105
|
if not file_names:
|
|
108
106
|
return
|
boris/geometric_measurement.py
CHANGED
|
@@ -33,9 +33,9 @@ except ModuleNotFoundError:
|
|
|
33
33
|
flag_pyreadr_loaded = False
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
from
|
|
37
|
-
from
|
|
38
|
-
from
|
|
36
|
+
from PySide6.QtCore import QPoint, Qt, Signal, QEvent
|
|
37
|
+
from PySide6.QtGui import QColor, QPainter, QPolygon, QPixmap, QAction, QPen
|
|
38
|
+
from PySide6.QtWidgets import (
|
|
39
39
|
QApplication,
|
|
40
40
|
QCheckBox,
|
|
41
41
|
QFileDialog,
|
|
@@ -51,7 +51,6 @@ from PyQt5.QtWidgets import (
|
|
|
51
51
|
QColorDialog,
|
|
52
52
|
QSpacerItem,
|
|
53
53
|
QSizePolicy,
|
|
54
|
-
QAction,
|
|
55
54
|
QDialog,
|
|
56
55
|
)
|
|
57
56
|
|
|
@@ -67,10 +66,10 @@ class wgMeasurement(QDialog):
|
|
|
67
66
|
widget for geometric measurements
|
|
68
67
|
"""
|
|
69
68
|
|
|
70
|
-
closeSignal =
|
|
71
|
-
send_event_signal =
|
|
72
|
-
reload_image_signal =
|
|
73
|
-
save_picture_signal =
|
|
69
|
+
closeSignal = Signal()
|
|
70
|
+
send_event_signal = Signal(QEvent)
|
|
71
|
+
reload_image_signal = Signal()
|
|
72
|
+
save_picture_signal = Signal(str)
|
|
74
73
|
mark_color: str = cfg.ACTIVE_MEASUREMENTS_COLOR
|
|
75
74
|
flag_saved = True # store if measurements are saved
|
|
76
75
|
draw_mem: dict = {}
|
|
@@ -452,15 +451,24 @@ def draw_point(self, x: int, y: int, color: str, n_player: int = 0) -> None:
|
|
|
452
451
|
"""
|
|
453
452
|
draw point on frame-by-frame image
|
|
454
453
|
"""
|
|
454
|
+
|
|
455
|
+
logging.debug("draw_point function")
|
|
456
|
+
|
|
455
457
|
RADIUS = 6
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
painter
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
458
|
+
|
|
459
|
+
pixmap_copy = self.dw_player[n_player].frame_viewer.pixmap().copy()
|
|
460
|
+
|
|
461
|
+
painter = QPainter(pixmap_copy)
|
|
462
|
+
try:
|
|
463
|
+
painter.setPen(QPen(QColor(color), 1))
|
|
464
|
+
painter.drawEllipse(QPoint(x, y), RADIUS, RADIUS)
|
|
465
|
+
# cross inside circle
|
|
466
|
+
painter.drawLine(x - RADIUS, y, x + RADIUS, y)
|
|
467
|
+
painter.drawLine(x, y - RADIUS, x, y + RADIUS)
|
|
468
|
+
finally:
|
|
469
|
+
painter.end()
|
|
470
|
+
|
|
471
|
+
self.dw_player[n_player].frame_viewer.setPixmap(pixmap_copy)
|
|
464
472
|
self.dw_player[n_player].frame_viewer.update()
|
|
465
473
|
|
|
466
474
|
|
|
@@ -468,11 +476,17 @@ def draw_line(self, x1: int, y1: int, x2: int, y2: int, color: str, n_player: in
|
|
|
468
476
|
"""
|
|
469
477
|
draw line on frame-by-frame image
|
|
470
478
|
"""
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
painter
|
|
474
|
-
|
|
475
|
-
|
|
479
|
+
|
|
480
|
+
pixmap_copy = self.dw_player[n_player].frame_viewer.pixmap().copy()
|
|
481
|
+
painter = QPainter(pixmap_copy)
|
|
482
|
+
|
|
483
|
+
try:
|
|
484
|
+
painter.setPen(QColor(color))
|
|
485
|
+
painter.drawLine(x1, y1, x2, y2)
|
|
486
|
+
finally:
|
|
487
|
+
painter.end()
|
|
488
|
+
|
|
489
|
+
self.dw_player[n_player].frame_viewer.setPixmap(pixmap_copy)
|
|
476
490
|
self.dw_player[n_player].frame_viewer.update()
|
|
477
491
|
|
|
478
492
|
|
|
@@ -520,6 +534,8 @@ def image_clicked(self, n_player: int, event) -> None:
|
|
|
520
534
|
|
|
521
535
|
x, y = event.pos().x(), event.pos().y()
|
|
522
536
|
|
|
537
|
+
logging.debug(f"clicked on {x} {y}")
|
|
538
|
+
|
|
523
539
|
# convert label coordinates in pixmap coordinates
|
|
524
540
|
pixmap_x = int(x - (self.dw_player[n_player].frame_viewer.width() - self.dw_player[n_player].frame_viewer.pixmap().width()) / 2)
|
|
525
541
|
pixmap_y = int(y - (self.dw_player[n_player].frame_viewer.height() - self.dw_player[n_player].frame_viewer.pixmap().height()) / 2)
|
|
@@ -892,11 +908,18 @@ def redraw_measurements(self):
|
|
|
892
908
|
for x, y in element["coordinates"]:
|
|
893
909
|
x, y = scale_coord([x, y])
|
|
894
910
|
polygon.append(QPoint(x, y))
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
painter
|
|
898
|
-
|
|
899
|
-
|
|
911
|
+
|
|
912
|
+
pixmap_copy = self.dw_player[idx].frame_viewer.pixmap().copy()
|
|
913
|
+
painter = QPainter(pixmap_copy)
|
|
914
|
+
|
|
915
|
+
try:
|
|
916
|
+
painter.setPen(QColor(elements_color))
|
|
917
|
+
painter.drawPolygon(polygon)
|
|
918
|
+
finally:
|
|
919
|
+
painter.end()
|
|
920
|
+
|
|
921
|
+
self.dw_player[idx].frame_viewer.setPixmap(pixmap_copy)
|
|
922
|
+
|
|
900
923
|
dw.frame_viewer.update()
|
|
901
924
|
|
|
902
925
|
if element["object_type"] == cfg.POLYLINE_OBJECT:
|
boris/gui_utilities.py
CHANGED
|
@@ -21,9 +21,9 @@ Copyright 2012-2024 Olivier Friard
|
|
|
21
21
|
|
|
22
22
|
import pathlib as pl
|
|
23
23
|
import logging
|
|
24
|
-
from
|
|
25
|
-
from
|
|
26
|
-
from
|
|
24
|
+
from PySide6.QtCore import QSettings
|
|
25
|
+
from PySide6.QtWidgets import QWidget
|
|
26
|
+
from PySide6.QtGui import QIcon
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def save_geometry(widget: QWidget, widget_name: str):
|
|
@@ -59,43 +59,44 @@ def restore_geometry(widget: QWidget, widget_name: str, default_geometry):
|
|
|
59
59
|
logging.warning("Error during restoring default")
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def set_icons(self):
|
|
62
|
+
def set_icons(self, theme_mode: str) -> None:
|
|
63
63
|
"""
|
|
64
64
|
set icons of actions
|
|
65
65
|
"""
|
|
66
66
|
|
|
67
67
|
# menu
|
|
68
|
-
self.
|
|
69
|
-
self.actionPlot_events2.setIcon(QIcon(":/plot_events"))
|
|
70
|
-
self.action_advanced_event_filtering.setIcon(QIcon(":/filter"))
|
|
71
|
-
self.actionPreferences.setIcon(QIcon(":/preferences"))
|
|
68
|
+
self.action_obs_list.setIcon(QIcon(f":/observations_list_{theme_mode}"))
|
|
72
69
|
|
|
73
|
-
self.
|
|
70
|
+
self.actionTime_budget.setIcon(QIcon(f":/time_budget_{theme_mode}"))
|
|
71
|
+
self.actionPlot_events2.setIcon(QIcon(f":/plot_events_{theme_mode}"))
|
|
72
|
+
self.action_advanced_event_filtering.setIcon(QIcon(f":/filter_{theme_mode}"))
|
|
74
73
|
|
|
75
|
-
self.
|
|
76
|
-
self.actionReset.setIcon(QIcon(":/reset"))
|
|
77
|
-
self.actionJumpBackward.setIcon(QIcon(":/jump_backward"))
|
|
78
|
-
self.actionJumpForward.setIcon(QIcon(":/jump_forward"))
|
|
74
|
+
self.actionPreferences.setIcon(QIcon(f":/preferences_{theme_mode}"))
|
|
79
75
|
|
|
80
|
-
self.
|
|
81
|
-
self.
|
|
82
|
-
self.
|
|
76
|
+
self.actionPlay.setIcon(QIcon(f":/play_{theme_mode}"))
|
|
77
|
+
self.actionReset.setIcon(QIcon(f":/reset_{theme_mode}"))
|
|
78
|
+
self.actionJumpBackward.setIcon(QIcon(f":/jump_backward_{theme_mode}"))
|
|
79
|
+
self.actionJumpForward.setIcon(QIcon(f":/jump_forward_{theme_mode}"))
|
|
83
80
|
|
|
84
|
-
self.
|
|
85
|
-
self.
|
|
81
|
+
self.actionFaster.setIcon(QIcon(f":/faster_{theme_mode}"))
|
|
82
|
+
self.actionSlower.setIcon(QIcon(f":/slower_{theme_mode}"))
|
|
83
|
+
self.actionNormalSpeed.setIcon(QIcon(f":/normal_speed_{theme_mode}"))
|
|
86
84
|
|
|
87
|
-
self.
|
|
85
|
+
self.actionPrevious.setIcon(QIcon(f":/previous_{theme_mode}"))
|
|
86
|
+
self.actionNext.setIcon(QIcon(f":/next_{theme_mode}"))
|
|
88
87
|
|
|
89
|
-
self.
|
|
90
|
-
self.actionFrame_forward.setIcon(QIcon(":/frame_forward"))
|
|
91
|
-
self.actionCloseObs.setIcon(QIcon(":/close_observation"))
|
|
92
|
-
self.actionCurrent_Time_Budget.setIcon(QIcon(":/time_budget"))
|
|
93
|
-
self.actionPlot_current_observation.setIcon(QIcon(":/plot_events"))
|
|
88
|
+
self.actionSnapshot.setIcon(QIcon(f":/snapshot_{theme_mode}"))
|
|
94
89
|
|
|
95
|
-
self.
|
|
90
|
+
self.actionFrame_backward.setIcon(QIcon(f":/frame_backward_{theme_mode}"))
|
|
91
|
+
self.actionFrame_forward.setIcon(QIcon(f":/frame_forward_{theme_mode}"))
|
|
92
|
+
self.actionCloseObs.setIcon(QIcon(f":/close_observation_{theme_mode}"))
|
|
93
|
+
self.actionCurrent_Time_Budget.setIcon(QIcon(f":/time_budget_{theme_mode}"))
|
|
94
|
+
self.actionPlot_current_observation.setIcon(QIcon(f":/plot_events_{theme_mode}"))
|
|
96
95
|
|
|
97
|
-
self.
|
|
98
|
-
|
|
99
|
-
self.
|
|
100
|
-
self.
|
|
101
|
-
self.
|
|
96
|
+
self.actionPlot_events_in_real_time.setIcon(QIcon(f":/plot_real_time_{theme_mode}"))
|
|
97
|
+
|
|
98
|
+
self.actionBehavior_bar_plot.setIcon(QIcon(f":/plot_time_budget_{theme_mode}"))
|
|
99
|
+
self.actionPlot_current_time_budget.setIcon(QIcon(f":/plot_time_budget_{theme_mode}"))
|
|
100
|
+
self.action_geometric_measurements.setIcon(QIcon(f":/measurement_{theme_mode}"))
|
|
101
|
+
self.actionFind_in_current_obs.setIcon(QIcon(f":/find_{theme_mode}"))
|
|
102
|
+
self.actionExplore_project.setIcon(QIcon(f":/explore_{theme_mode}"))
|
boris/import_observations.py
CHANGED
|
@@ -19,13 +19,12 @@ Copyright 2012-2024 Olivier Friard
|
|
|
19
19
|
MA 02110-1301, USA.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
import json
|
|
24
23
|
import datetime
|
|
25
24
|
from pathlib import Path
|
|
26
25
|
import pandas as pd
|
|
27
26
|
|
|
28
|
-
from
|
|
27
|
+
from PySide6.QtWidgets import (
|
|
29
28
|
QMessageBox,
|
|
30
29
|
QFileDialog,
|
|
31
30
|
)
|
|
@@ -210,10 +209,9 @@ def import_observations(self):
|
|
|
210
209
|
import observations from project file
|
|
211
210
|
"""
|
|
212
211
|
|
|
213
|
-
|
|
212
|
+
file_name, _ = QFileDialog().getOpenFileName(
|
|
214
213
|
None, "Choose a file", "", "BORIS project files (*.boris);;Spreadsheet files (*.ods *.xlsx *);;All files (*)"
|
|
215
214
|
)
|
|
216
|
-
file_name = fn[0] if type(fn) is tuple else fn
|
|
217
215
|
|
|
218
216
|
if not file_name:
|
|
219
217
|
return
|
boris/irr.py
CHANGED
|
@@ -25,7 +25,7 @@ import logging
|
|
|
25
25
|
from decimal import Decimal as dec
|
|
26
26
|
|
|
27
27
|
import numpy as np
|
|
28
|
-
from
|
|
28
|
+
from PySide6.QtWidgets import QInputDialog, QMessageBox
|
|
29
29
|
|
|
30
30
|
from . import config as cfg
|
|
31
31
|
from . import db_functions, dialog, project_functions, select_subj_behav
|
boris/latency.py
CHANGED
boris/map_creator.py
CHANGED
|
@@ -24,32 +24,22 @@ import binascii
|
|
|
24
24
|
import json
|
|
25
25
|
import os
|
|
26
26
|
|
|
27
|
-
from
|
|
27
|
+
from PySide6.QtCore import (
|
|
28
28
|
Qt,
|
|
29
|
-
|
|
29
|
+
Signal,
|
|
30
30
|
QPoint,
|
|
31
31
|
QByteArray,
|
|
32
32
|
QBuffer,
|
|
33
33
|
QIODevice,
|
|
34
34
|
QLineF,
|
|
35
35
|
)
|
|
36
|
-
from
|
|
37
|
-
QColor,
|
|
38
|
-
QBrush,
|
|
39
|
-
QMouseEvent,
|
|
40
|
-
QPixmap,
|
|
41
|
-
QIcon,
|
|
42
|
-
QPen,
|
|
43
|
-
QPolygon,
|
|
44
|
-
QPolygonF,
|
|
45
|
-
)
|
|
36
|
+
from PySide6.QtGui import QColor, QBrush, QMouseEvent, QPixmap, QIcon, QPen, QPolygon, QPolygonF, QAction
|
|
46
37
|
|
|
47
|
-
from
|
|
38
|
+
from PySide6.QtWidgets import (
|
|
48
39
|
QGraphicsPolygonItem,
|
|
49
40
|
QGraphicsEllipseItem,
|
|
50
41
|
QGraphicsPixmapItem,
|
|
51
42
|
QGraphicsLineItem,
|
|
52
|
-
QAction,
|
|
53
43
|
QMainWindow,
|
|
54
44
|
QGraphicsView,
|
|
55
45
|
QPushButton,
|
|
@@ -80,14 +70,14 @@ selectedBrush.setColor(QColor(255, 255, 0, 255))
|
|
|
80
70
|
|
|
81
71
|
|
|
82
72
|
class ModifiersMapCreatorWindow(QMainWindow):
|
|
83
|
-
closed =
|
|
73
|
+
closed = Signal()
|
|
84
74
|
|
|
85
75
|
class View(QGraphicsView):
|
|
86
76
|
"""
|
|
87
77
|
class for handling mousepress event in QGraphicsView
|
|
88
78
|
"""
|
|
89
79
|
|
|
90
|
-
mousePress =
|
|
80
|
+
mousePress = Signal(QMouseEvent)
|
|
91
81
|
|
|
92
82
|
def mousePressEvent(self, event):
|
|
93
83
|
self.mousePress.emit(event)
|
|
@@ -527,79 +517,77 @@ class ModifiersMapCreatorWindow(QMainWindow):
|
|
|
527
517
|
if response == "Cancel":
|
|
528
518
|
return
|
|
529
519
|
|
|
530
|
-
|
|
520
|
+
fileName, _ = QFileDialog().getOpenFileName(
|
|
531
521
|
self,
|
|
532
522
|
"Open a coding map",
|
|
533
523
|
"",
|
|
534
524
|
"BORIS coding map (*.boris_map);;All files (*)",
|
|
535
525
|
)
|
|
536
|
-
fileName = fn[0] if type(fn) is tuple else fn
|
|
537
526
|
|
|
538
|
-
if fileName:
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
)
|
|
547
|
-
|
|
527
|
+
if not fileName:
|
|
528
|
+
self.statusBar().showMessage("No file", 5000)
|
|
529
|
+
try:
|
|
530
|
+
self.codingMap = json.loads(open(fileName, "r").read())
|
|
531
|
+
except Exception:
|
|
532
|
+
QMessageBox.critical(
|
|
533
|
+
self,
|
|
534
|
+
cfg.programName,
|
|
535
|
+
"The file {} seems not a behaviors coding map...".format(fileName),
|
|
536
|
+
)
|
|
537
|
+
return
|
|
548
538
|
|
|
549
|
-
|
|
539
|
+
self.cancelMap()
|
|
550
540
|
|
|
551
|
-
|
|
541
|
+
self.mapName = self.codingMap["name"]
|
|
552
542
|
|
|
553
|
-
|
|
543
|
+
self.setWindowTitle(cfg.programName + " - Map creator tool - " + self.mapName)
|
|
554
544
|
|
|
555
|
-
|
|
545
|
+
self.bitmapFileName = True
|
|
556
546
|
|
|
557
|
-
|
|
547
|
+
self.fileName = fileName
|
|
558
548
|
|
|
559
|
-
|
|
560
|
-
|
|
549
|
+
self.areasList = self.codingMap["areas"] # dictionary of dictionaries
|
|
550
|
+
bitmapContent = binascii.a2b_base64(self.codingMap["bitmap"])
|
|
561
551
|
|
|
562
|
-
|
|
552
|
+
self.pixmap.loadFromData(bitmapContent)
|
|
563
553
|
|
|
564
|
-
|
|
554
|
+
self.btDeleteArea.setEnabled(False)
|
|
565
555
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
556
|
+
self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
|
|
557
|
+
pixItem = QGraphicsPixmapItem(self.pixmap)
|
|
558
|
+
pixItem.setPos(0, 0)
|
|
559
|
+
self.view.scene().addItem(pixItem)
|
|
570
560
|
|
|
571
|
-
|
|
572
|
-
|
|
561
|
+
for areaCode in self.areasList:
|
|
562
|
+
points = self.areasList[areaCode]["geometry"]
|
|
573
563
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
564
|
+
newPolygon = QPolygonF()
|
|
565
|
+
for p in points:
|
|
566
|
+
newPolygon.append(QPoint(p[0], p[1]))
|
|
577
567
|
|
|
578
|
-
|
|
579
|
-
|
|
568
|
+
clr = QColor()
|
|
569
|
+
clr.setRgba(self.areasList[areaCode]["color"])
|
|
580
570
|
|
|
581
|
-
|
|
582
|
-
|
|
571
|
+
# draw polygon
|
|
572
|
+
polygon = QGraphicsPolygonItem()
|
|
583
573
|
|
|
584
|
-
|
|
574
|
+
polygon.setPolygon(newPolygon)
|
|
585
575
|
|
|
586
|
-
|
|
576
|
+
polygon.setPen(QPen(clr, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))
|
|
587
577
|
|
|
588
|
-
|
|
578
|
+
polygon.setBrush(QBrush(clr, Qt.SolidPattern))
|
|
589
579
|
|
|
590
|
-
|
|
591
|
-
|
|
580
|
+
self.view.scene().addItem(polygon)
|
|
581
|
+
self.polygonsList2[areaCode] = polygon
|
|
592
582
|
|
|
593
|
-
|
|
583
|
+
self.btNewArea.setVisible(True)
|
|
594
584
|
|
|
595
|
-
|
|
585
|
+
self.btLoad.setVisible(False)
|
|
596
586
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
else:
|
|
602
|
-
self.statusBar().showMessage("No file", 5000)
|
|
587
|
+
self.saveMapAction.setEnabled(True)
|
|
588
|
+
self.saveAsMapAction.setEnabled(True)
|
|
589
|
+
self.mapNameAction.setEnabled(True)
|
|
590
|
+
self.statusBar().showMessage('Click "New area" to create a new area')
|
|
603
591
|
|
|
604
592
|
def saveMap(self):
|
|
605
593
|
if self.fileName:
|
|
@@ -843,41 +831,41 @@ class ModifiersMapCreatorWindow(QMainWindow):
|
|
|
843
831
|
|
|
844
832
|
maxSize = 512
|
|
845
833
|
|
|
846
|
-
|
|
847
|
-
fileName = fn[0] if type(fn) is tuple else fn
|
|
834
|
+
fileName, _ = QFileDialog().getOpenFileName(self, "Load bitmap", "", "bitmap files (*.png *.jpg);;All files (*)")
|
|
848
835
|
|
|
849
|
-
if fileName:
|
|
850
|
-
|
|
836
|
+
if not fileName:
|
|
837
|
+
return
|
|
838
|
+
self.bitmapFileName = fileName
|
|
851
839
|
|
|
852
|
-
|
|
840
|
+
self.pixmap.load(self.bitmapFileName)
|
|
853
841
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
842
|
+
if self.pixmap.size().width() > maxSize or self.pixmap.size().height() > maxSize:
|
|
843
|
+
self.pixmap = self.pixmap.scaled(maxSize, maxSize, Qt.KeepAspectRatio)
|
|
844
|
+
QMessageBox.information(
|
|
845
|
+
self,
|
|
846
|
+
cfg.programName,
|
|
847
|
+
"The bitmap was resized to %d x %d pixels\nThe original file was not modified"
|
|
848
|
+
% (self.pixmap.size().width(), self.pixmap.size().height()),
|
|
849
|
+
)
|
|
862
850
|
|
|
863
|
-
|
|
864
|
-
|
|
851
|
+
# scale image
|
|
852
|
+
# pixmap = pixmap.scaled (256, 256, Qt.KeepAspectRatio)
|
|
865
853
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
854
|
+
self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
|
|
855
|
+
pixitem = QGraphicsPixmapItem(self.pixmap)
|
|
856
|
+
pixitem.setPos(0, 0)
|
|
857
|
+
self.view.scene().addItem(pixitem)
|
|
870
858
|
|
|
871
|
-
|
|
859
|
+
self.btNewArea.setVisible(True)
|
|
872
860
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
861
|
+
self.btLoad.setVisible(False)
|
|
862
|
+
self.saveMapAction.setEnabled(True)
|
|
863
|
+
self.saveAsMapAction.setEnabled(True)
|
|
864
|
+
self.mapNameAction.setEnabled(True)
|
|
877
865
|
|
|
878
|
-
|
|
866
|
+
self.statusBar().showMessage("""Click "New modifier" to create a new modifier""")
|
|
879
867
|
|
|
880
|
-
|
|
868
|
+
self.flagMapChanged = True
|
|
881
869
|
|
|
882
870
|
|
|
883
871
|
if __name__ == "__main__":
|