boris-behav-obs 8.16.6__py3-none-any.whl → 9.7.1__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.
Files changed (125) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +24 -40
  4. boris/add_modifier.py +88 -80
  5. boris/add_modifier_ui.py +235 -131
  6. boris/advanced_event_filtering.py +23 -29
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +228 -229
  18. boris/behavior_binary_table.py +33 -50
  19. boris/behaviors_coding_map.py +17 -18
  20. boris/boris_cli.py +6 -25
  21. boris/cmd_arguments.py +12 -1
  22. boris/coding_pad.py +16 -34
  23. boris/config.py +101 -49
  24. boris/config_file.py +55 -64
  25. boris/connections.py +105 -58
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2108 -1275
  30. boris/core_qrc.py +15892 -10829
  31. boris/core_ui.py +941 -806
  32. boris/db_functions.py +17 -42
  33. boris/dev.py +134 -0
  34. boris/dialog.py +461 -242
  35. boris/duration_widget.py +9 -14
  36. boris/edit_event.py +61 -31
  37. boris/edit_event_ui.py +208 -97
  38. boris/event_operations.py +405 -281
  39. boris/events_cursor.py +25 -17
  40. boris/events_snapshots.py +36 -82
  41. boris/exclusion_matrix.py +4 -9
  42. boris/export_events.py +180 -203
  43. boris/export_observation.py +60 -73
  44. boris/external_processes.py +123 -98
  45. boris/geometric_measurement.py +427 -218
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +4 -4
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +304 -0
  50. boris/irr.py +20 -57
  51. boris/latency.py +31 -24
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +17 -19
  54. boris/menu_options.py +16 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv2.py +127 -36
  58. boris/observation.py +493 -210
  59. boris/observation_operations.py +1010 -391
  60. boris/observation_ui.py +573 -363
  61. boris/observations_list.py +51 -58
  62. boris/otx_parser.py +74 -68
  63. boris/param_panel.py +45 -59
  64. boris/param_panel_ui.py +254 -138
  65. boris/player_dock_widget.py +91 -56
  66. boris/plot_data_module.py +18 -53
  67. boris/plot_events.py +56 -153
  68. boris/plot_events_rt.py +16 -30
  69. boris/plot_spectrogram_rt.py +80 -56
  70. boris/plot_waveform_rt.py +23 -48
  71. boris/plugins.py +431 -0
  72. boris/portion/__init__.py +18 -8
  73. boris/portion/const.py +35 -18
  74. boris/portion/dict.py +5 -5
  75. boris/portion/func.py +2 -2
  76. boris/portion/interval.py +21 -41
  77. boris/portion/io.py +41 -32
  78. boris/preferences.py +298 -123
  79. boris/preferences_ui.py +664 -225
  80. boris/project.py +293 -270
  81. boris/project_functions.py +610 -537
  82. boris/project_import_export.py +204 -213
  83. boris/project_ui.py +673 -441
  84. boris/qrc_boris.py +6 -3
  85. boris/qrc_boris5.py +6 -3
  86. boris/select_modifiers.py +62 -90
  87. boris/select_observations.py +19 -197
  88. boris/select_subj_behav.py +67 -39
  89. boris/state_events.py +51 -33
  90. boris/subjects_pad.py +6 -8
  91. boris/synthetic_time_budget.py +25 -17
  92. boris/time_budget_functions.py +169 -169
  93. boris/time_budget_widget.py +71 -86
  94. boris/transitions.py +41 -41
  95. boris/utilities.py +562 -222
  96. boris/version.py +3 -3
  97. boris/video_equalizer.py +16 -14
  98. boris/video_equalizer_ui.py +199 -130
  99. boris/video_operations.py +78 -28
  100. boris/view_df.py +104 -0
  101. boris/view_df_ui.py +75 -0
  102. boris/write_event.py +240 -136
  103. boris_behav_obs-9.7.1.dist-info/METADATA +140 -0
  104. boris_behav_obs-9.7.1.dist-info/RECORD +109 -0
  105. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.1.dist-info}/WHEEL +1 -1
  106. boris_behav_obs-9.7.1.dist-info/entry_points.txt +2 -0
  107. boris/README.TXT +0 -22
  108. boris/add_modifier.ui +0 -323
  109. boris/converters.ui +0 -289
  110. boris/core.qrc +0 -37
  111. boris/core.ui +0 -1571
  112. boris/edit_event.ui +0 -233
  113. boris/icons/logo_eye.ico +0 -0
  114. boris/map_creator.py +0 -982
  115. boris/observation.ui +0 -814
  116. boris/param_panel.ui +0 -379
  117. boris/preferences.ui +0 -537
  118. boris/project.ui +0 -1074
  119. boris/vlc_local.py +0 -90
  120. boris_behav_obs-8.16.6.dist-info/LICENSE.TXT +0 -674
  121. boris_behav_obs-8.16.6.dist-info/METADATA +0 -134
  122. boris_behav_obs-8.16.6.dist-info/RECORD +0 -106
  123. boris_behav_obs-8.16.6.dist-info/entry_points.txt +0 -2
  124. {boris → boris_behav_obs-9.7.1.dist-info/licenses}/LICENSE.TXT +0 -0
  125. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.1.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-2023 Olivier Friard
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 PyQt5.QtCore import Qt, QTimer
23
- from PyQt5.QtWidgets import QAction
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
- self.actionExport_observations_list.triggered.connect(
113
- lambda: observation_operations.export_observations_list_clicked(self)
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(self.find_replace_events)
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: self.transitions_matrix("frequencies_after_behaviors")
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.actionMedia_file_information_2.triggered.connect(lambda: media_file.get_info(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(lambda: self.filter_behaviors(table=cfg.ETHOGRAM))
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
- self.twEvents.addAction(self.actionAdd_event)
302
- self.twEvents.addAction(self.actionEdit_selected_events)
303
- self.twEvents.addAction(self.actionEdit_event_time)
327
+ # separator2 = QAction(self)
328
+ # separator2.setSeparator(True)
329
+ # self.twEvents.addAction(separator2)
304
330
 
305
- self.twEvents.addAction(self.actionCopy_events)
306
- self.twEvents.addAction(self.actionPaste_events)
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.twEvents.addAction(separator2)
356
+ self.tv_events.addAction(separator2)
311
357
 
312
- self.twEvents.addAction(self.actionFind_events)
313
- self.twEvents.addAction(self.actionFind_replace_events)
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.twEvents.addAction(separator2)
363
+ self.tv_events.addAction(separator2)
318
364
 
319
- self.twEvents.addAction(self.actionFilter_events)
320
- self.twEvents.addAction(self.actionShow_all_events)
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.twEvents.addAction(separator2)
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.twEvents.addAction(self.actionCheckStateEventsSingleObs)
327
- self.twEvents.addAction(self.actionClose_unpaired_events)
375
+ self.tv_events.addAction(self.actionAdd_frame_indexes)
328
376
 
329
- self.twEvents.addAction(self.actionRunEventOutside)
377
+ self.tv_events.addAction(self.actionRunEventOutside)
330
378
 
331
379
  separator2 = QAction(self)
332
380
  separator2.setSeparator(True)
333
- self.twEvents.addAction(separator2)
381
+ self.tv_events.addAction(separator2)
334
382
 
335
- self.twEvents.addAction(self.actionDelete_selected_events)
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-2023 Olivier Friard
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 PyQt5.QtCore import *
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
- self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 1).text()
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
- fn = QFileDialog(self).getOpenFileName(self, "Load converters from file", "", "All files (*)")
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 = self.code_2_func(converter_name, converters_from_file[converter]["code"])
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)