biosignal-device-interface 0.2.2__py3-none-any.whl → 0.2.4__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 (23) hide show
  1. biosignal_device_interface/gui/device_template_widgets/core/base_multiple_devices_widget.py +17 -0
  2. biosignal_device_interface/gui/device_template_widgets/otb/otb_muovi_plus_widget.py +16 -0
  3. biosignal_device_interface/gui/device_template_widgets/otb/otb_muovi_widget.py +16 -0
  4. biosignal_device_interface/gui/device_template_widgets/otb/otb_quattrocento_light_widget.py +7 -0
  5. biosignal_device_interface/gui/device_template_widgets/otb/otb_quattrocento_widget.py +31 -0
  6. biosignal_device_interface/gui/device_template_widgets/otb/otb_syncstation_widget.py +7 -0
  7. biosignal_device_interface/gui/plot_widgets/biosignal_plot_widget.py +53 -4
  8. biosignal_device_interface/gui/ui/devices_template_widget.ui +99 -4
  9. biosignal_device_interface/gui/ui/otb_muovi_plus_template_widget.ui +7 -1
  10. biosignal_device_interface/gui/ui/otb_muovi_template_widget.ui +7 -1
  11. biosignal_device_interface/gui/ui/otb_quattrocento_light_template_widget.ui +7 -1
  12. biosignal_device_interface/gui/ui/otb_quattrocento_template_widget.ui +13 -1
  13. biosignal_device_interface/gui/ui/otb_syncstation_template_widget.ui +24 -18
  14. biosignal_device_interface/gui/ui_compiled/devices_template_widget.py +53 -5
  15. biosignal_device_interface/gui/ui_compiled/otb_muovi_plus_template_widget.py +5 -4
  16. biosignal_device_interface/gui/ui_compiled/otb_muovi_template_widget.py +5 -4
  17. biosignal_device_interface/gui/ui_compiled/otb_quattrocento_light_template_widget.py +6 -5
  18. biosignal_device_interface/gui/ui_compiled/otb_quattrocento_template_widget.py +4 -2
  19. biosignal_device_interface/gui/ui_compiled/otb_syncstation_template_widget.py +20 -19
  20. {biosignal_device_interface-0.2.2.dist-info → biosignal_device_interface-0.2.4.dist-info}/METADATA +46 -38
  21. {biosignal_device_interface-0.2.2.dist-info → biosignal_device_interface-0.2.4.dist-info}/RECORD +23 -23
  22. {biosignal_device_interface-0.2.2.dist-info → biosignal_device_interface-0.2.4.dist-info}/WHEEL +1 -1
  23. {biosignal_device_interface-0.2.2.dist-info → biosignal_device_interface-0.2.4.dist-info}/licenses/LICENSE +0 -0
@@ -50,6 +50,23 @@ class BaseMultipleDevicesWidget(QWidget):
50
50
  self.device_stacked_widget = self.ui.deviceStackedWidget
51
51
  self.device_selection_combo_box = self.ui.deviceSelectionComboBox
52
52
 
53
+ # Sync external scrollbar with scroll area's internal scrollbar
54
+ internal_scrollbar = self.ui.deviceScrollArea.verticalScrollBar()
55
+ external_scrollbar = self.ui.deviceScrollBar
56
+
57
+ # Sync scrollbar values bidirectionally
58
+ internal_scrollbar.valueChanged.connect(external_scrollbar.setValue)
59
+ external_scrollbar.valueChanged.connect(internal_scrollbar.setValue)
60
+
61
+ # Sync scrollbar range and visibility when content changes
62
+ def update_scrollbar_range(min_val: int, max_val: int):
63
+ external_scrollbar.setRange(min_val, max_val)
64
+ external_scrollbar.setVisible(max_val > min_val)
65
+
66
+ internal_scrollbar.rangeChanged.connect(update_scrollbar_range)
67
+ # Initialize visibility
68
+ external_scrollbar.setVisible(internal_scrollbar.maximum() > internal_scrollbar.minimum())
69
+
53
70
  def get_device_information(self) -> Dict[str, Union[str, int]]:
54
71
  return self._get_current_widget().get_device_information()
55
72
 
@@ -50,12 +50,15 @@ class OTBMuoviPlusWidget(BaseDeviceWidget):
50
50
  self.connect_push_button.setText("Disconnect")
51
51
  self.connect_push_button.setChecked(True)
52
52
  self.configure_push_button.setEnabled(True)
53
+ self.configure_push_button.setToolTip("Step 2: Configure device settings")
53
54
  self.connection_group_box.setEnabled(False)
54
55
  else:
55
56
  self.connect_push_button.setText("Connect")
56
57
  self.connect_push_button.setChecked(False)
57
58
  self.configure_push_button.setEnabled(False)
59
+ self.configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
58
60
  self.stream_push_button.setEnabled(False)
61
+ self.stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
59
62
  self.connection_group_box.setEnabled(True)
60
63
 
61
64
  self.connect_toggled.emit(is_connected)
@@ -73,6 +76,7 @@ class OTBMuoviPlusWidget(BaseDeviceWidget):
73
76
  def _configuration_toggled(self, is_configured: bool) -> None:
74
77
  if is_configured:
75
78
  self.stream_push_button.setEnabled(True)
79
+ self.stream_push_button.setToolTip("Step 3: Start data streaming")
76
80
 
77
81
  self.configure_toggled.emit(is_configured)
78
82
 
@@ -112,16 +116,19 @@ class OTBMuoviPlusWidget(BaseDeviceWidget):
112
116
  # Command Push Buttons
113
117
  self.connect_push_button: QPushButton = self.ui.commandConnectionPushButton
114
118
  self.connect_push_button.clicked.connect(self._toggle_connection)
119
+ self.connect_push_button.setToolTip("Step 1: Connect to the Muovi+ device")
115
120
  self._device.connect_toggled.connect(self._connection_toggled)
116
121
 
117
122
  self.configure_push_button: QPushButton = self.ui.commandConfigurationPushButton
118
123
  self.configure_push_button.clicked.connect(self._toggle_configuration)
119
124
  self.configure_push_button.setEnabled(False)
125
+ self.configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
120
126
  self._device.configure_toggled.connect(self._configuration_toggled)
121
127
 
122
128
  self.stream_push_button: QPushButton = self.ui.commandStreamPushButton
123
129
  self.stream_push_button.clicked.connect(self._toggle_stream)
124
130
  self.stream_push_button.setEnabled(False)
131
+ self.stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
125
132
  self._device.stream_toggled.connect(self._stream_toggled)
126
133
 
127
134
  # Connection parameters
@@ -148,9 +155,18 @@ class OTBMuoviPlusWidget(BaseDeviceWidget):
148
155
  # Input parameters
149
156
  self.input_parameters_group_box: QGroupBox = self.ui.inputGroupBox
150
157
  self.input_working_mode_combo_box: QComboBox = self.ui.inputWorkingModeComboBox
158
+ self.input_working_mode_combo_box.setToolTip(
159
+ "EMG: Electromyography for muscle signals\nEEG: Electroencephalography for brain signals"
160
+ )
151
161
  self.input_detection_mode_combo_box: QComboBox = (
152
162
  self.ui.inputDetectionModeComboBox
153
163
  )
164
+ self.input_detection_mode_combo_box.setToolTip(
165
+ "High Gain: Better for weak signals\nLow Gain: Better for strong signals\nImpedance Check: Verify electrode contact"
166
+ )
167
+
168
+ # Add tooltip for update button
169
+ self.connection_update_push_button.setToolTip("Refresh available device IP addresses")
154
170
 
155
171
  # Configuration parameters
156
172
  self.configuration_group_boxes: list[QGroupBox] = [
@@ -50,12 +50,15 @@ class OTBMuoviWidget(BaseDeviceWidget):
50
50
  self.connect_push_button.setText("Disconnect")
51
51
  self.connect_push_button.setChecked(True)
52
52
  self.configure_push_button.setEnabled(True)
53
+ self.configure_push_button.setToolTip("Step 2: Configure device settings")
53
54
  self.connection_group_box.setEnabled(False)
54
55
  else:
55
56
  self.connect_push_button.setText("Connect")
56
57
  self.connect_push_button.setChecked(False)
57
58
  self.configure_push_button.setEnabled(False)
59
+ self.configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
58
60
  self.stream_push_button.setEnabled(False)
61
+ self.stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
59
62
  self.connection_group_box.setEnabled(True)
60
63
 
61
64
  self.connect_toggled.emit(is_connected)
@@ -73,6 +76,7 @@ class OTBMuoviWidget(BaseDeviceWidget):
73
76
  def _configuration_toggled(self, is_configured: bool) -> None:
74
77
  if is_configured:
75
78
  self.stream_push_button.setEnabled(True)
79
+ self.stream_push_button.setToolTip("Step 3: Start data streaming")
76
80
 
77
81
  self.configure_toggled.emit(is_configured)
78
82
 
@@ -112,16 +116,19 @@ class OTBMuoviWidget(BaseDeviceWidget):
112
116
  # Command Push Buttons
113
117
  self.connect_push_button: QPushButton = self.ui.commandConnectionPushButton
114
118
  self.connect_push_button.clicked.connect(self._toggle_connection)
119
+ self.connect_push_button.setToolTip("Step 1: Connect to the Muovi device")
115
120
  self._device.connect_toggled.connect(self._connection_toggled)
116
121
 
117
122
  self.configure_push_button: QPushButton = self.ui.commandConfigurationPushButton
118
123
  self.configure_push_button.clicked.connect(self._toggle_configuration)
119
124
  self.configure_push_button.setEnabled(False)
125
+ self.configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
120
126
  self._device.configure_toggled.connect(self._configuration_toggled)
121
127
 
122
128
  self.stream_push_button: QPushButton = self.ui.commandStreamPushButton
123
129
  self.stream_push_button.clicked.connect(self._toggle_stream)
124
130
  self.stream_push_button.setEnabled(False)
131
+ self.stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
125
132
  self._device.stream_toggled.connect(self._stream_toggled)
126
133
 
127
134
  # Connection parameters
@@ -148,9 +155,18 @@ class OTBMuoviWidget(BaseDeviceWidget):
148
155
  # Input parameters
149
156
  self.input_parameters_group_box: QGroupBox = self.ui.inputGroupBox
150
157
  self.input_working_mode_combo_box: QComboBox = self.ui.inputWorkingModeComboBox
158
+ self.input_working_mode_combo_box.setToolTip(
159
+ "EMG: Electromyography for muscle signals\nEEG: Electroencephalography for brain signals"
160
+ )
151
161
  self.input_detection_mode_combo_box: QComboBox = (
152
162
  self.ui.inputDetectionModeComboBox
153
163
  )
164
+ self.input_detection_mode_combo_box.setToolTip(
165
+ "High Gain: Better for weak signals\nLow Gain: Better for strong signals\nImpedance Check: Verify electrode contact"
166
+ )
167
+
168
+ # Add tooltip for update button
169
+ self.connection_update_push_button.setToolTip("Refresh available device IP addresses")
154
170
 
155
171
  # Configuration parameters
156
172
  self.configuration_group_boxes: list[QGroupBox] = [
@@ -48,12 +48,15 @@ class OTBQuattrocentoLightWidget(BaseDeviceWidget):
48
48
  self._connect_push_button.setText("Disconnect")
49
49
  self._connect_push_button.setChecked(True)
50
50
  self._configure_push_button.setEnabled(True)
51
+ self._configure_push_button.setToolTip("Step 2: Configure device settings")
51
52
  self._connection_group_box.setEnabled(False)
52
53
  else:
53
54
  self._connect_push_button.setText("Connect")
54
55
  self._connect_push_button.setChecked(False)
55
56
  self._configure_push_button.setEnabled(False)
57
+ self._configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
56
58
  self._stream_push_button.setEnabled(False)
59
+ self._stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
57
60
  self._connection_group_box.setEnabled(True)
58
61
 
59
62
  self.connect_toggled.emit(is_connected)
@@ -81,6 +84,7 @@ class OTBQuattrocentoLightWidget(BaseDeviceWidget):
81
84
  def _configuration_toggled(self, is_configured: bool) -> None:
82
85
  if is_configured:
83
86
  self._stream_push_button.setEnabled(True)
87
+ self._stream_push_button.setToolTip("Step 3: Start data streaming")
84
88
 
85
89
  self.configure_toggled.emit(is_configured)
86
90
 
@@ -121,6 +125,7 @@ class OTBQuattrocentoLightWidget(BaseDeviceWidget):
121
125
  # Command Push Buttons
122
126
  self._connect_push_button: QPushButton = self.ui.commandConnectionPushButton
123
127
  self._connect_push_button.clicked.connect(self._toggle_connection)
128
+ self._connect_push_button.setToolTip("Step 1: Connect to the Quattrocento Light device")
124
129
  self._device.connect_toggled.connect(self._connection_toggled)
125
130
 
126
131
  self._configure_push_button: QPushButton = (
@@ -128,11 +133,13 @@ class OTBQuattrocentoLightWidget(BaseDeviceWidget):
128
133
  )
129
134
  self._configure_push_button.clicked.connect(self._toggle_configuration)
130
135
  self._configure_push_button.setEnabled(False)
136
+ self._configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
131
137
  self._device.configure_toggled.connect(self._configuration_toggled)
132
138
 
133
139
  self._stream_push_button: QPushButton = self.ui.commandStreamPushButton
134
140
  self._stream_push_button.clicked.connect(self._toggle_stream)
135
141
  self._stream_push_button.setEnabled(False)
142
+ self._stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
136
143
  self._device.stream_toggled.connect(self._stream_toggled)
137
144
 
138
145
  # Connection parameters
@@ -70,12 +70,15 @@ class OTBQuattrocentoWidget(BaseDeviceWidget):
70
70
  self._connect_push_button.setText("Disconnect")
71
71
  self._connect_push_button.setChecked(True)
72
72
  self._configure_push_button.setEnabled(True)
73
+ self._configure_push_button.setToolTip("Step 2: Configure device settings")
73
74
  self._connection_group_box.setEnabled(False)
74
75
  else:
75
76
  self._connect_push_button.setText("Connect")
76
77
  self._connect_push_button.setChecked(False)
77
78
  self._configure_push_button.setEnabled(False)
79
+ self._configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
78
80
  self._stream_push_button.setEnabled(False)
81
+ self._stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
79
82
  self._connection_group_box.setEnabled(True)
80
83
 
81
84
  self.connect_toggled.emit(is_connected)
@@ -131,6 +134,7 @@ class OTBQuattrocentoWidget(BaseDeviceWidget):
131
134
  def _configuration_toggled(self, is_configured: bool) -> None:
132
135
  if is_configured:
133
136
  self._stream_push_button.setEnabled(True)
137
+ self._stream_push_button.setToolTip("Step 3: Start data streaming")
134
138
 
135
139
  self.configure_toggled.emit(is_configured)
136
140
 
@@ -174,6 +178,7 @@ class OTBQuattrocentoWidget(BaseDeviceWidget):
174
178
  # Command Push Buttons
175
179
  self._connect_push_button: QPushButton = self.ui.commandConnectionPushButton
176
180
  self._connect_push_button.clicked.connect(self._toggle_connection)
181
+ self._connect_push_button.setToolTip("Step 1: Connect to the Quattrocento device")
177
182
  self._device.connect_toggled.connect(self._connection_toggled)
178
183
 
179
184
  self._configure_push_button: QPushButton = (
@@ -181,11 +186,13 @@ class OTBQuattrocentoWidget(BaseDeviceWidget):
181
186
  )
182
187
  self._configure_push_button.clicked.connect(self._toggle_configuration)
183
188
  self._configure_push_button.setEnabled(False)
189
+ self._configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
184
190
  self._device.configure_toggled.connect(self._configuration_toggled)
185
191
 
186
192
  self._stream_push_button: QPushButton = self.ui.commandStreamPushButton
187
193
  self._stream_push_button.clicked.connect(self._toggle_stream)
188
194
  self._stream_push_button.setEnabled(False)
195
+ self._stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
189
196
  self._device.stream_toggled.connect(self._stream_toggled)
190
197
 
191
198
  # Connection parameters
@@ -214,18 +221,33 @@ class OTBQuattrocentoWidget(BaseDeviceWidget):
214
221
  self._acquisition_sampling_frequency_combo_box: QComboBox = (
215
222
  self.ui.acquisitionSamplingFrequencyComboBox
216
223
  )
224
+ self._acquisition_sampling_frequency_combo_box.setToolTip(
225
+ "Higher frequencies capture more detail but generate more data"
226
+ )
217
227
  self._acquisition_number_of_channels_combo_box: QComboBox = (
218
228
  self.ui.acquisitionNumberOfChannelsComboBox
219
229
  )
230
+ self._acquisition_number_of_channels_combo_box.setToolTip(
231
+ "Number of EMG channels to record from"
232
+ )
220
233
  self._acquisition_decimator_check_box: QCheckBox = (
221
234
  self.ui.acquisitionDecimatorCheckBox
222
235
  )
236
+ self._acquisition_decimator_check_box.setToolTip(
237
+ "Reduce sampling rate by averaging samples (reduces data size)"
238
+ )
223
239
  self._acquisition_recording_check_box: QCheckBox = (
224
240
  self.ui.acquisitionRecordingCheckBox
225
241
  )
242
+ self._acquisition_recording_check_box.setToolTip(
243
+ "Enable on-device recording to SD card"
244
+ )
226
245
 
227
246
  # Grid selection
228
247
  self._grid_selection_group_box: QGroupBox = self.ui.gridSelectionGroupBox
248
+ self._grid_selection_group_box.setToolTip(
249
+ "Select which electrode grids to use for recording"
250
+ )
229
251
  self._grid_selection_check_box_list: list[QCheckBox] = [
230
252
  self.ui.gridOneCheckBox,
231
253
  self.ui.gridTwoCheckBox,
@@ -244,14 +266,23 @@ class OTBQuattrocentoWidget(BaseDeviceWidget):
244
266
  self._input_group_box: QGroupBox = self.ui.inputGroupBox
245
267
  self._input_channel_combo_box: QComboBox = self.ui.inputChannelComboBox
246
268
  self._input_low_pass_filter_combo_box: QComboBox = self.ui.inputLowPassComboBox
269
+ self._input_low_pass_filter_combo_box.setToolTip(
270
+ "Low-pass filter removes high-frequency noise"
271
+ )
247
272
  self._input_low_pass_default = self.ui.inputLowPassComboBox.currentIndex()
248
273
  self._input_high_pass_filter_combo_box: QComboBox = (
249
274
  self.ui.inputHighPassComboBox
250
275
  )
276
+ self._input_high_pass_filter_combo_box.setToolTip(
277
+ "High-pass filter removes low-frequency drift and motion artifacts"
278
+ )
251
279
  self._input_high_pass_default = self.ui.inputHighPassComboBox.currentIndex()
252
280
  self._input_detection_mode_combo_box: QComboBox = (
253
281
  self.ui.inputDetectionModeComboBox
254
282
  )
283
+ self._input_detection_mode_combo_box.setToolTip(
284
+ "Monopolar: Each electrode vs reference\nDifferential: Difference between adjacent electrodes"
285
+ )
255
286
 
256
287
  self._configuration_group_boxes: list[QGroupBox] = [
257
288
  self._acquisition_group_box,
@@ -59,12 +59,15 @@ class OTBSyncStationWidget(BaseDeviceWidget):
59
59
  self._command_connect_push_button.setText("Disconnect")
60
60
  self._command_connect_push_button.setChecked(True)
61
61
  self._command_configure_push_button.setEnabled(True)
62
+ self._command_configure_push_button.setToolTip("Step 2: Configure device settings")
62
63
  self._connection_group_box.setEnabled(False)
63
64
  else:
64
65
  self._command_connect_push_button.setText("Connect")
65
66
  self._command_connect_push_button.setChecked(False)
66
67
  self._command_configure_push_button.setEnabled(False)
68
+ self._command_configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
67
69
  self._command_stream_push_button.setEnabled(False)
70
+ self._command_stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
68
71
  self._connection_group_box.setEnabled(True)
69
72
 
70
73
  self.connect_toggled.emit(is_connected)
@@ -94,6 +97,7 @@ class OTBSyncStationWidget(BaseDeviceWidget):
94
97
  def _configuration_toggled(self, is_configured: bool) -> None:
95
98
  if is_configured:
96
99
  self._command_stream_push_button.setEnabled(True)
100
+ self._command_stream_push_button.setToolTip("Step 3: Start data streaming")
97
101
 
98
102
  self.configure_toggled.emit(is_configured)
99
103
 
@@ -156,6 +160,7 @@ class OTBSyncStationWidget(BaseDeviceWidget):
156
160
  self.ui.commandConnectionPushButton
157
161
  )
158
162
  self._command_connect_push_button.clicked.connect(self._toggle_connection)
163
+ self._command_connect_push_button.setToolTip("Step 1: Connect to the SyncStation device")
159
164
  self._device.connect_toggled.connect(self._connection_toggled)
160
165
 
161
166
  self._command_configure_push_button: QPushButton = (
@@ -163,11 +168,13 @@ class OTBSyncStationWidget(BaseDeviceWidget):
163
168
  )
164
169
  self._command_configure_push_button.clicked.connect(self._toggle_configuration)
165
170
  self._command_configure_push_button.setEnabled(False)
171
+ self._command_configure_push_button.setToolTip("Step 2: Configure device settings (connect first)")
166
172
  self._device.configure_toggled.connect(self._configuration_toggled)
167
173
 
168
174
  self._command_stream_push_button: QPushButton = self.ui.commandStreamPushButton
169
175
  self._command_stream_push_button.clicked.connect(self._toggle_stream)
170
176
  self._command_stream_push_button.setEnabled(False)
177
+ self._command_stream_push_button.setToolTip("Step 3: Start data streaming (configure first)")
171
178
  self._device.stream_toggled.connect(self._stream_toggled)
172
179
 
173
180
  # Connection Paramters
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  from functools import partial
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from PySide6.QtGui import QResizeEvent, QWheelEvent
5
+ from PySide6.QtGui import QResizeEvent, QWheelEvent, QFont
6
6
  from vispy import app, gloo
7
7
  from PySide6.QtWidgets import (
8
8
  QVBoxLayout,
@@ -11,6 +11,8 @@ from PySide6.QtWidgets import (
11
11
  QCheckBox,
12
12
  QGridLayout,
13
13
  QSizePolicy,
14
+ QLabel,
15
+ QFrame,
14
16
  )
15
17
  from PySide6.QtCore import Qt, Signal, QPoint
16
18
  import matplotlib.colors as mcolors
@@ -50,7 +52,45 @@ class BiosignalPlotWidget(QWidget):
50
52
  self.is_configured: bool = False
51
53
 
52
54
  def _configure_widget(self):
53
- # Create scroll_area
55
+ # Create main layout
56
+ self.setLayout(QVBoxLayout())
57
+ self.layout().setContentsMargins(0, 0, 0, 0)
58
+
59
+ # Create placeholder widget (shown when not configured)
60
+ self.placeholder_widget = QFrame(self)
61
+ self.placeholder_widget.setStyleSheet("""
62
+ QFrame {
63
+ background-color: rgba(18, 18, 18, 1);
64
+ border: 2px dashed rgba(100, 100, 100, 0.5);
65
+ border-radius: 8px;
66
+ }
67
+ """)
68
+ placeholder_layout = QVBoxLayout(self.placeholder_widget)
69
+ placeholder_layout.setAlignment(Qt.AlignCenter)
70
+
71
+ # Placeholder icon/text
72
+ self.placeholder_label = QLabel("📊")
73
+ self.placeholder_label.setStyleSheet("font-size: 48px; border: none;")
74
+ self.placeholder_label.setAlignment(Qt.AlignCenter)
75
+
76
+ self.placeholder_text = QLabel("EMG Signal Plot")
77
+ self.placeholder_text.setStyleSheet("color: rgba(150, 150, 150, 1); font-size: 16px; font-weight: bold; border: none;")
78
+ self.placeholder_text.setAlignment(Qt.AlignCenter)
79
+
80
+ self.placeholder_hint = QLabel("Connect and configure a device to view signals")
81
+ self.placeholder_hint.setStyleSheet("color: rgba(100, 100, 100, 1); font-size: 12px; border: none;")
82
+ self.placeholder_hint.setAlignment(Qt.AlignCenter)
83
+ self.placeholder_hint.setWordWrap(True)
84
+
85
+ placeholder_layout.addStretch()
86
+ placeholder_layout.addWidget(self.placeholder_label)
87
+ placeholder_layout.addWidget(self.placeholder_text)
88
+ placeholder_layout.addWidget(self.placeholder_hint)
89
+ placeholder_layout.addStretch()
90
+
91
+ self.layout().addWidget(self.placeholder_widget)
92
+
93
+ # Create scroll_area (hidden initially)
54
94
  self.scroll_area = QScrollArea(self)
55
95
  self.scroll_area.setHorizontalScrollBarPolicy(
56
96
  Qt.ScrollBarAlwaysOff
@@ -58,9 +98,8 @@ class BiosignalPlotWidget(QWidget):
58
98
  self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
59
99
  self.scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
60
100
  self.scroll_area.setLayoutDirection(Qt.RightToLeft)
101
+ self.scroll_area.hide() # Hidden until configured
61
102
 
62
- # Create a layout for the VispyFastPlotWidget
63
- self.setLayout(QVBoxLayout())
64
103
  # Add the scroll_area to the layout
65
104
  self.layout().addWidget(self.scroll_area)
66
105
 
@@ -176,6 +215,10 @@ class BiosignalPlotWidget(QWidget):
176
215
  else:
177
216
  self.container_widget_layout.setRowMinimumHeight(i, 0)
178
217
 
218
+ # Show plot, hide placeholder
219
+ self.placeholder_widget.hide()
220
+ self.scroll_area.show()
221
+
179
222
  self.is_configured = True
180
223
 
181
224
  def update_plot(self, input_data: np.ndarray) -> None:
@@ -202,6 +245,12 @@ class BiosignalPlotWidget(QWidget):
202
245
  def reset_data(self) -> None:
203
246
  self.canvas.on_reset()
204
247
 
248
+ def show_placeholder(self) -> None:
249
+ """Show the placeholder and hide the plot."""
250
+ self.scroll_area.hide()
251
+ self.placeholder_widget.show()
252
+ self.is_configured = False
253
+
205
254
  def _toggle_line(self, line_number: int, state: int) -> None:
206
255
  is_checked = state == 2
207
256
  self.lines_enabled[line_number] = is_checked
@@ -10,6 +10,12 @@
10
10
  <height>300</height>
11
11
  </rect>
12
12
  </property>
13
+ <property name="sizePolicy">
14
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
15
+ <horstretch>0</horstretch>
16
+ <verstretch>1</verstretch>
17
+ </sizepolicy>
18
+ </property>
13
19
  <property name="windowTitle">
14
20
  <string>Form</string>
15
21
  </property>
@@ -25,11 +31,100 @@
25
31
  <widget class="QComboBox" name="deviceSelectionComboBox"/>
26
32
  </item>
27
33
  <item row="1" column="0" colspan="2">
28
- <widget class="QStackedWidget" name="deviceStackedWidget">
29
- <property name="currentIndex">
30
- <number>-1</number>
34
+ <layout class="QHBoxLayout" name="scrollAreaLayout">
35
+ <property name="spacing">
36
+ <number>6</number>
31
37
  </property>
32
- </widget>
38
+ <property name="leftMargin">
39
+ <number>20</number>
40
+ </property>
41
+ <item>
42
+ <widget class="QScrollArea" name="deviceScrollArea">
43
+ <property name="sizePolicy">
44
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
45
+ <horstretch>0</horstretch>
46
+ <verstretch>1</verstretch>
47
+ </sizepolicy>
48
+ </property>
49
+ <property name="frameShape">
50
+ <enum>QFrame::Shape::NoFrame</enum>
51
+ </property>
52
+ <property name="horizontalScrollBarPolicy">
53
+ <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
54
+ </property>
55
+ <property name="verticalScrollBarPolicy">
56
+ <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
57
+ </property>
58
+ <property name="widgetResizable">
59
+ <bool>true</bool>
60
+ </property>
61
+ <widget class="QWidget" name="deviceScrollAreaContents">
62
+ <property name="geometry">
63
+ <rect>
64
+ <x>0</x>
65
+ <y>0</y>
66
+ <width>100</width>
67
+ <height>30</height>
68
+ </rect>
69
+ </property>
70
+ <layout class="QHBoxLayout" name="deviceScrollAreaLayout">
71
+ <property name="leftMargin">
72
+ <number>0</number>
73
+ </property>
74
+ <property name="topMargin">
75
+ <number>0</number>
76
+ </property>
77
+ <property name="rightMargin">
78
+ <number>0</number>
79
+ </property>
80
+ <property name="bottomMargin">
81
+ <number>0</number>
82
+ </property>
83
+ <item>
84
+ <spacer name="horizontalSpacerLeft">
85
+ <property name="orientation">
86
+ <enum>Qt::Horizontal</enum>
87
+ </property>
88
+ <property name="sizeHint" stdset="0">
89
+ <size>
90
+ <width>0</width>
91
+ <height>0</height>
92
+ </size>
93
+ </property>
94
+ </spacer>
95
+ </item>
96
+ <item>
97
+ <widget class="QStackedWidget" name="deviceStackedWidget">
98
+ <property name="currentIndex">
99
+ <number>-1</number>
100
+ </property>
101
+ </widget>
102
+ </item>
103
+ <item>
104
+ <spacer name="horizontalSpacerRight">
105
+ <property name="orientation">
106
+ <enum>Qt::Horizontal</enum>
107
+ </property>
108
+ <property name="sizeHint" stdset="0">
109
+ <size>
110
+ <width>0</width>
111
+ <height>0</height>
112
+ </size>
113
+ </property>
114
+ </spacer>
115
+ </item>
116
+ </layout>
117
+ </widget>
118
+ </widget>
119
+ </item>
120
+ <item>
121
+ <widget class="QScrollBar" name="deviceScrollBar">
122
+ <property name="orientation">
123
+ <enum>Qt::Orientation::Vertical</enum>
124
+ </property>
125
+ </widget>
126
+ </item>
127
+ </layout>
33
128
  </item>
34
129
  </layout>
35
130
  </widget>
@@ -6,13 +6,19 @@
6
6
  <rect>
7
7
  <x>0</x>
8
8
  <y>0</y>
9
- <width>400</width>
9
+ <width>340</width>
10
10
  <height>324</height>
11
11
  </rect>
12
12
  </property>
13
13
  <property name="windowTitle">
14
14
  <string>MuoviPlusForm</string>
15
15
  </property>
16
+ <property name="maximumSize">
17
+ <size>
18
+ <width>340</width>
19
+ <height>16777215</height>
20
+ </size>
21
+ </property>
16
22
  <layout class="QGridLayout" name="gridLayout">
17
23
  <item row="4" column="0">
18
24
  <spacer name="verticalSpacer">
@@ -6,13 +6,19 @@
6
6
  <rect>
7
7
  <x>0</x>
8
8
  <y>0</y>
9
- <width>400</width>
9
+ <width>340</width>
10
10
  <height>324</height>
11
11
  </rect>
12
12
  </property>
13
13
  <property name="windowTitle">
14
14
  <string>MuoviForm</string>
15
15
  </property>
16
+ <property name="maximumSize">
17
+ <size>
18
+ <width>340</width>
19
+ <height>16777215</height>
20
+ </size>
21
+ </property>
16
22
  <layout class="QGridLayout" name="gridLayout">
17
23
  <item row="4" column="0">
18
24
  <spacer name="verticalSpacer">
@@ -6,13 +6,19 @@
6
6
  <rect>
7
7
  <x>0</x>
8
8
  <y>0</y>
9
- <width>400</width>
9
+ <width>340</width>
10
10
  <height>422</height>
11
11
  </rect>
12
12
  </property>
13
13
  <property name="windowTitle">
14
14
  <string>Form</string>
15
15
  </property>
16
+ <property name="maximumSize">
17
+ <size>
18
+ <width>340</width>
19
+ <height>16777215</height>
20
+ </size>
21
+ </property>
16
22
  <layout class="QGridLayout" name="gridLayout_2">
17
23
  <item row="0" column="0">
18
24
  <widget class="QWidget" name="widget" native="true">
@@ -6,10 +6,22 @@
6
6
  <rect>
7
7
  <x>0</x>
8
8
  <y>0</y>
9
- <width>400</width>
9
+ <width>340</width>
10
10
  <height>638</height>
11
11
  </rect>
12
12
  </property>
13
+ <property name="minimumSize">
14
+ <size>
15
+ <width>0</width>
16
+ <height>550</height>
17
+ </size>
18
+ </property>
19
+ <property name="maximumSize">
20
+ <size>
21
+ <width>340</width>
22
+ <height>16777215</height>
23
+ </size>
24
+ </property>
13
25
  <property name="windowTitle">
14
26
  <string>Form</string>
15
27
  </property>