boris-behav-obs 8.12__py3-none-any.whl → 9.7.6__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.

Potentially problematic release.


This version of boris-behav-obs might be problematic. Click here for more details.

Files changed (128) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +28 -39
  4. boris/add_modifier.py +122 -109
  5. boris/add_modifier_ui.py +239 -135
  6. boris/advanced_event_filtering.py +81 -45
  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 +42 -49
  23. boris/config.py +141 -65
  24. boris/config_file.py +58 -67
  25. boris/connections.py +107 -61
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2373 -1786
  30. boris/core_qrc.py +15895 -10743
  31. boris/core_ui.py +943 -798
  32. boris/db_functions.py +17 -42
  33. boris/dev.py +109 -8
  34. boris/dialog.py +482 -236
  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 +408 -293
  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 +184 -223
  43. boris/export_observation.py +74 -100
  44. boris/external_processes.py +123 -98
  45. boris/geometric_measurement.py +644 -290
  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 +325 -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 +17 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv.py +1 -0
  58. boris/mpv2.py +732 -705
  59. boris/observation.py +533 -221
  60. boris/observation_operations.py +1025 -390
  61. boris/observation_ui.py +572 -362
  62. boris/observations_list.py +71 -53
  63. boris/otx_parser.py +74 -68
  64. boris/param_panel.py +31 -16
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +90 -60
  67. boris/plot_data_module.py +25 -33
  68. boris/plot_events.py +127 -90
  69. boris/plot_events_rt.py +17 -31
  70. boris/plot_spectrogram_rt.py +95 -30
  71. boris/plot_waveform_rt.py +32 -21
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +18 -8
  74. boris/portion/const.py +35 -18
  75. boris/portion/dict.py +5 -5
  76. boris/portion/func.py +2 -2
  77. boris/portion/interval.py +21 -41
  78. boris/portion/io.py +41 -32
  79. boris/preferences.py +306 -83
  80. boris/preferences_ui.py +684 -227
  81. boris/project.py +448 -293
  82. boris/project_functions.py +671 -238
  83. boris/project_import_export.py +213 -222
  84. boris/project_ui.py +674 -438
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +74 -48
  88. boris/select_observations.py +20 -198
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +52 -35
  91. boris/subjects_pad.py +6 -9
  92. boris/synthetic_time_budget.py +45 -28
  93. boris/time_budget_functions.py +171 -171
  94. boris/time_budget_widget.py +84 -114
  95. boris/transitions.py +41 -47
  96. boris/utilities.py +627 -236
  97. boris/version.py +3 -3
  98. boris/video_equalizer.py +16 -14
  99. boris/video_equalizer_ui.py +199 -130
  100. boris/video_operations.py +95 -29
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
  106. {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/converters.ui +0 -289
  111. boris/core.qrc +0 -36
  112. boris/core.ui +0 -1556
  113. boris/edit_event.ui +0 -233
  114. boris/icons/logo_eye.ico +0 -0
  115. boris/map_creator.py +0 -850
  116. boris/observation.ui +0 -814
  117. boris/param_panel.ui +0 -379
  118. boris/preferences.ui +0 -537
  119. boris/project.ui +0 -1069
  120. boris/project_server.py +0 -236
  121. boris/vlc.py +0 -10343
  122. boris/vlc_local.py +0 -90
  123. boris_behav_obs-8.12.dist-info/LICENSE.TXT +0 -674
  124. boris_behav_obs-8.12.dist-info/METADATA +0 -128
  125. boris_behav_obs-8.12.dist-info/RECORD +0 -108
  126. boris_behav_obs-8.12.dist-info/entry_points.txt +0 -3
  127. {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
  128. {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.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,11 +37,11 @@ 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,
43
44
  project_import_export,
44
- project_server,
45
45
  synthetic_time_budget,
46
46
  time_budget_widget,
47
47
  transitions,
@@ -70,16 +70,12 @@ def connections(self):
70
70
  self.actionExport_project.triggered.connect(lambda: project_import_export.export_project_as_pickle_object(self.pj))
71
71
  self.actionClose_project.triggered.connect(self.close_project)
72
72
 
73
- self.action_media_file_and_images_directories_relative_path.triggered.connect(
74
- self.set_media_files_path_relative_to_project_dir
75
- )
73
+ self.action_media_file_and_images_directories_relative_path.triggered.connect(self.set_media_files_path_relative_to_project_dir)
76
74
  self.action_data_files_relative_path.triggered.connect(self.set_data_files_path_relative_to_project_dir)
77
75
 
78
76
  self.action_remove_media_files_and_images_directories_path.triggered.connect(self.remove_media_files_path)
79
77
  self.action_remove_data_files_path.triggered.connect(self.remove_data_files_path)
80
78
 
81
- self.actionSend_project.triggered.connect(lambda: project_server.send_project_via_socket(self))
82
-
83
79
  self.menuCreate_subtitles_2.triggered.connect(self.create_subtitles)
84
80
 
85
81
  self.actionPreferences.triggered.connect(lambda: preferences.preferences(self))
@@ -87,19 +83,16 @@ def connections(self):
87
83
  self.actionQuit.triggered.connect(self.actionQuit_activated)
88
84
 
89
85
  # menu observations
90
- self.actionNew_observation.triggered.connect(
91
- lambda: observation_operations.new_observation(self, mode=cfg.NEW, obsId="")
92
- )
86
+ self.actionNew_observation.triggered.connect(lambda: observation_operations.new_observation(self, mode=cfg.NEW, obsId=""))
93
87
 
94
- self.actionOpen_observation.triggered.connect(
95
- lambda: observation_operations.open_observation(self, mode=cfg.OBS_START)
96
- )
88
+ self.actionOpen_observation.triggered.connect(lambda: observation_operations.open_observation(self, mode=cfg.OBS_START))
97
89
  self.actionView_observation.triggered.connect(lambda: observation_operations.open_observation(self, mode=cfg.VIEW))
98
90
  self.actionEdit_observation_2.triggered.connect(lambda: observation_operations.edit_observation(self))
99
91
  self.actionObservationsList.triggered.connect(lambda: observation_operations.observations_list(self))
100
92
 
101
93
  self.actionClose_observation.triggered.connect(lambda: observation_operations.close_observation(self))
102
94
 
95
+ self.action_create_observations.triggered.connect(lambda: observation_operations.create_observations(self))
103
96
  self.actionRemove_observations.triggered.connect(lambda: observation_operations.remove_observations(self))
104
97
 
105
98
  self.actionAdd_event.triggered.connect(lambda: event_operations.add_event(self))
@@ -110,22 +103,24 @@ def connections(self):
110
103
  self.actionShow_all_events.triggered.connect(lambda: event_operations.show_all_events(self))
111
104
 
112
105
  # twevent header
113
- self.actionConfigure_twEvents_columns.triggered.connect(self.configure_twevents_columns)
106
+ # self.actionConfigure_twEvents_columns.triggered.connect(self.configure_twevents_columns)
114
107
 
115
- self.actionExport_observations_list.triggered.connect(
116
- lambda: observation_operations.export_observations_list_clicked(self)
117
- )
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))
118
112
 
119
113
  self.actionCheckStateEvents.triggered.connect(lambda: state_events.check_state_events(self, mode="all"))
120
- self.actionCheckStateEventsSingleObs.triggered.connect(
121
- lambda: state_events.check_state_events(self, mode="current")
122
- )
114
+ self.actionCheckStateEventsSingleObs.triggered.connect(lambda: state_events.check_state_events(self, mode="current"))
123
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
+
124
118
  self.actionRunEventOutside.triggered.connect(self.run_event_outside)
125
119
 
126
120
  self.actionSelect_observations.triggered.connect(lambda: event_operations.select_events_between_activated(self))
127
121
 
128
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))
129
124
  self.actionEdit_event_time.triggered.connect(lambda: event_operations.edit_time_selected_events(self))
130
125
 
131
126
  self.actionCopy_events.triggered.connect(lambda: event_operations.copy_selected_events(self))
@@ -133,7 +128,7 @@ def connections(self):
133
128
 
134
129
  self.actionExplore_project.triggered.connect(lambda: project_functions.explore_project(self))
135
130
  self.actionFind_events.triggered.connect(lambda: event_operations.find_events(self))
136
- self.actionFind_replace_events.triggered.connect(self.find_replace_events)
131
+ self.actionFind_replace_events.triggered.connect(lambda: event_operations.find_replace_events(self))
137
132
  self.actionDelete_all_events.triggered.connect(lambda: event_operations.delete_all_events(self))
138
133
  self.actionDelete_selected_events.triggered.connect(lambda: event_operations.delete_selected_events(self))
139
134
 
@@ -166,13 +161,16 @@ def connections(self):
166
161
  self.actionNumber_of_transitions.triggered.connect(lambda: transitions.transitions_matrix(self, "number"))
167
162
 
168
163
  self.actionFrequencies_of_transitions_after_behaviors.triggered.connect(
169
- lambda: self.transitions_matrix("frequencies_after_behaviors")
164
+ lambda: transitions.transitions_matrix(self, "frequencies_after_behaviors")
170
165
  )
171
166
 
172
167
  # menu playback
173
168
  self.actionJumpTo.triggered.connect(self.jump_to)
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))
174
171
  self.actionZoom_level.triggered.connect(lambda: video_operations.zoom_level(self))
175
172
  self.actionRotate_current_video.triggered.connect(lambda: video_operations.rotate_displayed_video(self))
173
+
176
174
  self.actionDisplay_subtitles.triggered.connect(lambda: video_operations.display_subtitles(self))
177
175
  self.actionVideo_equalizer.triggered.connect(lambda: video_equalizer.video_equalizer_show(self))
178
176
 
@@ -180,9 +178,7 @@ def connections(self):
180
178
  self.action_block_dockwidgets.triggered.connect(self.block_dockwidgets)
181
179
 
182
180
  self.action_create_modifiers_coding_map.triggered.connect(self.modifiers_coding_map_creator)
183
- self.action_create_behaviors_coding_map.triggered.connect(
184
- lambda: behav_coding_map_creator.behaviors_coding_map_creator(self)
185
- )
181
+ self.action_create_behaviors_coding_map.triggered.connect(lambda: behav_coding_map_creator.behaviors_coding_map_creator(self))
186
182
 
187
183
  self.actionShow_spectrogram.triggered.connect(lambda: self.show_plot_widget("spectrogram", warning=True))
188
184
  self.actionShow_the_sound_waveform.triggered.connect(lambda: self.show_plot_widget("waveform", warning=True))
@@ -190,6 +186,7 @@ def connections(self):
190
186
 
191
187
  self.actionShow_data_files.triggered.connect(self.show_data_files)
192
188
  self.action_geometric_measurements.triggered.connect(lambda: geometric_measurement.show_widget(self))
189
+
193
190
  self.actionBehaviors_coding_map.triggered.connect(lambda: behaviors_coding_map.show_behaviors_coding_map(self))
194
191
 
195
192
  self.actionCoding_pad.triggered.connect(lambda: coding_pad.show_coding_pad(self))
@@ -199,24 +196,22 @@ def connections(self):
199
196
  self.actionAdd_image_overlay_on_video.triggered.connect(lambda: image_overlay.add_image_overlay(self))
200
197
  self.actionRemove_image_overlay.triggered.connect(lambda: image_overlay.remove_image_overlay(self))
201
198
 
199
+ self.actionMedia_file_information_2.triggered.connect(lambda: media_file.get_info(self))
202
200
  self.actionRecode_resize_video.triggered.connect(lambda: external_processes.ffmpeg_process(self, "reencode_resize"))
203
201
  self.actionRotate_video.triggered.connect(lambda: external_processes.ffmpeg_process(self, "rotate"))
204
202
  self.actionMerge_media_files.triggered.connect(lambda: external_processes.ffmpeg_process(self, "merge"))
205
- 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"))
206
204
 
207
205
  self.actionCreate_transitions_flow_diagram.triggered.connect(transitions.transitions_dot_script)
208
206
  self.actionCreate_transitions_flow_diagram_2.triggered.connect(transitions.transitions_flow_diagram)
209
207
 
210
208
  # menu Analysis
209
+
211
210
  self.actionTime_budget.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_behavior"))
212
- self.actionTime_budget_by_behaviors_category.triggered.connect(
213
- lambda: time_budget_widget.time_budget(self, mode="by_category")
214
- )
211
+ self.actionTime_budget_by_behaviors_category.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_category"))
215
212
 
216
213
  self.actionTime_budget_report.triggered.connect(lambda: synthetic_time_budget.synthetic_time_budget(self))
217
- self.actionSynthetic_binned_time_budget.triggered.connect(
218
- lambda: synthetic_time_budget.synthetic_binned_time_budget(self)
219
- )
214
+ self.actionSynthetic_binned_time_budget.triggered.connect(lambda: synthetic_time_budget.synthetic_binned_time_budget(self))
220
215
 
221
216
  self.actionBehavior_bar_plot.triggered.connect(lambda: self.behaviors_bar_plot(mode="list"))
222
217
  self.actionBehavior_bar_plot.setVisible(True)
@@ -230,6 +225,8 @@ def connections(self):
230
225
 
231
226
  self.action_latency.triggered.connect(lambda: latency.get_latency(self))
232
227
 
228
+ self.action_cooccurence.triggered.connect(lambda: cooccurence.get_cooccurence(self))
229
+
233
230
  # menu Help
234
231
  self.actionUser_guide.triggered.connect(self.actionUser_guide_triggered)
235
232
  self.actionAbout.triggered.connect(lambda: about.actionAbout_activated(self))
@@ -255,9 +252,7 @@ def connections(self):
255
252
  self.actionFrame_forward.triggered.connect(self.next_frame)
256
253
 
257
254
  self.actionCloseObs.triggered.connect(lambda: observation_operations.close_observation(self))
258
- self.actionCurrent_Time_Budget.triggered.connect(
259
- lambda: time_budget_widget.time_budget(self, mode="by_behavior", mode2="current")
260
- )
255
+ self.actionCurrent_Time_Budget.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_behavior", mode2="current"))
261
256
  self.actionPlot_current_observation.triggered.connect(lambda: self.plot_events_triggered(mode="current"))
262
257
 
263
258
  self.actionPlot_current_time_budget.triggered.connect(lambda: self.behaviors_bar_plot(mode="current"))
@@ -265,10 +260,13 @@ def connections(self):
265
260
  self.actionFind_in_current_obs.triggered.connect(lambda: event_operations.find_events(self))
266
261
 
267
262
  # table Widget double click
268
- self.twEvents.itemDoubleClicked.connect(self.twEvents_doubleClicked)
263
+ # self.twEvents.itemDoubleClicked.connect(self.twEvents_doubleClicked)
269
264
  self.twEthogram.itemDoubleClicked.connect(self.twEthogram_doubleClicked)
270
265
  self.twSubjects.itemDoubleClicked.connect(self.twSubjects_doubleClicked)
271
266
 
267
+ # events tableview
268
+ self.tv_events.doubleClicked.connect(self.tv_events_doubleClicked)
269
+
272
270
  # Actions for twEthogram context menu
273
271
  self.twEthogram.setContextMenuPolicy(Qt.ActionsContextMenu)
274
272
  self.twEthogram.horizontalHeader().sortIndicatorChanged.connect(self.twEthogram_sorted)
@@ -276,7 +274,9 @@ def connections(self):
276
274
  self.actionViewBehavior.triggered.connect(self.view_behavior)
277
275
  self.twEthogram.addAction(self.actionViewBehavior)
278
276
 
279
- 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
+ )
280
280
  self.twEthogram.addAction(self.actionFilterBehaviors)
281
281
 
282
282
  self.actionShowAllBehaviors.triggered.connect(self.show_all_behaviors)
@@ -292,49 +292,95 @@ def connections(self):
292
292
  self.twSubjects.addAction(self.actionShowAllSubjects)
293
293
 
294
294
  # actions for twEvents horizontal header menu
295
- tw_headers = self.twEvents.horizontalHeader()
296
- tw_headers.setContextMenuPolicy(Qt.ActionsContextMenu)
297
- 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)
298
302
 
299
303
  # Actions for twEvents menu
300
- 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)
301
326
 
302
- self.twEvents.addAction(self.actionAdd_event)
303
- self.twEvents.addAction(self.actionEdit_selected_events)
304
- self.twEvents.addAction(self.actionEdit_event_time)
327
+ # separator2 = QAction(self)
328
+ # separator2.setSeparator(True)
329
+ # self.twEvents.addAction(separator2)
305
330
 
306
- self.twEvents.addAction(self.actionCopy_events)
307
- 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)
308
353
 
309
354
  separator2 = QAction(self)
310
355
  separator2.setSeparator(True)
311
- self.twEvents.addAction(separator2)
356
+ self.tv_events.addAction(separator2)
312
357
 
313
- self.twEvents.addAction(self.actionFind_events)
314
- self.twEvents.addAction(self.actionFind_replace_events)
358
+ self.tv_events.addAction(self.actionFind_events)
359
+ self.tv_events.addAction(self.actionFind_replace_events)
315
360
 
316
361
  separator2 = QAction(self)
317
362
  separator2.setSeparator(True)
318
- self.twEvents.addAction(separator2)
363
+ self.tv_events.addAction(separator2)
319
364
 
320
- self.twEvents.addAction(self.actionFilter_events)
321
- self.twEvents.addAction(self.actionShow_all_events)
365
+ self.tv_events.addAction(self.actionFilter_events)
366
+ self.tv_events.addAction(self.actionShow_all_events)
322
367
 
323
368
  separator2 = QAction(self)
324
369
  separator2.setSeparator(True)
325
- 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)
326
374
 
327
- self.twEvents.addAction(self.actionCheckStateEventsSingleObs)
328
- self.twEvents.addAction(self.actionClose_unpaired_events)
375
+ self.tv_events.addAction(self.actionAdd_frame_indexes)
329
376
 
330
- self.twEvents.addAction(self.actionRunEventOutside)
377
+ self.tv_events.addAction(self.actionRunEventOutside)
331
378
 
332
379
  separator2 = QAction(self)
333
380
  separator2.setSeparator(True)
334
- self.twEvents.addAction(separator2)
381
+ self.tv_events.addAction(separator2)
335
382
 
336
- self.twEvents.addAction(self.actionDelete_selected_events)
337
- # self.twEvents.addAction(self.actionDelete_all_events)
383
+ self.tv_events.addAction(self.actionDelete_selected_events)
338
384
 
339
385
  # Actions for twSubjects context menu
340
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)