boris-behav-obs 8.9.16__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.
- boris/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +36 -39
- boris/add_modifier.py +122 -109
- boris/add_modifier_ui.py +239 -135
- boris/advanced_event_filtering.py +81 -45
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_latency.py +59 -0
- boris/analysis_plugins/irr_cohen_kappa.py +109 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
- boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
- boris/analysis_plugins/number_of_occurences.py +22 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
- boris/analysis_plugins/time_budget.py +61 -0
- boris/behav_coding_map_creator.py +228 -229
- boris/behavior_binary_table.py +33 -50
- boris/behaviors_coding_map.py +17 -18
- boris/boris_cli.py +6 -25
- boris/cmd_arguments.py +12 -1
- boris/coding_pad.py +42 -49
- boris/config.py +161 -77
- boris/config_file.py +63 -83
- boris/connections.py +112 -57
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2511 -1824
- boris/core_qrc.py +15895 -10185
- boris/core_ui.py +946 -792
- boris/db_functions.py +21 -41
- boris/dev.py +134 -0
- boris/dialog.py +505 -244
- boris/duration_widget.py +15 -20
- boris/edit_event.py +84 -28
- boris/edit_event_ui.py +214 -78
- boris/event_operations.py +517 -415
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +213 -583
- boris/export_observation.py +98 -611
- boris/external_processes.py +156 -97
- boris/geometric_measurement.py +652 -287
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +9 -9
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +325 -0
- boris/irr.py +26 -63
- boris/latency.py +34 -25
- boris/measurement_widget.py +14 -18
- boris/media_file.py +52 -84
- boris/menu_options.py +17 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv.py +1 -0
- boris/mpv2.py +732 -705
- boris/observation.py +655 -310
- boris/observation_operations.py +1036 -404
- boris/observation_ui.py +584 -356
- boris/observations_list.py +71 -53
- boris/otx_parser.py +74 -80
- boris/param_panel.py +31 -16
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +90 -60
- boris/plot_data_module.py +43 -46
- boris/plot_events.py +127 -90
- boris/plot_events_rt.py +17 -31
- boris/plot_spectrogram_rt.py +95 -30
- boris/plot_waveform_rt.py +32 -21
- boris/plugins.py +431 -0
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/preferences.py +306 -83
- boris/preferences_ui.py +685 -228
- boris/project.py +448 -293
- boris/project_functions.py +689 -254
- boris/project_import_export.py +213 -222
- boris/project_ui.py +674 -438
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +74 -48
- boris/select_observations.py +20 -199
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +53 -37
- boris/subjects_pad.py +6 -9
- boris/synthetic_time_budget.py +45 -28
- boris/time_budget_functions.py +171 -171
- boris/time_budget_widget.py +84 -114
- boris/transitions.py +41 -47
- boris/utilities.py +766 -266
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +125 -28
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +538 -0
- boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -323
- boris/boris_ui.py +0 -886
- boris/converters.ui +0 -289
- boris/core.qrc +0 -35
- boris/core.ui +0 -1543
- boris/edit_event.ui +0 -175
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -850
- boris/observation.ui +0 -773
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1069
- boris/project_server.py +0 -236
- boris/vlc.py +0 -10343
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
- boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
- boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
boris/player_dock_widget.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -20,44 +20,31 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
# add misc directory to search path for mpv-1.dll
|
|
24
|
-
import os
|
|
25
23
|
import sys
|
|
26
24
|
import logging
|
|
27
|
-
import
|
|
28
|
-
import datetime as dt
|
|
25
|
+
import functools
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
try:
|
|
27
|
+
if (sys.platform.startswith("win") or sys.platform.startswith("linux")) and ("-i" not in sys.argv) and ("--ipc" not in sys.argv):
|
|
33
28
|
from . import mpv2 as mpv
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
f_out.write("-" * 80 + "\n")
|
|
54
|
-
sys.exit()
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
from PyQt5.QtWidgets import QLabel, QDockWidget, QWidget, QHBoxLayout, QSlider, QSizePolicy, QStackedWidget
|
|
58
|
-
from PyQt5.QtCore import pyqtSignal, QEvent, Qt
|
|
59
|
-
|
|
60
|
-
import logging
|
|
29
|
+
else:
|
|
30
|
+
import ipc_mpv
|
|
31
|
+
import config as cfg
|
|
32
|
+
import gui_utilities
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
from PySide6.QtWidgets import (
|
|
36
|
+
QLabel,
|
|
37
|
+
QDockWidget,
|
|
38
|
+
QWidget,
|
|
39
|
+
QHBoxLayout,
|
|
40
|
+
QVBoxLayout,
|
|
41
|
+
QSlider,
|
|
42
|
+
QSizePolicy,
|
|
43
|
+
QStackedWidget,
|
|
44
|
+
QToolButton,
|
|
45
|
+
)
|
|
46
|
+
from PySide6.QtCore import Signal, QEvent, Qt
|
|
47
|
+
from PySide6.QtGui import QIcon, QAction
|
|
61
48
|
|
|
62
49
|
|
|
63
50
|
class Clickable_label(QLabel):
|
|
@@ -66,7 +53,7 @@ class Clickable_label(QLabel):
|
|
|
66
53
|
Label emits a signal when clicked
|
|
67
54
|
"""
|
|
68
55
|
|
|
69
|
-
mouse_pressed_signal =
|
|
56
|
+
mouse_pressed_signal = Signal(int, QEvent)
|
|
70
57
|
|
|
71
58
|
def __init__(self, id_, parent=None):
|
|
72
59
|
QLabel.__init__(self, parent)
|
|
@@ -76,23 +63,40 @@ class Clickable_label(QLabel):
|
|
|
76
63
|
"""
|
|
77
64
|
label clicked
|
|
78
65
|
"""
|
|
66
|
+
logging.debug(f"mousepress event: label {self.id_} clicked {event.pos()}")
|
|
79
67
|
|
|
80
|
-
|
|
68
|
+
"""
|
|
69
|
+
super().mousePressEvent(event)
|
|
70
|
+
x, y = event.pos().x(), event.pos().y()
|
|
71
|
+
draw_on_pixmap(self, x, y) # Example usage
|
|
72
|
+
"""
|
|
81
73
|
|
|
82
74
|
self.mouse_pressed_signal.emit(self.id_, event)
|
|
83
75
|
|
|
84
76
|
|
|
77
|
+
def mpv_logger(player_id, loglevel, component, message):
|
|
78
|
+
"""
|
|
79
|
+
redirect MPV log messages to general logging system
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
logging.debug(f"MPV player #{player_id}: [{loglevel}] {component}: {message}")
|
|
83
|
+
|
|
84
|
+
|
|
85
85
|
class DW_player(QDockWidget):
|
|
86
|
+
"""
|
|
87
|
+
Define the player class
|
|
88
|
+
"""
|
|
86
89
|
|
|
87
|
-
key_pressed_signal =
|
|
88
|
-
volume_slider_moved_signal =
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
key_pressed_signal = Signal(QEvent)
|
|
91
|
+
volume_slider_moved_signal = Signal(int, int)
|
|
92
|
+
mute_action_triggered_signal = Signal(int)
|
|
93
|
+
view_signal = Signal(int, str, int)
|
|
94
|
+
resize_signal = Signal(int)
|
|
91
95
|
|
|
92
96
|
def __init__(self, id_, parent=None):
|
|
93
97
|
super().__init__(parent)
|
|
98
|
+
# print("ipc", parent.MPV_IPC_MODE)
|
|
94
99
|
self.id_ = id_
|
|
95
|
-
self.zoomed = False
|
|
96
100
|
self.setWindowTitle(f"Player #{id_ + 1}")
|
|
97
101
|
self.setObjectName(f"player{id_ + 1}")
|
|
98
102
|
|
|
@@ -101,23 +105,46 @@ class DW_player(QDockWidget):
|
|
|
101
105
|
|
|
102
106
|
self.videoframe = QWidget(self)
|
|
103
107
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
if parent.MPV_IPC_MODE:
|
|
109
|
+
self.player = ipc_mpv.IPC_MPV(socket_path=f"{cfg.MPV_SOCKET}{self.id_}")
|
|
110
|
+
else:
|
|
111
|
+
self.player = mpv.MPV(
|
|
112
|
+
wid=str(int(self.videoframe.winId())),
|
|
113
|
+
vo="x11" if sys.platform.startswith("linux") else "",
|
|
114
|
+
log_handler=functools.partial(mpv_logger, self.id_),
|
|
115
|
+
loglevel="debug",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
self.player.screenshot_format = "png"
|
|
112
119
|
self.hlayout.addWidget(self.videoframe)
|
|
113
120
|
|
|
121
|
+
# layout volume slider and mute button
|
|
122
|
+
self.vlayout = QVBoxLayout()
|
|
123
|
+
|
|
124
|
+
# volume slider
|
|
114
125
|
self.volume_slider = QSlider(Qt.Vertical, self)
|
|
115
126
|
self.volume_slider.setFocusPolicy(Qt.NoFocus)
|
|
116
127
|
self.volume_slider.setMaximum(100)
|
|
117
128
|
self.volume_slider.setValue(50)
|
|
118
129
|
self.volume_slider.sliderMoved.connect(self.volume_slider_moved)
|
|
119
130
|
|
|
120
|
-
self.
|
|
131
|
+
self.vlayout.addWidget(self.volume_slider)
|
|
132
|
+
|
|
133
|
+
# mute button
|
|
134
|
+
self.mute_button = QToolButton()
|
|
135
|
+
self.mute_button.setFocusPolicy(Qt.NoFocus)
|
|
136
|
+
self.mute_button.setAutoRaise(True)
|
|
137
|
+
self.mute_action = QAction()
|
|
138
|
+
|
|
139
|
+
theme_mode = gui_utilities.theme_mode()
|
|
140
|
+
|
|
141
|
+
self.mute_action.setIcon(QIcon(f":/volume_xmark_{theme_mode}"))
|
|
142
|
+
self.mute_action.triggered.connect(self.mute_action_triggered)
|
|
143
|
+
self.mute_button.setDefaultAction(self.mute_action)
|
|
144
|
+
|
|
145
|
+
self.vlayout.addWidget(self.mute_button)
|
|
146
|
+
|
|
147
|
+
self.hlayout.addLayout(self.vlayout)
|
|
121
148
|
|
|
122
149
|
self.stack1.setLayout(self.hlayout)
|
|
123
150
|
|
|
@@ -146,21 +173,24 @@ class DW_player(QDockWidget):
|
|
|
146
173
|
"""
|
|
147
174
|
self.volume_slider_moved_signal.emit(self.id_, self.volume_slider.value())
|
|
148
175
|
|
|
149
|
-
def
|
|
176
|
+
def mute_action_triggered(self):
|
|
150
177
|
"""
|
|
151
|
-
emit signal when
|
|
178
|
+
emit signal when mute action is triggered
|
|
152
179
|
"""
|
|
153
|
-
|
|
180
|
+
theme_mode = gui_utilities.theme_mode()
|
|
181
|
+
if self.player.mute:
|
|
182
|
+
self.mute_action.setIcon(QIcon(f":/volume_xmark_{theme_mode}"))
|
|
183
|
+
else:
|
|
184
|
+
self.mute_action.setIcon(QIcon(f":/volume_off_{theme_mode}"))
|
|
185
|
+
self.mute_action_triggered_signal.emit(self.id_)
|
|
154
186
|
|
|
155
|
-
|
|
156
|
-
def view_signal_triggered(self, msg, button):
|
|
187
|
+
def keyPressEvent(self, event):
|
|
157
188
|
"""
|
|
158
|
-
|
|
189
|
+
emit signal when key pressed on dock widget
|
|
159
190
|
"""
|
|
160
|
-
self.
|
|
161
|
-
'''
|
|
191
|
+
self.key_pressed_signal.emit(event)
|
|
162
192
|
|
|
163
|
-
def resizeEvent(self,
|
|
193
|
+
def resizeEvent(self, _):
|
|
164
194
|
"""
|
|
165
195
|
emits signal when dockwidget resized
|
|
166
196
|
"""
|
boris/plot_data_module.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
This program is free software; you can redistribute it and/or modify
|
|
@@ -29,8 +29,8 @@ from decimal import Decimal as dec
|
|
|
29
29
|
import numpy as np
|
|
30
30
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
31
31
|
from matplotlib.figure import Figure
|
|
32
|
-
from
|
|
33
|
-
from
|
|
32
|
+
from PySide6.QtCore import Signal, QEvent, QThread, QObject, Slot, QTimer
|
|
33
|
+
from PySide6.QtWidgets import (
|
|
34
34
|
QSizePolicy,
|
|
35
35
|
QWidget,
|
|
36
36
|
QPushButton,
|
|
@@ -42,6 +42,7 @@ from PyQt5.QtWidgets import (
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
from . import utilities as util
|
|
45
|
+
from . import config as cfg
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
class MyMplCanvas(FigureCanvas):
|
|
@@ -55,11 +56,10 @@ class MyMplCanvas(FigureCanvas):
|
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
class Plot_data(QWidget):
|
|
58
|
-
|
|
59
|
-
send_fig = pyqtSignal(float)
|
|
59
|
+
send_fig = Signal(float)
|
|
60
60
|
|
|
61
61
|
# send keypress event to mainwindow
|
|
62
|
-
sendEvent =
|
|
62
|
+
sendEvent = Signal(QEvent)
|
|
63
63
|
|
|
64
64
|
def __init__(
|
|
65
65
|
self,
|
|
@@ -75,7 +75,6 @@ class Plot_data(QWidget):
|
|
|
75
75
|
column_converter,
|
|
76
76
|
log_level="",
|
|
77
77
|
):
|
|
78
|
-
|
|
79
78
|
super().__init__()
|
|
80
79
|
|
|
81
80
|
self.installEventFilter(self)
|
|
@@ -133,14 +132,18 @@ class Plot_data(QWidget):
|
|
|
133
132
|
substract_first_value,
|
|
134
133
|
converters=converters,
|
|
135
134
|
column_converter=column_converter,
|
|
136
|
-
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
print(f"{error_msg=}")
|
|
137
138
|
|
|
138
139
|
if not result:
|
|
139
140
|
self.error_msg = error_msg
|
|
140
141
|
return
|
|
141
142
|
|
|
143
|
+
"""
|
|
142
144
|
logging.debug("data[50]: {}".format(data[:50]))
|
|
143
145
|
logging.debug("shape: {}".format(data.shape))
|
|
146
|
+
"""
|
|
144
147
|
|
|
145
148
|
if data.shape == (0,):
|
|
146
149
|
self.error_msg = "Empty input file"
|
|
@@ -150,7 +153,7 @@ class Plot_data(QWidget):
|
|
|
150
153
|
data = data[data[:, 0].argsort()]
|
|
151
154
|
|
|
152
155
|
# unique
|
|
153
|
-
|
|
156
|
+
_, idx = np.unique(data[:, 0], return_index=True)
|
|
154
157
|
data = data[idx]
|
|
155
158
|
|
|
156
159
|
# time
|
|
@@ -162,24 +165,28 @@ class Plot_data(QWidget):
|
|
|
162
165
|
# check if time is linear
|
|
163
166
|
diff = set(np.round(np.diff(data, axis=0)[:, 0], 4))
|
|
164
167
|
|
|
168
|
+
# check if only one time value is available
|
|
169
|
+
if not diff:
|
|
170
|
+
self.error_msg = "only one time value is present"
|
|
171
|
+
return
|
|
172
|
+
|
|
165
173
|
if min(diff) == 0:
|
|
166
174
|
self.error_msg = "more values for same time"
|
|
167
175
|
return
|
|
168
176
|
|
|
169
|
-
logging.debug(f"diff: {diff}")
|
|
177
|
+
"""logging.debug(f"diff: {diff}")"""
|
|
170
178
|
|
|
171
179
|
min_time_step = min(diff)
|
|
172
180
|
|
|
173
|
-
logging.debug(f"min_time_step: {min_time_step}")
|
|
181
|
+
"""logging.debug(f"min_time_step: {min_time_step}")"""
|
|
174
182
|
|
|
175
183
|
# check if sampling rate is not constant
|
|
176
184
|
if len(diff) != 1:
|
|
177
|
-
|
|
178
|
-
logging.debug("len diff != 1")
|
|
185
|
+
"""logging.debug("len diff != 1")"""
|
|
179
186
|
|
|
180
187
|
min_time_step = min(diff)
|
|
181
188
|
|
|
182
|
-
logging.debug(f"min_time_step: {min_time_step}")
|
|
189
|
+
"""logging.debug(f"min_time_step: {min_time_step}")"""
|
|
183
190
|
|
|
184
191
|
# increase value for low sampling rate (> 1 s)
|
|
185
192
|
if min_time_step > 1:
|
|
@@ -189,7 +196,7 @@ class Plot_data(QWidget):
|
|
|
189
196
|
data = np.array((x2, np.interp(x2, data[:, 0], data[:, 1]))).T
|
|
190
197
|
del x2
|
|
191
198
|
|
|
192
|
-
logging.debug(f"data[:,0]: {data[:, 0]}")
|
|
199
|
+
"""logging.debug(f"data[:,0]: {data[:, 0]}")"""
|
|
193
200
|
|
|
194
201
|
# time
|
|
195
202
|
min_time_value, max_time_value = min(data[:, 0]), max(data[:, 0])
|
|
@@ -204,9 +211,9 @@ class Plot_data(QWidget):
|
|
|
204
211
|
data = data[0 :: int(round(0.04 / min_time_step, 2))]
|
|
205
212
|
min_time_step = 0.04
|
|
206
213
|
|
|
207
|
-
logging.debug(f"new data after subsampling: {data[:50]}")
|
|
214
|
+
"""logging.debug(f"new data after subsampling: {data[:50]}")"""
|
|
208
215
|
|
|
209
|
-
|
|
216
|
+
min_var_value, max_var_value = min(data[:, 1]), max(data[:, 1])
|
|
210
217
|
|
|
211
218
|
max_frequency = 1 / min_time_step
|
|
212
219
|
|
|
@@ -241,7 +248,7 @@ class Plot_data(QWidget):
|
|
|
241
248
|
if min_time_step < 0.2:
|
|
242
249
|
self.time_out = 200
|
|
243
250
|
else:
|
|
244
|
-
self.time_out = min_time_step * 1000
|
|
251
|
+
self.time_out = round(min_time_step * 1000)
|
|
245
252
|
|
|
246
253
|
def eventFilter(self, receiver, event):
|
|
247
254
|
"""
|
|
@@ -281,40 +288,31 @@ class Plot_data(QWidget):
|
|
|
281
288
|
|
|
282
289
|
# Slot receives data and plots it
|
|
283
290
|
def plot(self, x, y, position_data, position_start, min_value, max_value, position_end):
|
|
284
|
-
|
|
285
|
-
logging.debug(f"len x (plot): {len(x)}")
|
|
286
|
-
logging.debug(f"len y (plot): {len(y)}")
|
|
287
|
-
|
|
288
291
|
# print current value
|
|
289
292
|
try:
|
|
290
293
|
if x[0] == 0:
|
|
291
294
|
self.lb_value.setText(str(round(y[position_data], 3)))
|
|
292
295
|
else:
|
|
293
296
|
self.lb_value.setText(str(round(y[len(y) // 2], 3)))
|
|
294
|
-
except:
|
|
297
|
+
except Exception:
|
|
295
298
|
self.lb_value.setText("Read error")
|
|
296
299
|
|
|
297
300
|
try:
|
|
298
301
|
self.myplot.axes.clear()
|
|
299
|
-
|
|
300
302
|
self.myplot.axes.set_title(self.plot_title)
|
|
301
|
-
|
|
302
303
|
self.myplot.axes.set_xlim(position_start, position_end)
|
|
303
|
-
|
|
304
304
|
self.myplot.axes.set_ylabel(self.y_label, rotation=90, labelpad=10)
|
|
305
305
|
self.myplot.axes.set_ylim((min_value, max_value))
|
|
306
|
-
|
|
307
306
|
self.myplot.axes.plot(x, y, self.plot_style)
|
|
308
|
-
self.myplot.axes.axvline(x=position_data, color=
|
|
307
|
+
self.myplot.axes.axvline(x=position_data, color=cfg.REALTIME_PLOT_CURSOR_COLOR, linestyle="-")
|
|
309
308
|
|
|
310
309
|
self.myplot.draw()
|
|
311
|
-
except:
|
|
312
|
-
logging.debug("error")
|
|
313
|
-
pass # only for protection agains crash
|
|
310
|
+
except Exception:
|
|
311
|
+
logging.debug(f"error in plotting external data: {sys.exc_info()[1]}")
|
|
314
312
|
|
|
315
313
|
|
|
316
314
|
class Plotter(QObject):
|
|
317
|
-
return_fig =
|
|
315
|
+
return_fig = Signal(
|
|
318
316
|
np.ndarray, # x array
|
|
319
317
|
np.ndarray, # y array
|
|
320
318
|
float, # position_data
|
|
@@ -324,9 +322,8 @@ class Plotter(QObject):
|
|
|
324
322
|
float, # position end
|
|
325
323
|
)
|
|
326
324
|
|
|
327
|
-
@
|
|
325
|
+
@Slot(float)
|
|
328
326
|
def replot(self, current_time): # time_ in s
|
|
329
|
-
|
|
330
327
|
logging.debug("current_time: {}".format(current_time))
|
|
331
328
|
|
|
332
329
|
current_discrete_time = round(round(current_time / self.min_time_step) * self.min_time_step, 2)
|
|
@@ -337,7 +334,6 @@ class Plotter(QObject):
|
|
|
337
334
|
freq_interval = int(round(self.interval / self.min_time_step))
|
|
338
335
|
|
|
339
336
|
if self.min_time_value <= current_discrete_time <= self.max_time_value:
|
|
340
|
-
|
|
341
337
|
logging.debug("self.min_time_value <= current_discrete_time <= self.max_time_value")
|
|
342
338
|
|
|
343
339
|
idx = np.where(self.data[:, 0] == current_discrete_time)[0]
|
|
@@ -345,7 +341,6 @@ class Plotter(QObject):
|
|
|
345
341
|
idx = np.where(abs(self.data[:, 0] - current_discrete_time) <= 0.02)[0]
|
|
346
342
|
|
|
347
343
|
if len(idx):
|
|
348
|
-
|
|
349
344
|
position_data = idx[0]
|
|
350
345
|
|
|
351
346
|
logging.debug(f"position data: {position_data}")
|
|
@@ -382,14 +377,9 @@ class Plotter(QObject):
|
|
|
382
377
|
d = np.array([np.nan] * int(self.interval / self.min_time_step)).T
|
|
383
378
|
|
|
384
379
|
elif current_time > self.max_time_value:
|
|
385
|
-
|
|
386
380
|
logging.debug(f"self.interval/self.min_time_step/2: {self.interval / self.min_time_step / 2}")
|
|
387
381
|
|
|
388
|
-
dim_footer = int(
|
|
389
|
-
round(
|
|
390
|
-
(current_time - self.max_time_value) / self.min_time_step + self.interval / self.min_time_step / 2
|
|
391
|
-
)
|
|
392
|
-
)
|
|
382
|
+
dim_footer = int(round((current_time - self.max_time_value) / self.min_time_step + self.interval / self.min_time_step / 2))
|
|
393
383
|
|
|
394
384
|
footer = np.array([np.nan] * dim_footer).T
|
|
395
385
|
logging.debug(f"len footer: {len(footer)}")
|
|
@@ -398,7 +388,6 @@ class Plotter(QObject):
|
|
|
398
388
|
logging.debug(f"a: {a}")
|
|
399
389
|
|
|
400
390
|
if a >= 0:
|
|
401
|
-
|
|
402
391
|
logging.debug("a>=0")
|
|
403
392
|
|
|
404
393
|
st = int(round(len(self.data) - a))
|
|
@@ -424,7 +413,6 @@ class Plotter(QObject):
|
|
|
424
413
|
logging.debug(f"len d a<0: {len(d)}")
|
|
425
414
|
|
|
426
415
|
elif current_time < self.min_time_value:
|
|
427
|
-
|
|
428
416
|
x = (self.min_time_value - current_time) / self.min_time_step
|
|
429
417
|
dim_header = int(round(self.interval / self.min_time_step / 2 + x))
|
|
430
418
|
header = np.array([np.nan] * dim_header).T
|
|
@@ -444,7 +432,11 @@ class Plotter(QObject):
|
|
|
444
432
|
|
|
445
433
|
logging.debug(f"self.min_time_step: {self.min_time_step}")
|
|
446
434
|
|
|
447
|
-
x = np.arange(
|
|
435
|
+
x = np.arange(
|
|
436
|
+
current_time - self.interval // 2,
|
|
437
|
+
current_time + self.interval // 2,
|
|
438
|
+
self.min_time_step,
|
|
439
|
+
)
|
|
448
440
|
|
|
449
441
|
logging.debug(f"len x 1: {len(x)}")
|
|
450
442
|
|
|
@@ -489,7 +481,12 @@ if __name__ == "__main__":
|
|
|
489
481
|
"convert_time_ecg": {
|
|
490
482
|
"name": "convert_time_ecg",
|
|
491
483
|
"description": "convert '%d/%m/%Y %H:%M:%S.%f' in seconds from epoch",
|
|
492
|
-
"code":
|
|
484
|
+
"code": (
|
|
485
|
+
"\nimport datetime\n"
|
|
486
|
+
"epoch = datetime.datetime.utcfromtimestamp(0)\n"
|
|
487
|
+
'datetime_format = "%d/%m/%Y %H:%M:%S.%f"\n\n'
|
|
488
|
+
"OUTPUT = (datetime.datetime.strptime(INPUT, datetime_format) - epoch).total_seconds()\n"
|
|
489
|
+
),
|
|
493
490
|
},
|
|
494
491
|
"hhmmss_2_seconds": {
|
|
495
492
|
"name": "hhmmss_2_seconds",
|