boris-behav-obs 8.16.6__py3-none-any.whl → 9.7.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 +24 -40
- boris/add_modifier.py +88 -80
- boris/add_modifier_ui.py +235 -131
- boris/advanced_event_filtering.py +23 -29
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_latency.py +59 -0
- boris/analysis_plugins/irr_cohen_kappa.py +109 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
- boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
- boris/analysis_plugins/number_of_occurences.py +22 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
- boris/analysis_plugins/time_budget.py +61 -0
- boris/behav_coding_map_creator.py +228 -229
- boris/behavior_binary_table.py +33 -50
- boris/behaviors_coding_map.py +17 -18
- boris/boris_cli.py +6 -25
- boris/cmd_arguments.py +12 -1
- boris/coding_pad.py +16 -34
- boris/config.py +108 -49
- boris/config_file.py +58 -67
- boris/connections.py +105 -58
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2106 -1277
- boris/core_qrc.py +15892 -10829
- boris/core_ui.py +941 -806
- boris/db_functions.py +17 -42
- boris/dev.py +134 -0
- boris/dialog.py +461 -242
- boris/duration_widget.py +9 -14
- boris/edit_event.py +61 -31
- boris/edit_event_ui.py +208 -97
- boris/event_operations.py +405 -281
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +180 -203
- boris/export_observation.py +60 -73
- boris/external_processes.py +123 -98
- boris/geometric_measurement.py +427 -218
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +4 -4
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +304 -0
- boris/irr.py +20 -57
- boris/latency.py +31 -24
- boris/measurement_widget.py +14 -18
- boris/media_file.py +17 -19
- boris/menu_options.py +16 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv2.py +127 -36
- boris/observation.py +493 -210
- boris/observation_operations.py +1010 -391
- boris/observation_ui.py +573 -363
- boris/observations_list.py +51 -58
- boris/otx_parser.py +74 -68
- boris/param_panel.py +45 -59
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +91 -56
- boris/plot_data_module.py +18 -53
- boris/plot_events.py +56 -153
- boris/plot_events_rt.py +16 -30
- boris/plot_spectrogram_rt.py +80 -56
- boris/plot_waveform_rt.py +23 -48
- boris/plugins.py +431 -0
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/preferences.py +304 -123
- boris/preferences_ui.py +684 -227
- boris/project.py +293 -270
- boris/project_functions.py +618 -537
- boris/project_import_export.py +204 -213
- boris/project_ui.py +673 -441
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +62 -90
- boris/select_observations.py +19 -197
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +51 -33
- boris/subjects_pad.py +6 -8
- boris/synthetic_time_budget.py +25 -17
- boris/time_budget_functions.py +169 -169
- boris/time_budget_widget.py +71 -86
- boris/transitions.py +41 -41
- boris/utilities.py +562 -222
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +78 -28
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +240 -136
- boris_behav_obs-9.7.2.dist-info/METADATA +140 -0
- boris_behav_obs-9.7.2.dist-info/RECORD +109 -0
- {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.2.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.2.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -323
- boris/converters.ui +0 -289
- boris/core.qrc +0 -37
- boris/core.ui +0 -1571
- boris/edit_event.ui +0 -233
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -982
- boris/observation.ui +0 -814
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1074
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.16.6.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.16.6.dist-info/METADATA +0 -134
- boris_behav_obs-8.16.6.dist-info/RECORD +0 -106
- boris_behav_obs-8.16.6.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-9.7.2.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.2.dist-info}/top_level.txt +0 -0
boris/connections.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This program is free software; you can redistribute it and/or modify
|
|
7
7
|
it under the terms of the GNU General Public License as published by
|
|
@@ -19,8 +19,8 @@ Copyright 2012-2023 Olivier Friard
|
|
|
19
19
|
MA 02110-1301, USA.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
from
|
|
23
|
-
from
|
|
22
|
+
from PySide6.QtCore import Qt, QTimer
|
|
23
|
+
from PySide6.QtGui import QAction
|
|
24
24
|
|
|
25
25
|
from . import config as cfg
|
|
26
26
|
from . import (
|
|
@@ -37,6 +37,7 @@ from . import (
|
|
|
37
37
|
import_observations,
|
|
38
38
|
irr,
|
|
39
39
|
latency,
|
|
40
|
+
cooccurence,
|
|
40
41
|
media_file,
|
|
41
42
|
observation_operations,
|
|
42
43
|
preferences,
|
|
@@ -69,9 +70,7 @@ def connections(self):
|
|
|
69
70
|
self.actionExport_project.triggered.connect(lambda: project_import_export.export_project_as_pickle_object(self.pj))
|
|
70
71
|
self.actionClose_project.triggered.connect(self.close_project)
|
|
71
72
|
|
|
72
|
-
self.action_media_file_and_images_directories_relative_path.triggered.connect(
|
|
73
|
-
self.set_media_files_path_relative_to_project_dir
|
|
74
|
-
)
|
|
73
|
+
self.action_media_file_and_images_directories_relative_path.triggered.connect(self.set_media_files_path_relative_to_project_dir)
|
|
75
74
|
self.action_data_files_relative_path.triggered.connect(self.set_data_files_path_relative_to_project_dir)
|
|
76
75
|
|
|
77
76
|
self.action_remove_media_files_and_images_directories_path.triggered.connect(self.remove_media_files_path)
|
|
@@ -84,19 +83,16 @@ def connections(self):
|
|
|
84
83
|
self.actionQuit.triggered.connect(self.actionQuit_activated)
|
|
85
84
|
|
|
86
85
|
# menu observations
|
|
87
|
-
self.actionNew_observation.triggered.connect(
|
|
88
|
-
lambda: observation_operations.new_observation(self, mode=cfg.NEW, obsId="")
|
|
89
|
-
)
|
|
86
|
+
self.actionNew_observation.triggered.connect(lambda: observation_operations.new_observation(self, mode=cfg.NEW, obsId=""))
|
|
90
87
|
|
|
91
|
-
self.actionOpen_observation.triggered.connect(
|
|
92
|
-
lambda: observation_operations.open_observation(self, mode=cfg.OBS_START)
|
|
93
|
-
)
|
|
88
|
+
self.actionOpen_observation.triggered.connect(lambda: observation_operations.open_observation(self, mode=cfg.OBS_START))
|
|
94
89
|
self.actionView_observation.triggered.connect(lambda: observation_operations.open_observation(self, mode=cfg.VIEW))
|
|
95
90
|
self.actionEdit_observation_2.triggered.connect(lambda: observation_operations.edit_observation(self))
|
|
96
91
|
self.actionObservationsList.triggered.connect(lambda: observation_operations.observations_list(self))
|
|
97
92
|
|
|
98
93
|
self.actionClose_observation.triggered.connect(lambda: observation_operations.close_observation(self))
|
|
99
94
|
|
|
95
|
+
self.action_create_observations.triggered.connect(lambda: observation_operations.create_observations(self))
|
|
100
96
|
self.actionRemove_observations.triggered.connect(lambda: observation_operations.remove_observations(self))
|
|
101
97
|
|
|
102
98
|
self.actionAdd_event.triggered.connect(lambda: event_operations.add_event(self))
|
|
@@ -107,22 +103,24 @@ def connections(self):
|
|
|
107
103
|
self.actionShow_all_events.triggered.connect(lambda: event_operations.show_all_events(self))
|
|
108
104
|
|
|
109
105
|
# twevent header
|
|
110
|
-
self.actionConfigure_twEvents_columns.triggered.connect(self.configure_twevents_columns)
|
|
106
|
+
# self.actionConfigure_twEvents_columns.triggered.connect(self.configure_twevents_columns)
|
|
111
107
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
# tv_events header
|
|
109
|
+
self.actionConfigure_tvevents_columns.triggered.connect(self.configure_tvevents_columns)
|
|
110
|
+
|
|
111
|
+
self.actionExport_observations_list.triggered.connect(lambda: observation_operations.export_observations_list_clicked(self))
|
|
115
112
|
|
|
116
113
|
self.actionCheckStateEvents.triggered.connect(lambda: state_events.check_state_events(self, mode="all"))
|
|
117
|
-
self.actionCheckStateEventsSingleObs.triggered.connect(
|
|
118
|
-
lambda: state_events.check_state_events(self, mode="current")
|
|
119
|
-
)
|
|
114
|
+
self.actionCheckStateEventsSingleObs.triggered.connect(lambda: state_events.check_state_events(self, mode="current"))
|
|
120
115
|
self.actionClose_unpaired_events.triggered.connect(lambda: state_events.fix_unpaired_events(self))
|
|
116
|
+
self.actionAdd_frame_indexes.triggered.connect(lambda: event_operations.add_frame_indexes(self))
|
|
117
|
+
|
|
121
118
|
self.actionRunEventOutside.triggered.connect(self.run_event_outside)
|
|
122
119
|
|
|
123
120
|
self.actionSelect_observations.triggered.connect(lambda: event_operations.select_events_between_activated(self))
|
|
124
121
|
|
|
125
122
|
self.actionEdit_selected_events.triggered.connect(lambda: event_operations.edit_selected_events(self))
|
|
123
|
+
self.action_add_comment.triggered.connect(lambda: event_operations.add_comment(self))
|
|
126
124
|
self.actionEdit_event_time.triggered.connect(lambda: event_operations.edit_time_selected_events(self))
|
|
127
125
|
|
|
128
126
|
self.actionCopy_events.triggered.connect(lambda: event_operations.copy_selected_events(self))
|
|
@@ -130,7 +128,7 @@ def connections(self):
|
|
|
130
128
|
|
|
131
129
|
self.actionExplore_project.triggered.connect(lambda: project_functions.explore_project(self))
|
|
132
130
|
self.actionFind_events.triggered.connect(lambda: event_operations.find_events(self))
|
|
133
|
-
self.actionFind_replace_events.triggered.connect(
|
|
131
|
+
self.actionFind_replace_events.triggered.connect(lambda: event_operations.find_replace_events(self))
|
|
134
132
|
self.actionDelete_all_events.triggered.connect(lambda: event_operations.delete_all_events(self))
|
|
135
133
|
self.actionDelete_selected_events.triggered.connect(lambda: event_operations.delete_selected_events(self))
|
|
136
134
|
|
|
@@ -163,14 +161,16 @@ def connections(self):
|
|
|
163
161
|
self.actionNumber_of_transitions.triggered.connect(lambda: transitions.transitions_matrix(self, "number"))
|
|
164
162
|
|
|
165
163
|
self.actionFrequencies_of_transitions_after_behaviors.triggered.connect(
|
|
166
|
-
lambda:
|
|
164
|
+
lambda: transitions.transitions_matrix(self, "frequencies_after_behaviors")
|
|
167
165
|
)
|
|
168
166
|
|
|
169
167
|
# menu playback
|
|
170
168
|
self.actionJumpTo.triggered.connect(self.jump_to)
|
|
171
169
|
self.action_deinterlace.triggered.connect(lambda: video_operations.deinterlace(self))
|
|
170
|
+
self.action_change_time_offset_of_players.triggered.connect(lambda: video_operations.change_player_offset(self))
|
|
172
171
|
self.actionZoom_level.triggered.connect(lambda: video_operations.zoom_level(self))
|
|
173
172
|
self.actionRotate_current_video.triggered.connect(lambda: video_operations.rotate_displayed_video(self))
|
|
173
|
+
|
|
174
174
|
self.actionDisplay_subtitles.triggered.connect(lambda: video_operations.display_subtitles(self))
|
|
175
175
|
self.actionVideo_equalizer.triggered.connect(lambda: video_equalizer.video_equalizer_show(self))
|
|
176
176
|
|
|
@@ -178,9 +178,7 @@ def connections(self):
|
|
|
178
178
|
self.action_block_dockwidgets.triggered.connect(self.block_dockwidgets)
|
|
179
179
|
|
|
180
180
|
self.action_create_modifiers_coding_map.triggered.connect(self.modifiers_coding_map_creator)
|
|
181
|
-
self.action_create_behaviors_coding_map.triggered.connect(
|
|
182
|
-
lambda: behav_coding_map_creator.behaviors_coding_map_creator(self)
|
|
183
|
-
)
|
|
181
|
+
self.action_create_behaviors_coding_map.triggered.connect(lambda: behav_coding_map_creator.behaviors_coding_map_creator(self))
|
|
184
182
|
|
|
185
183
|
self.actionShow_spectrogram.triggered.connect(lambda: self.show_plot_widget("spectrogram", warning=True))
|
|
186
184
|
self.actionShow_the_sound_waveform.triggered.connect(lambda: self.show_plot_widget("waveform", warning=True))
|
|
@@ -198,24 +196,22 @@ def connections(self):
|
|
|
198
196
|
self.actionAdd_image_overlay_on_video.triggered.connect(lambda: image_overlay.add_image_overlay(self))
|
|
199
197
|
self.actionRemove_image_overlay.triggered.connect(lambda: image_overlay.remove_image_overlay(self))
|
|
200
198
|
|
|
199
|
+
self.actionMedia_file_information_2.triggered.connect(lambda: media_file.get_info(self))
|
|
201
200
|
self.actionRecode_resize_video.triggered.connect(lambda: external_processes.ffmpeg_process(self, "reencode_resize"))
|
|
202
201
|
self.actionRotate_video.triggered.connect(lambda: external_processes.ffmpeg_process(self, "rotate"))
|
|
203
202
|
self.actionMerge_media_files.triggered.connect(lambda: external_processes.ffmpeg_process(self, "merge"))
|
|
204
|
-
self.
|
|
203
|
+
self.actionCreate_video_spectrogram.triggered.connect(lambda: external_processes.ffmpeg_process(self, "video_spectrogram"))
|
|
205
204
|
|
|
206
205
|
self.actionCreate_transitions_flow_diagram.triggered.connect(transitions.transitions_dot_script)
|
|
207
206
|
self.actionCreate_transitions_flow_diagram_2.triggered.connect(transitions.transitions_flow_diagram)
|
|
208
207
|
|
|
209
208
|
# menu Analysis
|
|
209
|
+
|
|
210
210
|
self.actionTime_budget.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_behavior"))
|
|
211
|
-
self.actionTime_budget_by_behaviors_category.triggered.connect(
|
|
212
|
-
lambda: time_budget_widget.time_budget(self, mode="by_category")
|
|
213
|
-
)
|
|
211
|
+
self.actionTime_budget_by_behaviors_category.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_category"))
|
|
214
212
|
|
|
215
213
|
self.actionTime_budget_report.triggered.connect(lambda: synthetic_time_budget.synthetic_time_budget(self))
|
|
216
|
-
self.actionSynthetic_binned_time_budget.triggered.connect(
|
|
217
|
-
lambda: synthetic_time_budget.synthetic_binned_time_budget(self)
|
|
218
|
-
)
|
|
214
|
+
self.actionSynthetic_binned_time_budget.triggered.connect(lambda: synthetic_time_budget.synthetic_binned_time_budget(self))
|
|
219
215
|
|
|
220
216
|
self.actionBehavior_bar_plot.triggered.connect(lambda: self.behaviors_bar_plot(mode="list"))
|
|
221
217
|
self.actionBehavior_bar_plot.setVisible(True)
|
|
@@ -229,6 +225,8 @@ def connections(self):
|
|
|
229
225
|
|
|
230
226
|
self.action_latency.triggered.connect(lambda: latency.get_latency(self))
|
|
231
227
|
|
|
228
|
+
self.action_cooccurence.triggered.connect(lambda: cooccurence.get_cooccurence(self))
|
|
229
|
+
|
|
232
230
|
# menu Help
|
|
233
231
|
self.actionUser_guide.triggered.connect(self.actionUser_guide_triggered)
|
|
234
232
|
self.actionAbout.triggered.connect(lambda: about.actionAbout_activated(self))
|
|
@@ -254,9 +252,7 @@ def connections(self):
|
|
|
254
252
|
self.actionFrame_forward.triggered.connect(self.next_frame)
|
|
255
253
|
|
|
256
254
|
self.actionCloseObs.triggered.connect(lambda: observation_operations.close_observation(self))
|
|
257
|
-
self.actionCurrent_Time_Budget.triggered.connect(
|
|
258
|
-
lambda: time_budget_widget.time_budget(self, mode="by_behavior", mode2="current")
|
|
259
|
-
)
|
|
255
|
+
self.actionCurrent_Time_Budget.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_behavior", mode2="current"))
|
|
260
256
|
self.actionPlot_current_observation.triggered.connect(lambda: self.plot_events_triggered(mode="current"))
|
|
261
257
|
|
|
262
258
|
self.actionPlot_current_time_budget.triggered.connect(lambda: self.behaviors_bar_plot(mode="current"))
|
|
@@ -264,10 +260,13 @@ def connections(self):
|
|
|
264
260
|
self.actionFind_in_current_obs.triggered.connect(lambda: event_operations.find_events(self))
|
|
265
261
|
|
|
266
262
|
# table Widget double click
|
|
267
|
-
self.twEvents.itemDoubleClicked.connect(self.twEvents_doubleClicked)
|
|
263
|
+
# self.twEvents.itemDoubleClicked.connect(self.twEvents_doubleClicked)
|
|
268
264
|
self.twEthogram.itemDoubleClicked.connect(self.twEthogram_doubleClicked)
|
|
269
265
|
self.twSubjects.itemDoubleClicked.connect(self.twSubjects_doubleClicked)
|
|
270
266
|
|
|
267
|
+
# events tableview
|
|
268
|
+
self.tv_events.doubleClicked.connect(self.tv_events_doubleClicked)
|
|
269
|
+
|
|
271
270
|
# Actions for twEthogram context menu
|
|
272
271
|
self.twEthogram.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
273
272
|
self.twEthogram.horizontalHeader().sortIndicatorChanged.connect(self.twEthogram_sorted)
|
|
@@ -275,7 +274,9 @@ def connections(self):
|
|
|
275
274
|
self.actionViewBehavior.triggered.connect(self.view_behavior)
|
|
276
275
|
self.twEthogram.addAction(self.actionViewBehavior)
|
|
277
276
|
|
|
278
|
-
self.actionFilterBehaviors.triggered.connect(
|
|
277
|
+
self.actionFilterBehaviors.triggered.connect(
|
|
278
|
+
lambda: self.filter_behaviors(table=cfg.ETHOGRAM, behavior_type=cfg.STATE_EVENT_TYPES + cfg.POINT_EVENT_TYPES)
|
|
279
|
+
)
|
|
279
280
|
self.twEthogram.addAction(self.actionFilterBehaviors)
|
|
280
281
|
|
|
281
282
|
self.actionShowAllBehaviors.triggered.connect(self.show_all_behaviors)
|
|
@@ -291,49 +292,95 @@ def connections(self):
|
|
|
291
292
|
self.twSubjects.addAction(self.actionShowAllSubjects)
|
|
292
293
|
|
|
293
294
|
# actions for twEvents horizontal header menu
|
|
294
|
-
tw_headers = self.twEvents.horizontalHeader()
|
|
295
|
-
tw_headers.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
296
|
-
tw_headers.addAction(self.actionConfigure_twEvents_columns)
|
|
295
|
+
# tw_headers = self.twEvents.horizontalHeader()
|
|
296
|
+
# tw_headers.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
297
|
+
# tw_headers.addAction(self.actionConfigure_twEvents_columns)
|
|
298
|
+
|
|
299
|
+
tv_headers = self.tv_events.horizontalHeader()
|
|
300
|
+
tv_headers.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
301
|
+
tv_headers.addAction(self.actionConfigure_tvevents_columns)
|
|
297
302
|
|
|
298
303
|
# Actions for twEvents menu
|
|
299
|
-
self.twEvents.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
304
|
+
# self.twEvents.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
305
|
+
|
|
306
|
+
# self.twEvents.addAction(self.actionAdd_event)
|
|
307
|
+
# self.twEvents.addAction(self.actionEdit_selected_events)
|
|
308
|
+
# self.twEvents.addAction(self.actionEdit_event_time)
|
|
309
|
+
|
|
310
|
+
# self.twEvents.addAction(self.actionCopy_events)
|
|
311
|
+
# self.twEvents.addAction(self.actionPaste_events)
|
|
312
|
+
|
|
313
|
+
# separator2 = QAction(self)
|
|
314
|
+
# separator2.setSeparator(True)
|
|
315
|
+
# self.twEvents.addAction(separator2)
|
|
316
|
+
|
|
317
|
+
# self.twEvents.addAction(self.actionFind_events)
|
|
318
|
+
# self.twEvents.addAction(self.actionFind_replace_events)
|
|
319
|
+
|
|
320
|
+
# separator2 = QAction(self)
|
|
321
|
+
# separator2.setSeparator(True)
|
|
322
|
+
# self.twEvents.addAction(separator2)
|
|
323
|
+
|
|
324
|
+
# self.twEvents.addAction(self.actionFilter_events)
|
|
325
|
+
# self.twEvents.addAction(self.actionShow_all_events)
|
|
300
326
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
self.twEvents.addAction(
|
|
327
|
+
# separator2 = QAction(self)
|
|
328
|
+
# separator2.setSeparator(True)
|
|
329
|
+
# self.twEvents.addAction(separator2)
|
|
304
330
|
|
|
305
|
-
self.twEvents.addAction(self.
|
|
306
|
-
self.twEvents.addAction(self.
|
|
331
|
+
# self.twEvents.addAction(self.actionCheckStateEventsSingleObs)
|
|
332
|
+
# self.twEvents.addAction(self.actionClose_unpaired_events)
|
|
333
|
+
|
|
334
|
+
# self.twEvents.addAction(self.actionRunEventOutside)
|
|
335
|
+
|
|
336
|
+
# separator2 = QAction(self)
|
|
337
|
+
# separator2.setSeparator(True)
|
|
338
|
+
# self.twEvents.addAction(separator2)
|
|
339
|
+
|
|
340
|
+
# self.twEvents.addAction(self.actionDelete_selected_events)
|
|
341
|
+
|
|
342
|
+
# Actions for tv_events menu
|
|
343
|
+
self.tv_events.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
344
|
+
|
|
345
|
+
self.tv_events.addAction(self.actionAdd_event)
|
|
346
|
+
self.tv_events.addAction(self.actionEdit_selected_events)
|
|
347
|
+
self.tv_events.addAction(self.action_add_comment)
|
|
348
|
+
|
|
349
|
+
self.tv_events.addAction(self.actionEdit_event_time)
|
|
350
|
+
|
|
351
|
+
self.tv_events.addAction(self.actionCopy_events)
|
|
352
|
+
self.tv_events.addAction(self.actionPaste_events)
|
|
307
353
|
|
|
308
354
|
separator2 = QAction(self)
|
|
309
355
|
separator2.setSeparator(True)
|
|
310
|
-
self.
|
|
356
|
+
self.tv_events.addAction(separator2)
|
|
311
357
|
|
|
312
|
-
self.
|
|
313
|
-
self.
|
|
358
|
+
self.tv_events.addAction(self.actionFind_events)
|
|
359
|
+
self.tv_events.addAction(self.actionFind_replace_events)
|
|
314
360
|
|
|
315
361
|
separator2 = QAction(self)
|
|
316
362
|
separator2.setSeparator(True)
|
|
317
|
-
self.
|
|
363
|
+
self.tv_events.addAction(separator2)
|
|
318
364
|
|
|
319
|
-
self.
|
|
320
|
-
self.
|
|
365
|
+
self.tv_events.addAction(self.actionFilter_events)
|
|
366
|
+
self.tv_events.addAction(self.actionShow_all_events)
|
|
321
367
|
|
|
322
368
|
separator2 = QAction(self)
|
|
323
369
|
separator2.setSeparator(True)
|
|
324
|
-
self.
|
|
370
|
+
self.tv_events.addAction(separator2)
|
|
371
|
+
|
|
372
|
+
self.tv_events.addAction(self.actionCheckStateEventsSingleObs)
|
|
373
|
+
self.tv_events.addAction(self.actionClose_unpaired_events)
|
|
325
374
|
|
|
326
|
-
self.
|
|
327
|
-
self.twEvents.addAction(self.actionClose_unpaired_events)
|
|
375
|
+
self.tv_events.addAction(self.actionAdd_frame_indexes)
|
|
328
376
|
|
|
329
|
-
self.
|
|
377
|
+
self.tv_events.addAction(self.actionRunEventOutside)
|
|
330
378
|
|
|
331
379
|
separator2 = QAction(self)
|
|
332
380
|
separator2.setSeparator(True)
|
|
333
|
-
self.
|
|
381
|
+
self.tv_events.addAction(separator2)
|
|
334
382
|
|
|
335
|
-
self.
|
|
336
|
-
# self.twEvents.addAction(self.actionDelete_all_events)
|
|
383
|
+
self.tv_events.addAction(self.actionDelete_selected_events)
|
|
337
384
|
|
|
338
385
|
# Actions for twSubjects context menu
|
|
339
386
|
self.actionDeselectCurrentSubject.triggered.connect(lambda: self.update_subject(""))
|
boris/converters.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -22,14 +22,10 @@ This file is part of BORIS.
|
|
|
22
22
|
|
|
23
23
|
import sys
|
|
24
24
|
import json
|
|
25
|
-
import urllib.error
|
|
26
|
-
import urllib.parse
|
|
27
25
|
import urllib.request
|
|
28
26
|
|
|
29
27
|
|
|
30
|
-
from
|
|
31
|
-
from PyQt5.QtGui import *
|
|
32
|
-
from PyQt5.QtWidgets import *
|
|
28
|
+
from PySide6.QtWidgets import QMessageBox, QTableWidgetItem, QFileDialog, QInputDialog, QLineEdit
|
|
33
29
|
|
|
34
30
|
from . import dialog
|
|
35
31
|
from . import config as cfg
|
|
@@ -113,12 +109,8 @@ def modify_converter(self):
|
|
|
113
109
|
w.setEnabled(False)
|
|
114
110
|
|
|
115
111
|
self.le_converter_name.setText(self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 0).text())
|
|
116
|
-
self.le_converter_description.setText(
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
self.pteCode.setPlainText(
|
|
120
|
-
self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 2).text().replace("@", "\n")
|
|
121
|
-
)
|
|
112
|
+
self.le_converter_description.setText(self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 1).text())
|
|
113
|
+
self.pteCode.setPlainText(self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 2).text().replace("@", "\n"))
|
|
122
114
|
|
|
123
115
|
self.row_in_modification = self.tw_converters.selectedIndexes()[0].row()
|
|
124
116
|
|
|
@@ -153,9 +145,7 @@ def save_converter(self):
|
|
|
153
145
|
return
|
|
154
146
|
|
|
155
147
|
if not self.le_converter_name.text().replace("_", "a").isalnum():
|
|
156
|
-
QMessageBox.critical(
|
|
157
|
-
self, "BORIS", "Forbidden characters are used in converter name.<br>Use a..z, A..Z, 0..9 _"
|
|
158
|
-
)
|
|
148
|
+
QMessageBox.critical(self, "BORIS", "Forbidden characters are used in converter name.<br>Use a..z, A..Z, 0..9 _")
|
|
159
149
|
return
|
|
160
150
|
|
|
161
151
|
# test code with exec
|
|
@@ -164,7 +154,7 @@ def save_converter(self):
|
|
|
164
154
|
QMessageBox.critical(self, "BORIS", "The converter must have Python code")
|
|
165
155
|
return
|
|
166
156
|
|
|
167
|
-
function = code_2_func(self, self.le_converter_name.text(), code)
|
|
157
|
+
function = code_2_func(self, name=self.le_converter_name.text(), code=code)
|
|
168
158
|
|
|
169
159
|
try:
|
|
170
160
|
exec(function)
|
|
@@ -247,8 +237,7 @@ def load_converters_from_file_repo(self, mode: str):
|
|
|
247
237
|
|
|
248
238
|
converters_from_file = {}
|
|
249
239
|
if mode == "file":
|
|
250
|
-
|
|
251
|
-
file_name = fn[0] if type(fn) is tuple else fn
|
|
240
|
+
file_name, _ = QFileDialog.getOpenFileName(self, "Load converters from file", "", "All files (*)")
|
|
252
241
|
|
|
253
242
|
if file_name:
|
|
254
243
|
with open(file_name, "r") as f_in:
|
|
@@ -259,35 +248,25 @@ def load_converters_from_file_repo(self, mode: str):
|
|
|
259
248
|
return
|
|
260
249
|
|
|
261
250
|
if mode == "repo":
|
|
262
|
-
|
|
263
|
-
converters_repo_URL = "http://www.boris.unito.it/archive/converters.json"
|
|
251
|
+
converters_repo_URL = "https://www.boris.unito.it/static/converters.json"
|
|
264
252
|
try:
|
|
265
253
|
converters_from_repo = urllib.request.urlopen(converters_repo_URL).read().strip().decode("utf-8")
|
|
266
254
|
except Exception:
|
|
267
|
-
QMessageBox.critical(
|
|
268
|
-
self, cfg.programName, "An error occured during retrieving converters from BORIS remote repository"
|
|
269
|
-
)
|
|
255
|
+
QMessageBox.critical(self, cfg.programName, "An error occured during retrieving converters from BORIS remote repository")
|
|
270
256
|
return
|
|
271
257
|
|
|
272
258
|
try:
|
|
273
259
|
converters_from_file = eval(converters_from_repo)["BORIS converters"]
|
|
274
260
|
except Exception:
|
|
275
|
-
QMessageBox.critical(
|
|
276
|
-
self, cfg.programName, "An error occured during retrieving converters from BORIS remote repository"
|
|
277
|
-
)
|
|
261
|
+
QMessageBox.critical(self, cfg.programName, "An error occured during retrieving converters from BORIS remote repository")
|
|
278
262
|
return
|
|
279
263
|
|
|
280
264
|
if converters_from_file:
|
|
281
|
-
|
|
282
|
-
diag_choose_conv = dialog.ChooseObservationsToImport(
|
|
283
|
-
"Choose the converters to load:", sorted(list(converters_from_file.keys()))
|
|
284
|
-
)
|
|
265
|
+
diag_choose_conv = dialog.ChooseObservationsToImport("Choose the converters to load:", sorted(list(converters_from_file.keys())))
|
|
285
266
|
|
|
286
267
|
if diag_choose_conv.exec_():
|
|
287
|
-
|
|
288
268
|
selected_converters = diag_choose_conv.get_selected_observations()
|
|
289
269
|
if selected_converters:
|
|
290
|
-
|
|
291
270
|
# extract converter names from table
|
|
292
271
|
converter_names = []
|
|
293
272
|
for row in range(self.tw_converters.rowCount()):
|
|
@@ -325,7 +304,7 @@ def load_converters_from_file_repo(self, mode: str):
|
|
|
325
304
|
else:
|
|
326
305
|
continue
|
|
327
306
|
# test if code does not produce error
|
|
328
|
-
function =
|
|
307
|
+
function = code_2_func(self, name=converter_name, code=converters_from_file[converter]["code"])
|
|
329
308
|
|
|
330
309
|
try:
|
|
331
310
|
exec(function)
|
|
@@ -333,10 +312,7 @@ def load_converters_from_file_repo(self, mode: str):
|
|
|
333
312
|
QMessageBox.critical(
|
|
334
313
|
self,
|
|
335
314
|
"BORIS",
|
|
336
|
-
(
|
|
337
|
-
f"The code of {converter_name} converter produces an error: "
|
|
338
|
-
f"<br><b>{sys.exc_info()[1]}</b>"
|
|
339
|
-
),
|
|
315
|
+
(f"The code of {converter_name} converter produces an error: <br><b>{sys.exc_info()[1]}</b>"),
|
|
340
316
|
)
|
|
341
317
|
|
|
342
318
|
self.tw_converters.setRowCount(self.tw_converters.rowCount() + 1)
|