boris-behav-obs 8.27.10__py2.py3-none-any.whl → 9.0.2__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. boris/about.py +7 -5
  2. boris/add_modifier.py +35 -35
  3. boris/add_modifier_ui.py +229 -129
  4. boris/advanced_event_filtering.py +3 -3
  5. boris/analysis_plugins/__init__.py +0 -0
  6. boris/analysis_plugins/number_of_occurences.py +60 -0
  7. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +72 -0
  8. boris/analysis_plugins/time_budget.py +95 -0
  9. boris/behav_coding_map_creator.py +103 -108
  10. boris/behavior_binary_table.py +1 -1
  11. boris/behaviors_coding_map.py +8 -8
  12. boris/coding_pad.py +6 -6
  13. boris/config.py +6 -0
  14. boris/config_file.py +1 -1
  15. boris/connections.py +4 -2
  16. boris/converters.py +2 -3
  17. boris/converters_ui.py +187 -110
  18. boris/cooccurence.py +2 -2
  19. boris/core.py +341 -94
  20. boris/core_qrc.py +16088 -13246
  21. boris/core_ui.py +922 -812
  22. boris/dialog.py +14 -13
  23. boris/duration_widget.py +5 -5
  24. boris/edit_event.py +1 -1
  25. boris/edit_event_ui.py +162 -88
  26. boris/event_operations.py +4 -25
  27. boris/events_cursor.py +17 -9
  28. boris/events_snapshots.py +5 -5
  29. boris/exclusion_matrix.py +1 -1
  30. boris/export_events.py +38 -28
  31. boris/export_observation.py +1 -1
  32. boris/external_processes.py +3 -5
  33. boris/geometric_measurement.py +49 -26
  34. boris/gui_utilities.py +31 -30
  35. boris/import_observations.py +2 -4
  36. boris/irr.py +1 -1
  37. boris/latency.py +1 -1
  38. boris/map_creator.py +77 -89
  39. boris/measurement_widget.py +4 -4
  40. boris/media_file.py +2 -4
  41. boris/menu_options.py +1 -3
  42. boris/modifiers_coding_map.py +4 -4
  43. boris/mpv2.py +0 -2
  44. boris/observation.py +124 -29
  45. boris/observation_operations.py +18 -40
  46. boris/observation_ui.py +566 -374
  47. boris/observations_list.py +6 -6
  48. boris/param_panel.py +2 -2
  49. boris/param_panel_ui.py +246 -141
  50. boris/player_dock_widget.py +16 -21
  51. boris/plot_data_module.py +6 -6
  52. boris/plot_events_rt.py +7 -8
  53. boris/plot_spectrogram_rt.py +7 -8
  54. boris/plot_waveform_rt.py +6 -7
  55. boris/plugins.py +79 -0
  56. boris/preferences.py +127 -17
  57. boris/preferences_ui.py +464 -240
  58. boris/project.py +69 -72
  59. boris/project_functions.py +233 -31
  60. boris/project_import_export.py +59 -67
  61. boris/project_ui.py +672 -440
  62. boris/qrc_boris.py +6 -3
  63. boris/qrc_boris5.py +6 -3
  64. boris/select_modifiers.py +2 -2
  65. boris/select_observations.py +2 -2
  66. boris/select_subj_behav.py +3 -3
  67. boris/state_events.py +1 -1
  68. boris/subjects_pad.py +5 -5
  69. boris/synthetic_time_budget.py +2 -2
  70. boris/time_budget_functions.py +15 -0
  71. boris/time_budget_widget.py +4 -4
  72. boris/transitions.py +34 -25
  73. boris/utilities.py +96 -3
  74. boris/version.py +2 -2
  75. boris/video_equalizer.py +4 -4
  76. boris/video_equalizer_ui.py +199 -130
  77. boris/video_operations.py +1 -1
  78. boris/view_df.py +106 -0
  79. boris/view_df_ui.py +75 -0
  80. boris/write_event.py +9 -1
  81. {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.2.dist-info}/METADATA +5 -5
  82. boris_behav_obs-9.0.2.dist-info/RECORD +103 -0
  83. {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.2.dist-info}/WHEEL +1 -1
  84. boris/qdarkstyle/__init__.py +0 -479
  85. boris/qdarkstyle/__main__.py +0 -66
  86. boris/qdarkstyle/colorsystem.py +0 -38
  87. boris/qdarkstyle/dark/__init__.py +0 -1
  88. boris/qdarkstyle/dark/darkstyle_rc.py +0 -11379
  89. boris/qdarkstyle/dark/palette.py +0 -38
  90. boris/qdarkstyle/example/__init__.py +0 -4
  91. boris/qdarkstyle/example/__main__.py +0 -386
  92. boris/qdarkstyle/example/ui/__init__.py +0 -4
  93. boris/qdarkstyle/light/__init__.py +0 -1
  94. boris/qdarkstyle/light/lightstyle_rc.py +0 -11305
  95. boris/qdarkstyle/light/palette.py +0 -37
  96. boris/qdarkstyle/palette.py +0 -102
  97. boris/qdarkstyle/utils/__init__.py +0 -73
  98. boris/qdarkstyle/utils/__main__.py +0 -96
  99. boris/qdarkstyle/utils/images.py +0 -449
  100. boris/qdarkstyle/utils/scss.py +0 -318
  101. boris/vlc_local.py +0 -83
  102. boris_behav_obs-8.27.10.dist-info/RECORD +0 -114
  103. {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.2.dist-info}/LICENSE.TXT +0 -0
  104. {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.2.dist-info}/entry_points.txt +0 -0
  105. {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,72 @@
1
+ """
2
+ BORIS plugin
3
+
4
+ number of occurences of behaviors by independent_variable
5
+ """
6
+
7
+ import pandas as pd
8
+
9
+ __version__ = "0.1.0"
10
+ __version_date__ = "2024-11-14"
11
+ __plugin_name__ = "Number of occurences of behaviors by subject by independent_variable"
12
+ __author__ = "Olivier Friard - University of Torino - Italy"
13
+
14
+
15
+ def run(df: pd.DataFrame):
16
+ """
17
+ Calculate the number of occurrences of behaviors by subject and by independent_variable.
18
+ """
19
+
20
+ results = (
21
+ df.groupby(
22
+ [
23
+ "independent variable 'Weather'",
24
+ "Subject",
25
+ "Behavior",
26
+ ]
27
+ )["Behavior"]
28
+ .count()
29
+ .reset_index(name="number of occurences")
30
+ )
31
+
32
+ return results
33
+
34
+
35
+ def main(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
36
+ """
37
+ filter by selected observations.
38
+ filter by selected subjects.
39
+ filter by selected behaviors.
40
+ filter by time interval.
41
+ """
42
+
43
+ # filter selected observations
44
+ if observations_list:
45
+ df = df[df["Observation id"].isin(observations_list)]
46
+
47
+ if parameters:
48
+ # filter selected subjects
49
+ df = df[df["Subject"].isin(parameters["selected subjects"])]
50
+
51
+ # filter selected behaviors
52
+ df = df[df["Behavior"].isin(parameters["selected behaviors"])]
53
+
54
+ # filter selected time interval
55
+ if parameters["start time"] is not None and parameters["end time"] is not None:
56
+ MIN_TIME = parameters["start time"]
57
+ MAX_TIME = parameters["end time"]
58
+ df_interval = df[
59
+ (
60
+ ((df["Start (s)"] >= MIN_TIME) & (df["Start (s)"] <= MAX_TIME))
61
+ | ((df["Stop (s)"] >= MIN_TIME) & (df["Stop (s)"] <= MAX_TIME))
62
+ )
63
+ | ((df["Start (s)"] < MIN_TIME) & (df["Stop (s)"] > MAX_TIME))
64
+ ]
65
+ df_interval.loc[df["Start (s)"] < MIN_TIME, "Start (s)"] = MIN_TIME
66
+ df_interval.loc[df["Stop (s)"] > MAX_TIME, "Stop (s)"] = MAX_TIME
67
+
68
+ df_interval.loc[:, "Duration (s)"] = df_interval["Stop (s)"] - df_interval["Start (s)"]
69
+
70
+ df = df_interval
71
+
72
+ return run(df)
@@ -0,0 +1,95 @@
1
+ """
2
+ BORIS plugin
3
+ """
4
+
5
+ import pandas as pd
6
+
7
+ __version__ = "0.1.0"
8
+ __version_date__ = "2024-11-14"
9
+ __plugin_name__ = "Time budget"
10
+ __author__ = "Olivier Friard - University of Torino - Italy"
11
+
12
+
13
+ def run(df: pd.DataFrame):
14
+ """
15
+ Calculate the following values:
16
+
17
+ - Total number of occurences of behavior
18
+ - Total duration of behavior (in seconds)
19
+ - Duration mean of behavior (in seconds)
20
+ - Standard deviation of behavior duration (in seconds)
21
+ - Inter-event intervals mean (in seconds)
22
+ - Inter-event intervals standard deviation (in seconds)
23
+ - % of total duration
24
+ """
25
+
26
+ dfs = [
27
+ df.groupby(["Subject", "Behavior"])["Behavior"].count().reset_index(name="number of occurences"),
28
+ df.groupby(["Subject", "Behavior"])["Duration (s)"].sum().reset_index(name="total duration"),
29
+ df.groupby(["Subject", "Behavior"])["Duration (s)"].mean().astype(float).round(3).reset_index(name="duration mean"),
30
+ df.groupby(["Subject", "Behavior"])["Duration (s)"].std().astype(float).round(3).reset_index(name="duration std dev"),
31
+ ]
32
+
33
+ # inter events
34
+ df2 = df.sort_values(by=["Observation id", "Subject", "Behavior", "Start (s)"])
35
+ df2["diff"] = df2.groupby(["Observation id", "Subject", "Behavior"])["Start (s)"].shift(periods=-1) - df2["Stop (s)"]
36
+
37
+ dfs.append(df2.groupby(["Subject", "Behavior"])["diff"].mean().astype(float).round(3).reset_index(name="inter-event intervals mean"))
38
+
39
+ dfs.append(df2.groupby(["Subject", "Behavior"])["diff"].std().astype(float).round(3).reset_index(name="inter-event intervals std dev"))
40
+
41
+ # % of time
42
+ dfs.append(
43
+ (100 * df.groupby(["Subject", "Behavior"])["Duration (s)"].sum() / df.groupby(["Subject"])["Duration (s)"].sum())
44
+ .astype(float)
45
+ .round(3)
46
+ .reset_index(name="% of total duration")
47
+ )
48
+
49
+ merged_df = dfs[0]
50
+ for df in dfs[1:]:
51
+ merged_df = pd.merge(merged_df, df, on=["Subject", "Behavior"])
52
+
53
+ return merged_df
54
+
55
+
56
+ def main(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
57
+ """
58
+ filter by selected observations.
59
+ filter by selected subjects.
60
+ filter by selected behaviors.
61
+ filter by time interval.
62
+ """
63
+
64
+ # filter selected observations
65
+ if observations_list:
66
+ df = df[df["Observation id"].isin(observations_list)]
67
+
68
+ if parameters:
69
+ # filter selected subjects
70
+ df = df[df["Subject"].isin(parameters["selected subjects"])]
71
+
72
+ # filter selected behaviors
73
+ df = df[df["Behavior"].isin(parameters["selected behaviors"])]
74
+
75
+ # filter selected time interval
76
+ if parameters["start time"] is not None and parameters["end time"] is not None:
77
+ MIN_TIME = parameters["start time"]
78
+ MAX_TIME = parameters["end time"]
79
+
80
+ df_interval = df[
81
+ (
82
+ ((df["Start (s)"] >= MIN_TIME) & (df["Start (s)"] <= MAX_TIME))
83
+ | ((df["Stop (s)"] >= MIN_TIME) & (df["Stop (s)"] <= MAX_TIME))
84
+ )
85
+ | ((df["Start (s)"] < MIN_TIME) & (df["Stop (s)"] > MAX_TIME))
86
+ ]
87
+
88
+ df_interval.loc[df["Start (s)"] < MIN_TIME, "Start (s)"] = MIN_TIME
89
+ df_interval.loc[df["Stop (s)"] > MAX_TIME, "Stop (s)"] = MAX_TIME
90
+
91
+ df_interval.loc[:, "Duration (s)"] = df_interval["Stop (s)"] - df_interval["Start (s)"]
92
+
93
+ df = df_interval
94
+
95
+ return run(df)
@@ -23,12 +23,11 @@ This file is part of BORIS.
23
23
  import binascii
24
24
  import io
25
25
  import json
26
- import os
26
+ from pathlib import Path
27
27
 
28
- from PyQt5.QtCore import QBuffer, QByteArray, QIODevice, QLineF, QPoint, Qt, pyqtSignal
29
- from PyQt5.QtGui import QBrush, QColor, QIcon, QMouseEvent, QPen, QPixmap, QPolygonF
30
- from PyQt5.QtWidgets import (
31
- QAction,
28
+ from PySide6.QtCore import QBuffer, QByteArray, QIODevice, QLineF, QPoint, Qt, Signal
29
+ from PySide6.QtGui import QBrush, QColor, QIcon, QMouseEvent, QPen, QPixmap, QPolygonF, QAction
30
+ from PySide6.QtWidgets import (
32
31
  QApplication,
33
32
  QColorDialog,
34
33
  QFileDialog,
@@ -68,14 +67,14 @@ selectedBrush.setColor(QColor(255, 255, 0, 255))
68
67
 
69
68
 
70
69
  class BehaviorsMapCreatorWindow(QMainWindow):
71
- signal_add_to_project = pyqtSignal(dict)
70
+ signal_add_to_project = Signal(dict)
72
71
 
73
72
  class View(QGraphicsView):
74
73
  """
75
74
  class for handling mousepress event in QGraphicsView
76
75
  """
77
76
 
78
- mousePress = pyqtSignal(QMouseEvent)
77
+ mousePress = Signal(QMouseEvent)
79
78
 
80
79
  def mousePressEvent(self, event):
81
80
  self.mousePress.emit(event)
@@ -199,6 +198,8 @@ class BehaviorsMapCreatorWindow(QMainWindow):
199
198
  self.btCancelAreaCreation.setVisible(False)
200
199
  hlayout_cmd.addWidget(self.btCancelAreaCreation)
201
200
 
201
+ hlayout_cmd.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
202
+
202
203
  self.btDeleteArea = QPushButton("Delete selected behavior area", self)
203
204
  self.btDeleteArea.clicked.connect(self.deleteArea)
204
205
  self.btDeleteArea.setVisible(False)
@@ -621,6 +622,8 @@ class BehaviorsMapCreatorWindow(QMainWindow):
621
622
 
622
623
  while True:
623
624
  map_name, ok = QInputDialog.getText(self, "Behaviors coding map name", "Enter a name for the new coding map")
625
+ if not ok:
626
+ return
624
627
  if map_name.upper() in self.bcm_list:
625
628
  QMessageBox.critical(
626
629
  self,
@@ -634,18 +637,13 @@ class BehaviorsMapCreatorWindow(QMainWindow):
634
637
  if ok and map_name and map_name.upper() not in self.bcm_list:
635
638
  self.mapName = map_name
636
639
  break
637
- if not ok:
638
- return
639
- """
640
- if not self.mapName:
641
- QMessageBox.critical(self, "", "You must define a name for the new coding map")
642
- return
643
- """
644
640
 
645
641
  self.setWindowTitle(f"{cfg.programName} - Behaviors coding map creator tool - {self.mapName}")
646
642
 
647
- self.btLoad.setVisible(True)
648
- self.statusBar().showMessage('Click "Load bitmap" button to select and load a bitmap into the viewer')
643
+ self.loadBitmap()
644
+
645
+ # self.btLoad.setVisible(True)
646
+ # self.statusBar().showMessage('Click "Load bitmap" button to select and load a bitmap into the viewer')
649
647
 
650
648
  def openMap(self):
651
649
  """
@@ -664,75 +662,72 @@ class BehaviorsMapCreatorWindow(QMainWindow):
664
662
  if (response == "Save" and not self.saveMap_clicked()) or (response == "Cancel"):
665
663
  return
666
664
 
667
- fn = QFileDialog(self).getOpenFileName(
665
+ fileName, _ = QFileDialog(self).getOpenFileName(
668
666
  self, "Open a behaviors coding map", "", "Behaviors coding map (*.behav_coding_map);;All files (*)"
669
667
  )
670
- fileName = fn[0] if type(fn) is tuple else fn
671
-
672
- if fileName:
673
- try:
674
- self.codingMap = json.loads(open(fileName, "r").read())
675
- except Exception:
676
- QMessageBox.critical(self, cfg.programName, f"The file {fileName} is not a behaviors coding map.")
677
- return
678
668
 
679
- if "coding_map_type" not in self.codingMap or self.codingMap["coding_map_type"] != "BORIS behaviors coding map":
680
- QMessageBox.critical(self, cfg.programName, f"The file {fileName} is not a BORIS behaviors coding map.")
669
+ if not fileName:
670
+ self.statusBar().showMessage("No file", 5000)
671
+ try:
672
+ self.codingMap = json.loads(open(fileName, "r").read())
673
+ except Exception:
674
+ QMessageBox.critical(self, cfg.programName, f"The file {fileName} is not a behaviors coding map.")
675
+ return
681
676
 
682
- self.cancelMap()
677
+ if "coding_map_type" not in self.codingMap or self.codingMap["coding_map_type"] != "BORIS behaviors coding map":
678
+ QMessageBox.critical(self, cfg.programName, f"The file {fileName} is not a BORIS behaviors coding map.")
683
679
 
684
- self.mapName = self.codingMap["name"]
680
+ self.cancelMap()
685
681
 
686
- self.setWindowTitle(f"{cfg.programName} - Behaviors coding map creator - {self.mapName}")
682
+ self.mapName = self.codingMap["name"]
687
683
 
688
- self.bitmapFileName = True
684
+ self.setWindowTitle(f"{cfg.programName} - Behaviors coding map creator - {self.mapName}")
689
685
 
690
- self.fileName = fileName
686
+ self.bitmapFileName = True
691
687
 
692
- bitmapContent = binascii.a2b_base64(self.codingMap["bitmap"])
688
+ self.fileName = fileName
693
689
 
694
- self.pixmap.loadFromData(bitmapContent)
690
+ bitmapContent = binascii.a2b_base64(self.codingMap["bitmap"])
695
691
 
696
- self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
697
- self.view.setMinimumHeight(self.pixmap.size().height())
698
- # self.view.setMaximumHeight(self.pixmap.size().height())
699
- pixItem = QGraphicsPixmapItem(self.pixmap)
700
- pixItem.setPos(0, 0)
701
- self.view.scene().addItem(pixItem)
692
+ self.pixmap.loadFromData(bitmapContent)
702
693
 
703
- for key in self.codingMap["areas"]:
704
- areaCode = self.codingMap["areas"][key]["code"]
705
- points = self.codingMap["areas"][key]["geometry"]
694
+ self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
695
+ self.view.setMinimumHeight(self.pixmap.size().height())
696
+ # self.view.setMaximumHeight(self.pixmap.size().height())
697
+ pixItem = QGraphicsPixmapItem(self.pixmap)
698
+ pixItem.setPos(0, 0)
699
+ self.view.scene().addItem(pixItem)
706
700
 
707
- newPolygon = QPolygonF()
708
- for p in points:
709
- newPolygon.append(QPoint(p[0], p[1]))
701
+ for key in self.codingMap["areas"]:
702
+ areaCode = self.codingMap["areas"][key]["code"]
703
+ points = self.codingMap["areas"][key]["geometry"]
710
704
 
711
- # draw polygon
712
- """polygon = QGraphicsPolygonItem(None, None) if QT_VERSION_STR[0] == "4" else QGraphicsPolygonItem()"""
713
- polygon = QGraphicsPolygonItem()
714
- polygon.setPolygon(newPolygon)
715
- clr = QColor()
716
- clr.setRgba(self.codingMap["areas"][key]["color"])
717
- polygon.setPen(QPen(clr, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))
718
- polygon.setBrush(QBrush(clr, Qt.SolidPattern))
705
+ newPolygon = QPolygonF()
706
+ for p in points:
707
+ newPolygon.append(QPoint(p[0], p[1]))
719
708
 
720
- self.view.scene().addItem(polygon)
709
+ # draw polygon
710
+ """polygon = QGraphicsPolygonItem(None, None) if QT_VERSION_STR[0] == "4" else QGraphicsPolygonItem()"""
711
+ polygon = QGraphicsPolygonItem()
712
+ polygon.setPolygon(newPolygon)
713
+ clr = QColor()
714
+ clr.setRgba(self.codingMap["areas"][key]["color"])
715
+ polygon.setPen(QPen(clr, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))
716
+ polygon.setBrush(QBrush(clr, Qt.SolidPattern))
721
717
 
722
- self.polygonsList2.append([areaCode, polygon])
718
+ self.view.scene().addItem(polygon)
723
719
 
724
- self.btNewArea.setVisible(True)
725
- self.btLoad.setVisible(False)
720
+ self.polygonsList2.append([areaCode, polygon])
726
721
 
727
- self.saveMapAction.setEnabled(True)
728
- self.saveAsMapAction.setEnabled(True)
729
- self.addToProject.setEnabled(True)
730
- self.mapNameAction.setEnabled(True)
722
+ self.btNewArea.setVisible(True)
723
+ self.btLoad.setVisible(False)
731
724
 
732
- self.update_area_list()
725
+ self.saveMapAction.setEnabled(True)
726
+ self.saveAsMapAction.setEnabled(True)
727
+ self.addToProject.setEnabled(True)
728
+ self.mapNameAction.setEnabled(True)
733
729
 
734
- else:
735
- self.statusBar().showMessage("No file", 5000)
730
+ self.update_area_list()
736
731
 
737
732
  def make_coding_map_dict(self) -> dict:
738
733
  """
@@ -788,25 +783,25 @@ class BehaviorsMapCreatorWindow(QMainWindow):
788
783
  def saveAsMap_clicked(self):
789
784
  filters = "Behaviors coding map (*.behav_coding_map);;All files (*)"
790
785
 
791
- fn = QFileDialog(self).getSaveFileName(self, "Save behaviors coding map as", "", filters)
792
- self.fileName = fn[0] if type(fn) is tuple else fn
786
+ self.fileName, _ = QFileDialog.getSaveFileName(self, "Save behaviors coding map as", "", filters)
793
787
 
794
- if self.fileName:
795
- if os.path.splitext(self.fileName)[1] != ".behav_coding_map":
796
- self.fileName += ".behav_coding_map"
797
- self.saveMap()
788
+ if not self.fileName:
789
+ return
790
+ """if os.path.splitext(self.fileName)[1] != ".behav_coding_map":"""
791
+ if Path(self.fileName).suffix != ".behav_coding_map":
792
+ self.fileName += ".behav_coding_map"
793
+ self.saveMap()
798
794
 
799
795
  def saveMap_clicked(self):
800
796
  if not self.fileName:
801
- fn = QFileDialog().getSaveFileName(
797
+ self.fileName, _ = QFileDialog().getSaveFileName(
802
798
  self,
803
799
  "Save modifiers map",
804
800
  self.mapName + ".behav_coding_map",
805
801
  "Behaviors coding map (*.behav_coding_map);;All files (*)",
806
802
  )
807
- self.fileName = fn[0] if type(fn) is tuple else fn
808
803
 
809
- if self.fileName and os.path.splitext(self.fileName)[1] != ".behav_coding_map":
804
+ if self.fileName and Path(self.fileName).suffix != ".behav_coding_map":
810
805
  self.fileName += ".behav_coding_map"
811
806
 
812
807
  if self.fileName:
@@ -987,50 +982,50 @@ class BehaviorsMapCreatorWindow(QMainWindow):
987
982
  def loadBitmap(self):
988
983
  """
989
984
  load bitmap as background for coding map
990
- resize bitmap to CODING_MAP_RESIZE_W x CODING_MAP_RESIZE_H defined in config.py
985
+ no more resize bitmap to CODING_MAP_RESIZE_W x CODING_MAP_RESIZE_H defined in config.py
991
986
  """
992
987
 
993
- fn = QFileDialog(self).getOpenFileName(self, "Load bitmap", "", "bitmap files (*.png *.jpg);;All files (*)")
994
- fileName = fn[0] if type(fn) is tuple else fn
988
+ fileName, _ = QFileDialog.getOpenFileName(self, "Load bitmap", "", "bitmap files (*.png *.jpg);;All files (*)")
995
989
 
996
- if fileName:
997
- self.bitmapFileName = fileName
990
+ if not fileName:
991
+ return
992
+ self.bitmapFileName = fileName
998
993
 
999
- self.pixmap.load(self.bitmapFileName)
994
+ self.pixmap.load(self.bitmapFileName)
1000
995
 
1001
- # scale image
1002
- """
1003
- if (
1004
- self.pixmap.size().width() > cfg.CODING_MAP_RESIZE_W
1005
- or self.pixmap.size().height() > cfg.CODING_MAP_RESIZE_H
1006
- ):
1007
- self.pixmap = self.pixmap.scaled(cfg.CODING_MAP_RESIZE_W, cfg.CODING_MAP_RESIZE_H, Qt.KeepAspectRatio)
1008
- QMessageBox.information(
1009
- self,
1010
- cfg.programName,
1011
- (
1012
- f"The bitmap was resized to {self.pixmap.size().width()}x{self.pixmap.size().height()} pixels\n"
1013
- "The original file was not modified"
1014
- ),
1015
- )
1016
- """
996
+ # scale image
997
+ """
998
+ if (
999
+ self.pixmap.size().width() > cfg.CODING_MAP_RESIZE_W
1000
+ or self.pixmap.size().height() > cfg.CODING_MAP_RESIZE_H
1001
+ ):
1002
+ self.pixmap = self.pixmap.scaled(cfg.CODING_MAP_RESIZE_W, cfg.CODING_MAP_RESIZE_H, Qt.KeepAspectRatio)
1003
+ QMessageBox.information(
1004
+ self,
1005
+ cfg.programName,
1006
+ (
1007
+ f"The bitmap was resized to {self.pixmap.size().width()}x{self.pixmap.size().height()} pixels\n"
1008
+ "The original file was not modified"
1009
+ ),
1010
+ )
1011
+ """
1017
1012
 
1018
- self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
1019
- pixitem = QGraphicsPixmapItem(self.pixmap)
1020
- pixitem.setPos(0, 0)
1021
- self.view.scene().addItem(pixitem)
1013
+ self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
1014
+ pixitem = QGraphicsPixmapItem(self.pixmap)
1015
+ pixitem.setPos(0, 0)
1016
+ self.view.scene().addItem(pixitem)
1022
1017
 
1023
- self.btNewArea.setVisible(True)
1018
+ self.btNewArea.setVisible(True)
1024
1019
 
1025
- self.btLoad.setVisible(False)
1026
- self.saveMapAction.setEnabled(True)
1027
- self.saveAsMapAction.setEnabled(True)
1028
- self.addToProject.setEnabled(True)
1029
- self.mapNameAction.setEnabled(True)
1020
+ self.btLoad.setVisible(False)
1021
+ self.saveMapAction.setEnabled(True)
1022
+ self.saveAsMapAction.setEnabled(True)
1023
+ self.addToProject.setEnabled(True)
1024
+ self.mapNameAction.setEnabled(True)
1030
1025
 
1031
- self.statusBar().showMessage("""Click "New behavior area" to create a new behavior area""")
1026
+ self.statusBar().showMessage("""Click "New behavior area" to create a new behavior area""")
1032
1027
 
1033
- self.flag_map_changed = True
1028
+ self.flag_map_changed = True
1034
1029
 
1035
1030
 
1036
1031
  def behaviors_coding_map_creator(self):
@@ -24,7 +24,7 @@ import pathlib
24
24
  from decimal import Decimal as dec
25
25
 
26
26
  import tablib
27
- from PyQt5.QtWidgets import QFileDialog, QInputDialog, QMessageBox
27
+ from PySide6.QtWidgets import QFileDialog, QInputDialog, QMessageBox
28
28
 
29
29
  from . import observation_operations
30
30
 
@@ -20,9 +20,9 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- from PyQt5.QtGui import QMouseEvent, QPixmap, QPolygonF, QColor, QBrush, QPen
24
- from PyQt5.QtCore import Qt, pyqtSignal, QEvent, QPoint
25
- from PyQt5.QtWidgets import (
23
+ from PySide6.QtGui import QMouseEvent, QPixmap, QPolygonF, QColor, QBrush, QPen
24
+ from PySide6.QtCore import Qt, Signal, QEvent, QPoint
25
+ from PySide6.QtWidgets import (
26
26
  QLabel,
27
27
  QHBoxLayout,
28
28
  QGraphicsView,
@@ -49,8 +49,8 @@ penStyle = Qt.NoPen
49
49
 
50
50
  class BehaviorsCodingMapWindowClass(QWidget):
51
51
  class View(QGraphicsView):
52
- mousePress = pyqtSignal(QMouseEvent)
53
- mouseMove = pyqtSignal(QMouseEvent)
52
+ mousePress = Signal(QMouseEvent)
53
+ mouseMove = Signal(QMouseEvent)
54
54
 
55
55
  def eventFilter(self, source, event):
56
56
  if event.type() == QEvent.MouseMove:
@@ -71,9 +71,9 @@ class BehaviorsCodingMapWindowClass(QWidget):
71
71
  self.viewport().installEventFilter(self)
72
72
  self.setMouseTracking(True)
73
73
 
74
- clickSignal = pyqtSignal(str, list) # click signal to be sent to mainwindow
75
- keypressSignal = pyqtSignal(QEvent)
76
- close_signal = pyqtSignal(str)
74
+ clickSignal = Signal(str, list) # click signal to be sent to mainwindow
75
+ keypressSignal = Signal(QEvent)
76
+ close_signal = Signal(str)
77
77
 
78
78
  def __init__(self, behaviors_coding_map, idx=0):
79
79
  super(BehaviorsCodingMapWindowClass, self).__init__()
boris/coding_pad.py CHANGED
@@ -19,9 +19,9 @@ Copyright 2012-2024 Olivier Friard
19
19
  MA 02110-1301, USA.
20
20
  """
21
21
 
22
- from PyQt5.QtCore import Qt, pyqtSignal, QEvent, QRect
23
- from PyQt5.QtGui import QFont
24
- from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QGridLayout, QComboBox, QMessageBox
22
+ from PySide6.QtCore import Qt, Signal, QEvent, QRect
23
+ from PySide6.QtGui import QFont
24
+ from PySide6.QtWidgets import QWidget, QPushButton, QHBoxLayout, QGridLayout, QComboBox, QMessageBox
25
25
 
26
26
  from . import config as cfg
27
27
  from . import utilities as util
@@ -38,9 +38,9 @@ class Button(QWidget):
38
38
 
39
39
 
40
40
  class CodingPad(QWidget):
41
- clickSignal = pyqtSignal(str)
42
- sendEventSignal = pyqtSignal(QEvent)
43
- close_signal = pyqtSignal(QRect, dict)
41
+ clickSignal = Signal(str)
42
+ sendEventSignal = Signal(QEvent)
43
+ close_signal = Signal(QRect, dict)
44
44
 
45
45
  def __init__(self, pj: dict, filtered_behaviors, parent=None):
46
46
  super().__init__(parent)
boris/config.py CHANGED
@@ -49,6 +49,7 @@ NA: str = "NA"
49
49
 
50
50
  REALTIME_PLOT_CURSOR_COLOR: str = "red"
51
51
 
52
+ DARKER_DIFFERENCE = 5
52
53
 
53
54
  CTRL_KEY: str = "Ctrl"
54
55
  ALT_KEY: str = "Alt"
@@ -400,6 +401,9 @@ MPV_HWDEC_AUTOSAFE = "auto-safe"
400
401
  MPV_HWDEC_OPTIONS = (MPV_HWDEC_AUTO, MPV_HWDEC_AUTOSAFE, MPV_HWDEC_NO)
401
402
  MPV_HWDEC_DEFAULT_VALUE = MPV_HWDEC_AUTO
402
403
 
404
+ ANALYSIS_PLUGINS = "analysis_plugins"
405
+ EXCLUDED_PLUGINS = "excluded_plugins"
406
+ PERSONAL_PLUGINS_DIR = "personal_plugins_dir"
403
407
 
404
408
  PROJECT_FILE_INDENTATION = "project file indentation"
405
409
  PROJECT_FILE_INDENTATION_COMBO_OPTIONS = ("None", "Newline", "Tab", "2 spaces", "4 spaces")
@@ -409,8 +413,10 @@ PROJECT_FILE_INDENTATION_DEFAULT_VALUE = None
409
413
  TOOLBAR_ICON_SIZE = "toolbar icon size"
410
414
  DEFAULT_TOOLBAR_ICON_SIZE_VALUE = 24
411
415
 
416
+ """
412
417
  DARK_MODE = "dark_mode"
413
418
  DARK_MODE_DEFAULT_VALUE = False
419
+ """
414
420
 
415
421
  VIDEO_VIEWER = 0
416
422
  PICTURE_VIEWER = 1
boris/config_file.py CHANGED
@@ -28,7 +28,7 @@ import time
28
28
  from . import config as cfg
29
29
  from . import dialog
30
30
 
31
- from PyQt5.QtCore import QByteArray, QSettings
31
+ from PySide6.QtCore import QByteArray, QSettings
32
32
 
33
33
 
34
34
  def read(self):
boris/connections.py CHANGED
@@ -19,8 +19,8 @@ Copyright 2012-2024 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 (
@@ -49,6 +49,7 @@ from . import (
49
49
  video_operations,
50
50
  project_functions,
51
51
  external_processes,
52
+ plugins,
52
53
  )
53
54
 
54
55
  from . import state_events as state_events
@@ -200,6 +201,7 @@ def connections(self):
200
201
  self.actionCreate_transitions_flow_diagram_2.triggered.connect(transitions.transitions_flow_diagram)
201
202
 
202
203
  # menu Analysis
204
+
203
205
  self.actionTime_budget.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_behavior"))
204
206
  self.actionTime_budget_by_behaviors_category.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_category"))
205
207
 
boris/converters.py CHANGED
@@ -27,7 +27,7 @@ import urllib.parse
27
27
  import urllib.request
28
28
 
29
29
 
30
- from PyQt5.QtWidgets import QMessageBox, QTableWidgetItem, QFileDialog, QInputDialog, QLineEdit
30
+ from PySide6.QtWidgets import QMessageBox, QTableWidgetItem, QFileDialog, QInputDialog, QLineEdit
31
31
 
32
32
  from . import dialog
33
33
  from . import config as cfg
@@ -239,8 +239,7 @@ def load_converters_from_file_repo(self, mode: str):
239
239
 
240
240
  converters_from_file = {}
241
241
  if mode == "file":
242
- fn = QFileDialog(self).getOpenFileName(self, "Load converters from file", "", "All files (*)")
243
- file_name = fn[0] if type(fn) is tuple else fn
242
+ file_name, _ = QFileDialog.getOpenFileName(self, "Load converters from file", "", "All files (*)")
244
243
 
245
244
  if file_name:
246
245
  with open(file_name, "r") as f_in: