boris-behav-obs 9.7.15__py3-none-any.whl → 9.8__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 (80) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +4 -3
  4. boris/add_modifier.py +1 -1
  5. boris/advanced_event_filtering.py +1 -1
  6. boris/analysis_plugins/irr_weighted_cohen_kappa.py +2 -2
  7. boris/behav_coding_map_creator.py +1 -1
  8. boris/behavior_binary_table.py +1 -1
  9. boris/behaviors_coding_map.py +1 -1
  10. boris/boris_cli.py +1 -1
  11. boris/cmd_arguments.py +1 -1
  12. boris/coding_pad.py +1 -1
  13. boris/config.py +3 -1
  14. boris/config_file.py +18 -19
  15. boris/connections.py +12 -13
  16. boris/converters.py +1 -1
  17. boris/cooccurence.py +1 -1
  18. boris/core.py +41 -42
  19. boris/core_qrc.py +1830 -1967
  20. boris/core_ui.py +1 -1
  21. boris/db_functions.py +5 -14
  22. boris/dialog.py +1 -1
  23. boris/edit_event.py +1 -1
  24. boris/event_operations.py +1 -1
  25. boris/events_cursor.py +1 -1
  26. boris/events_snapshots.py +84 -60
  27. boris/exclusion_matrix.py +1 -1
  28. boris/export_events.py +49 -43
  29. boris/export_observation.py +1 -1
  30. boris/external_processes.py +1 -1
  31. boris/geometric_measurement.py +1 -1
  32. boris/gui_utilities.py +1 -1
  33. boris/image_overlay.py +1 -1
  34. boris/import_observations.py +1 -1
  35. boris/ipc_mpv.py +1 -1
  36. boris/irr.py +1 -1
  37. boris/latency.py +1 -1
  38. boris/measurement_widget.py +1 -1
  39. boris/media_file.py +1 -1
  40. boris/menu_options.py +14 -12
  41. boris/modifier_coding_map_creator.py +1 -1
  42. boris/modifiers_coding_map.py +1 -1
  43. boris/observation.py +13 -14
  44. boris/observation_operations.py +1 -1
  45. boris/observations_list.py +1 -1
  46. boris/otx_parser.py +1 -1
  47. boris/param_panel.py +1 -1
  48. boris/player_dock_widget.py +1 -1
  49. boris/plot_data_module.py +1 -1
  50. boris/plot_events.py +1 -1
  51. boris/plot_events_rt.py +1 -1
  52. boris/plot_spectrogram_rt.py +2 -2
  53. boris/plot_waveform_rt.py +1 -1
  54. boris/plugins.py +1 -1
  55. boris/preferences.py +1 -1
  56. boris/project.py +1 -1
  57. boris/project_functions.py +12 -12
  58. boris/project_import_export.py +1 -1
  59. boris/select_modifiers.py +1 -1
  60. boris/select_observations.py +22 -23
  61. boris/select_subj_behav.py +4 -4
  62. boris/state_events.py +1 -1
  63. boris/subjects_pad.py +1 -1
  64. boris/synthetic_time_budget.py +1 -1
  65. boris/time_budget_functions.py +1 -1
  66. boris/time_budget_widget.py +1 -1
  67. boris/transitions.py +1 -1
  68. boris/utilities.py +1 -1
  69. boris/version.py +3 -3
  70. boris/video_equalizer.py +1 -1
  71. boris/video_operations.py +1 -1
  72. boris/view_df.py +28 -4
  73. boris/write_event.py +1 -1
  74. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.dist-info}/METADATA +2 -2
  75. boris_behav_obs-9.8.dist-info/RECORD +110 -0
  76. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.dist-info}/WHEEL +1 -1
  77. boris_behav_obs-9.7.15.dist-info/RECORD +0 -110
  78. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.dist-info}/entry_points.txt +0 -0
  79. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.dist-info}/licenses/LICENSE.TXT +0 -0
  80. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.dist-info}/top_level.txt +0 -0
boris/core_ui.py CHANGED
@@ -912,7 +912,7 @@ class Ui_MainWindow(object):
912
912
  self.actionEdit_selected_events.setText(QCoreApplication.translate("MainWindow", u"Edit selected event(s)", None))
913
913
  self.actionShow_spectrogram.setText(QCoreApplication.translate("MainWindow", u"Show the sound spectrogram", None))
914
914
  self.actionExport_events_as_Praat_TextGrid.setText(QCoreApplication.translate("MainWindow", u"as Praat TextGrid", None))
915
- self.actionExtract_events_from_media_files.setText(QCoreApplication.translate("MainWindow", u"Extract sequences from media files", None))
915
+ self.actionExtract_events_from_media_files.setText(QCoreApplication.translate("MainWindow", u"Extract clips from media files", None))
916
916
  self.action_geometric_measurements.setText(QCoreApplication.translate("MainWindow", u"Geometric measurement", None))
917
917
  self.actionFrame_forward.setText(QCoreApplication.translate("MainWindow", u"Frame forward", None))
918
918
  self.actionFrame_backward.setText(QCoreApplication.translate("MainWindow", u"frame backward", None))
boris/db_functions.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
6
 
7
7
  This program is free software; you can redistribute it and/or modify
@@ -21,12 +21,12 @@ Copyright 2012-2025 Olivier Friard
21
21
 
22
22
  """
23
23
 
24
- import sqlite3
25
24
  import logging
25
+ import sqlite3
26
26
  from typing import Optional, Tuple
27
+
27
28
  from . import config as cfg
28
- from . import project_functions
29
- from . import event_operations
29
+ from . import event_operations, project_functions
30
30
 
31
31
 
32
32
  def load_events_in_db(
@@ -39,6 +39,7 @@ def load_events_in_db(
39
39
  """
40
40
  populate a memory sqlite database with events from selected_observations,
41
41
  selected_subjects and selected_behaviors
42
+ include modifiers
42
43
 
43
44
  Args:
44
45
  pj (dict): project dictionary
@@ -59,16 +60,6 @@ def load_events_in_db(
59
60
  if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper() and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
60
61
  ]
61
62
 
62
- # selected behaviors defined as point event
63
- """
64
- point_behaviors_codes = [
65
- pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
66
- for x in pj[cfg.ETHOGRAM]
67
- if cfg.POINT in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
68
- and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
69
- ]
70
- """
71
-
72
63
  db = sqlite3.connect(":memory:", isolation_level=None)
73
64
 
74
65
  """
boris/dialog.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
boris/edit_event.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
boris/event_operations.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
6
 
7
7
  This program is free software; you can redistribute it and/or modify
boris/events_cursor.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
6
 
7
7
  This program is free software; you can redistribute it and/or modify
boris/events_snapshots.py CHANGED
@@ -1,23 +1,22 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
+ This file is part of BORIS.
6
7
 
7
- This program is free software; you can redistribute it and/or modify
8
+ BORIS is free software; you can redistribute it and/or modify
8
9
  it under the terms of the GNU General Public License as published by
9
- the Free Software Foundation; either version 2 of the License, or
10
- (at your option) any later version.
10
+ the Free Software Foundation; either version 3 of the License, or
11
+ any later version.
11
12
 
12
- This program is distributed in the hope that it will be useful,
13
+ BORIS is distributed in the hope that it will be useful,
13
14
  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
15
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
16
  GNU General Public License for more details.
16
17
 
17
18
  You should have received a copy of the GNU General Public License
18
- along with this program; if not, write to the Free Software
19
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20
- MA 02110-1301, USA.
19
+ along with this program; if not see <http://www.gnu.org/licenses/>.
21
20
 
22
21
  """
23
22
 
@@ -34,10 +33,10 @@ from . import db_functions, dialog, project_functions, select_observations, sele
34
33
  from . import utilities as util
35
34
 
36
35
 
37
- def events_snapshots(self):
36
+ def extract_media_snapshots(self):
38
37
  """
39
38
  create snapshots corresponding to coded events
40
- if observations are from media file and media files have video
39
+ Observations must be from media file and media files must have video
41
40
  """
42
41
 
43
42
  _, selected_observations = select_observations.select_observations2(
@@ -47,7 +46,7 @@ def events_snapshots(self):
47
46
  return
48
47
 
49
48
  # check if obs are MEDIA
50
- live_images_obs_list = []
49
+ live_images_obs_list: list = []
51
50
  for obs_id in selected_observations:
52
51
  if self.pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in [cfg.LIVE, cfg.IMAGES]:
53
52
  live_images_obs_list.append(obs_id)
@@ -79,7 +78,7 @@ def events_snapshots(self):
79
78
  selected_observations,
80
79
  start_coding=dec("NaN"),
81
80
  end_coding=dec("NaN"),
82
- show_include_modifiers=False,
81
+ show_include_modifiers=True,
83
82
  show_exclude_non_coded_behaviors=False,
84
83
  n_observations=len(selected_observations),
85
84
  )
@@ -94,7 +93,15 @@ def events_snapshots(self):
94
93
  label_caption="Choose parameters",
95
94
  elements_list=[
96
95
  ("dsb", "Time interval around the events (in seconds)", 0.0, 86400, 1, 0, 3),
97
- ("il", "Bitmap format", (("JPG - small size / low quality", ""), ("PNG - big size / high quality", ""))),
96
+ (
97
+ "il",
98
+ "Bitmap format",
99
+ (
100
+ ("JPG - small size / low quality", ""),
101
+ ("PNG - big size / high quality", ""),
102
+ # ("WEBP - small size / high quality", "")
103
+ ),
104
+ ),
98
105
  ],
99
106
  title="Extract frames",
100
107
  )
@@ -105,6 +112,8 @@ def events_snapshots(self):
105
112
  frame_bitmap_format = "jpg"
106
113
  elif "PNG" in ib.elements["Bitmap format"].currentText():
107
114
  frame_bitmap_format = "png"
115
+ # elif "WEBP" in ib.elements["Bitmap format"].currentText():
116
+ # frame_bitmap_format = "webp"
108
117
  else:
109
118
  return
110
119
 
@@ -113,7 +122,7 @@ def events_snapshots(self):
113
122
  self,
114
123
  "Choose a directory to extract events",
115
124
  os.path.expanduser("~"),
116
- options=QFileDialog.ShowDirsOnly,
125
+ options=QFileDialog.Option.ShowDirsOnly,
117
126
  )
118
127
  if not export_dir:
119
128
  return
@@ -137,10 +146,13 @@ def events_snapshots(self):
137
146
  for subject in parameters[cfg.SELECTED_SUBJECTS]:
138
147
  for behavior in parameters[cfg.SELECTED_BEHAVIORS]:
139
148
  cursor.execute(
140
- "SELECT occurence FROM events WHERE observation = ? AND subject = ? AND code = ?",
149
+ "SELECT occurence, modifiers FROM events WHERE observation = ? AND subject = ? AND code = ?",
141
150
  (obs_id, subject, behavior),
142
151
  )
143
- rows = [{"occurence": util.float2decimal(r["occurence"])} for r in cursor.fetchall()]
152
+
153
+ rows = tuple(
154
+ {"occurence": util.float2decimal(r["occurence"]), "modifiers": r[cfg.MODIFIERS]} for r in cursor.fetchall()
155
+ )
144
156
 
145
157
  behavior_state = project_functions.event_type(behavior, self.pj[cfg.ETHOGRAM])
146
158
 
@@ -165,11 +177,11 @@ def events_snapshots(self):
165
177
  "The following media file does not have video.<br>"
166
178
  f"{self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]}"
167
179
  ),
168
- [cfg.OK, "Abort"],
180
+ (cfg.OK, cfg.ABORT),
169
181
  )
170
182
  if response == cfg.OK:
171
183
  continue
172
- if response == "Abort":
184
+ if response == cfg.ABORT:
173
185
  return
174
186
 
175
187
  # check FPS
@@ -194,11 +206,11 @@ def events_snapshots(self):
194
206
  "The FPS was not found for the following media file:<br>"
195
207
  f"{self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]}"
196
208
  ),
197
- [cfg.OK, "Abort"],
209
+ (cfg.OK, cfg.ABORT),
198
210
  )
199
211
  if response == cfg.OK:
200
212
  continue
201
- if response == "Abort":
213
+ if response == cfg.ABORT:
202
214
  return
203
215
 
204
216
  global_start = dec("0.000") if row["occurence"] < time_interval else round(row["occurence"] - time_interval, 3)
@@ -238,11 +250,11 @@ def events_snapshots(self):
238
250
  "At the moment it no possible to extract frames "
239
251
  "for this type of event.<br>"
240
252
  ),
241
- [cfg.OK, "Abort"],
253
+ (cfg.OK, cfg.ABORT),
242
254
  )
243
255
  if response == cfg.OK:
244
256
  continue
245
- if response == "Abort":
257
+ if response == cfg.ABORT:
246
258
  return
247
259
 
248
260
  # globalStop = round(rows[idx + 1]["occurence"] + time_interval, 3)
@@ -279,17 +291,23 @@ def events_snapshots(self):
279
291
  else:
280
292
  continue
281
293
 
282
- ffmpeg_command = (
283
- f'"{self.ffmpeg_bin}" '
284
- f"-ss {start:.3f} "
285
- f'-i "{media_path}" '
286
- f"-vframes {vframes} "
287
- f'"{export_dir}{os.sep}'
288
- f"{util.safeFileName(obs_id).replace(' ', '-')}"
289
- f"_PLAYER{nplayer}"
290
- f"_{util.safeFileName(subject).replace(' ', '-')}"
291
- f"_{util.safeFileName(behavior).replace(' ', '-')}"
292
- f'_{global_start:.3f}_%08d.{frame_bitmap_format}"'
294
+ ffmpeg_command = "".join(
295
+ [
296
+ f'"{self.ffmpeg_bin}" ',
297
+ f'-i "{media_path}" ',
298
+ f"-ss {start:.3f} ",
299
+ f"-vframes {vframes} ",
300
+ f'"{export_dir}{os.sep}',
301
+ f"{util.safeFileName(obs_id).replace(' ', '-')}",
302
+ f"_PLAYER{nplayer}",
303
+ f"_{util.safeFileName(subject).replace(' ', '-')}",
304
+ f"_{util.safeFileName(behavior).replace(' ', '-')}",
305
+ f"_{global_start:.3f}_%08d",
306
+ f"_{util.safeFileName(row[cfg.MODIFIERS].replace('|', '+')).replace(' ', '-')}"
307
+ if parameters[cfg.INCLUDE_MODIFIERS] and row[cfg.MODIFIERS]
308
+ else "",
309
+ f'.{frame_bitmap_format}"',
310
+ ]
293
311
  )
294
312
 
295
313
  logging.debug(f"ffmpeg command: {ffmpeg_command}")
@@ -300,10 +318,10 @@ def events_snapshots(self):
300
318
  self.statusbar.showMessage(f"Frames extracted in {export_dir}", 0)
301
319
 
302
320
 
303
- def extract_events(self):
321
+ def extract_media_clips(self):
304
322
  """
305
- extract sub-sequences from media files corresponding to coded events with FFmpeg
306
- in case of point event, from -n to +n seconds are extracted (n is asked to user)
323
+ extract with FFmpeg sub-sequences from media files corresponding to coded events
324
+ In case of point event, from -n to +n seconds are extracted (n is asked to user)
307
325
  """
308
326
 
309
327
  _, selected_observations = select_observations.select_observations2(
@@ -312,14 +330,14 @@ def extract_events(self):
312
330
  if not selected_observations:
313
331
  return
314
332
 
315
- # check if obs are MEDIA
316
- live_images_obs_list = []
333
+ # check if obs are from media files
334
+ live_images_obs_list: list = []
317
335
  for obs_id in selected_observations:
318
- if self.pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in [cfg.LIVE, cfg.IMAGES]:
336
+ if self.pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.LIVE, cfg.IMAGES):
319
337
  live_images_obs_list.append(obs_id)
320
338
 
321
339
  if live_images_obs_list:
322
- out = "The following observations are live observations or observation from pictures and will be removed from analysis<br><br>"
340
+ out = "The following observations are live observations or observation from pictures and will be removed<br><br>"
323
341
  out += "<br>".join(live_images_obs_list)
324
342
  results = dialog.Results_dialog()
325
343
  results.setWindowTitle(cfg.programName)
@@ -345,7 +363,7 @@ def extract_events(self):
345
363
  selected_observations,
346
364
  start_coding=dec("NaN"),
347
365
  end_coding=dec("NaN"),
348
- show_include_modifiers=False,
366
+ show_include_modifiers=True,
349
367
  show_exclude_non_coded_behaviors=False,
350
368
  )
351
369
  if parameters == {}:
@@ -377,7 +395,7 @@ def extract_events(self):
377
395
  self,
378
396
  "Choose a directory to extract events",
379
397
  os.path.expanduser("~"),
380
- options=QFileDialog.ShowDirsOnly,
398
+ options=QFileDialog.Option.ShowDirsOnly,
381
399
  )
382
400
  if not export_dir:
383
401
  return
@@ -393,7 +411,7 @@ def extract_events(self):
393
411
  self.statusBar().showMessage("Extracting sequences from media files")
394
412
  QApplication.processEvents()
395
413
 
396
- ffmpeg_extract_command: str = '"{ffmpeg_bin}" -ss {start} -i "{input_}" -y -t {duration} {codecs} '
414
+ ffmpeg_extract_command: str = '"{ffmpeg_bin}"-i "{input_}" -ss {start} -y -t {duration} {codecs} '
397
415
  mem_command: str = ""
398
416
  for obs_id in selected_observations:
399
417
  for nplayer in self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE]:
@@ -407,11 +425,12 @@ def extract_events(self):
407
425
  for subject in parameters[cfg.SELECTED_SUBJECTS]:
408
426
  for behavior in parameters[cfg.SELECTED_BEHAVIORS]:
409
427
  cursor.execute(
410
- "SELECT occurence FROM events WHERE observation = ? AND subject = ? AND code = ?",
428
+ "SELECT occurence, modifiers FROM events WHERE observation = ? AND subject = ? AND code = ?",
411
429
  (obs_id, subject, behavior),
412
430
  )
413
- rows = [{"occurence": util.float2decimal(r["occurence"])} for r in cursor.fetchall()]
414
-
431
+ rows = tuple(
432
+ {"occurence": util.float2decimal(r["occurence"]), "modifiers": r[cfg.MODIFIERS]} for r in cursor.fetchall()
433
+ )
415
434
  behavior_state = project_functions.event_type(behavior, self.pj[cfg.ETHOGRAM])
416
435
  if behavior_state in cfg.STATE_EVENT_TYPES and len(rows) % 2: # unpaired events
417
436
  continue
@@ -433,7 +452,7 @@ def extract_events(self):
433
452
  dialog.MessageDialog(
434
453
  cfg.programName,
435
454
  f"The media file {self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]} does not have a video stream",
436
- ["Continue", "Abort"],
455
+ ("Continue", "Abort"),
437
456
  )
438
457
  == "Abort"
439
458
  ):
@@ -461,9 +480,9 @@ def extract_events(self):
461
480
  dialog.MessageDialog(
462
481
  cfg.programName,
463
482
  f"The media file {self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]} does not have an audio stream",
464
- ["Continue", "Abort"],
483
+ ("Continue", cfg.ABORT),
465
484
  )
466
- == "Abort"
485
+ == cfg.ABORT
467
486
  ):
468
487
  return
469
488
  else:
@@ -509,11 +528,11 @@ def extract_events(self):
509
528
  "The event extends on 2 successive video. "
510
529
  " At the moment it is not possible to extract this type of event.<br>"
511
530
  ),
512
- [cfg.OK, "Abort"],
531
+ (cfg.OK, cfg.ABORT),
513
532
  )
514
533
  if response == cfg.OK:
515
534
  continue
516
- if response == "Abort":
535
+ if response == cfg.ABORT:
517
536
  return
518
537
 
519
538
  globalStart = dec("0.000") if row["occurence"] < timeOffset else round(row["occurence"] - timeOffset, 3)
@@ -550,22 +569,27 @@ def extract_events(self):
550
569
  continue
551
570
 
552
571
  new_file_name = pl.Path(export_dir) / pl.Path(
553
- (
554
- f"{util.safeFileName(obs_id).replace(' ', '-')}_"
555
- f"PLAYER{nplayer}_"
556
- f"{util.safeFileName(subject).replace(' ', '-')}_"
557
- f"{util.safeFileName(behavior)}_"
558
- f"{globalStart}-{globalStop}"
559
- f"{new_extension}"
572
+ "".join(
573
+ [
574
+ f"{util.safeFileName(obs_id).replace(' ', '-')}_",
575
+ f"PLAYER{nplayer}_",
576
+ f"{util.safeFileName(subject).replace(' ', '-')}_",
577
+ f"{util.safeFileName(behavior)}_",
578
+ f"{globalStart}-{globalStop}",
579
+ f"_{util.safeFileName(row[cfg.MODIFIERS].replace('|', '+')).replace(' ', '-')}"
580
+ if parameters[cfg.INCLUDE_MODIFIERS] and row[cfg.MODIFIERS]
581
+ else "",
582
+ f"{new_extension}",
583
+ ]
560
584
  )
561
- ) # .with_suffix(new_extension)
585
+ )
562
586
 
563
587
  if new_file_name.is_file():
564
588
  if mem_command not in (cfg.OVERWRITE_ALL, cfg.SKIP_ALL):
565
589
  mem_command = dialog.MessageDialog(
566
590
  cfg.programName,
567
591
  f"The file <b>{new_file_name}</b> already exists.",
568
- [cfg.OVERWRITE, cfg.OVERWRITE_ALL, cfg.SKIP, cfg.SKIP_ALL, cfg.CANCEL],
592
+ (cfg.OVERWRITE, cfg.OVERWRITE_ALL, cfg.SKIP, cfg.SKIP_ALL, cfg.CANCEL),
569
593
  )
570
594
  if mem_command == cfg.CANCEL:
571
595
  return
boris/exclusion_matrix.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7