biosignal-device-interface 0.1.0__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 (35) hide show
  1. biosignal_device_interface/__init__.py +4 -0
  2. biosignal_device_interface/constants/__init__.py +0 -0
  3. biosignal_device_interface/constants/devices/__init__.py +0 -0
  4. biosignal_device_interface/constants/devices/core/base_device_constants.py +51 -0
  5. biosignal_device_interface/constants/devices/otb/otb_muovi_constants.py +129 -0
  6. biosignal_device_interface/constants/devices/otb/otb_quattrocento_constants.py +59 -0
  7. biosignal_device_interface/constants/plots/color_palette.py +59 -0
  8. biosignal_device_interface/devices/__init__.py +9 -0
  9. biosignal_device_interface/devices/core/__init__.py +0 -0
  10. biosignal_device_interface/devices/core/base_device.py +413 -0
  11. biosignal_device_interface/devices/otb/__init__.py +9 -0
  12. biosignal_device_interface/devices/otb/otb_muovi.py +291 -0
  13. biosignal_device_interface/devices/otb/otb_quattrocento.py +235 -0
  14. biosignal_device_interface/gui/device_template_widgets/__init__.py +6 -0
  15. biosignal_device_interface/gui/device_template_widgets/all_devices_widget.py +39 -0
  16. biosignal_device_interface/gui/device_template_widgets/core/base_device_widget.py +121 -0
  17. biosignal_device_interface/gui/device_template_widgets/core/base_multiple_devices_widget.py +105 -0
  18. biosignal_device_interface/gui/device_template_widgets/otb/__init__.py +10 -0
  19. biosignal_device_interface/gui/device_template_widgets/otb/otb_devices_widget.py +32 -0
  20. biosignal_device_interface/gui/device_template_widgets/otb/otb_muovi_plus_widget.py +158 -0
  21. biosignal_device_interface/gui/device_template_widgets/otb/otb_muovi_widget.py +158 -0
  22. biosignal_device_interface/gui/device_template_widgets/otb/otb_quattrocento_light_widget.py +170 -0
  23. biosignal_device_interface/gui/plot_widgets/biosignal_plot_widget.py +496 -0
  24. biosignal_device_interface/gui/ui/devices_template_widget.ui +38 -0
  25. biosignal_device_interface/gui/ui/otb_muovi_plus_template_widget.ui +171 -0
  26. biosignal_device_interface/gui/ui/otb_muovi_template_widget.ui +171 -0
  27. biosignal_device_interface/gui/ui/otb_quattrocento_light_template_widget.ui +266 -0
  28. biosignal_device_interface/gui/ui_compiled/devices_template_widget.py +56 -0
  29. biosignal_device_interface/gui/ui_compiled/otb_muovi_plus_template_widget.py +153 -0
  30. biosignal_device_interface/gui/ui_compiled/otb_muovi_template_widget.py +153 -0
  31. biosignal_device_interface/gui/ui_compiled/otb_quattrocento_light_template_widget.py +217 -0
  32. biosignal_device_interface-0.1.0.dist-info/LICENSE +395 -0
  33. biosignal_device_interface-0.1.0.dist-info/METADATA +138 -0
  34. biosignal_device_interface-0.1.0.dist-info/RECORD +35 -0
  35. biosignal_device_interface-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,496 @@
1
+ from __future__ import annotations
2
+ from functools import partial
3
+ from typing import TYPE_CHECKING
4
+
5
+ from PySide6.QtGui import QResizeEvent, QWheelEvent
6
+ from vispy import app, gloo
7
+ from PySide6.QtWidgets import (
8
+ QVBoxLayout,
9
+ QWidget,
10
+ QScrollArea,
11
+ QCheckBox,
12
+ QGridLayout,
13
+ QSizePolicy,
14
+ )
15
+ from PySide6.QtCore import Qt, Signal, QPoint
16
+ import matplotlib.colors as mcolors
17
+ import numpy as np
18
+ from scipy.signal import resample
19
+
20
+ from biosignal_device_interface.constants.plots.color_palette import (
21
+ COLOR_PALETTE_RGB_DARK,
22
+ COLOR_PALETTE_RGB_LIGHT,
23
+ )
24
+
25
+
26
+ class BiosignalPlotWidget(QWidget):
27
+ bad_channels_updated: Signal = Signal(np.ndarray)
28
+
29
+ def __init__(self, parent=None):
30
+ super().__init__(parent)
31
+
32
+ self.main_window = parent
33
+ self.number_of_lines: int | None = None
34
+ self.number_of_vertices: int | None = None
35
+ self.internal_sampling_frequency_threshold = 256
36
+ self.external_sampling_frequency: int | None = None
37
+ self.internal_sampling_frequency: int | None = None
38
+ self.sampling_factor: int | None = None
39
+ self.downsample_buffer: np.ndarray | None = None
40
+
41
+ self.line_checkboxes: list[QCheckBox] = []
42
+ self.lines_enabled: np.ndarray | None = None
43
+
44
+ self.canvas_layout: QVBoxLayout | None = None
45
+
46
+ self.color_dict = mcolors.CSS4_COLORS
47
+
48
+ self._configure_widget()
49
+
50
+ self.is_configured: bool = False
51
+
52
+ def _configure_widget(self):
53
+ # Create scroll_area
54
+ self.scroll_area = QScrollArea(self)
55
+ self.scroll_area.setHorizontalScrollBarPolicy(
56
+ Qt.ScrollBarAlwaysOff
57
+ ) # Disable horizontal scrollbar
58
+ self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
59
+ self.scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
60
+ self.scroll_area.setLayoutDirection(Qt.RightToLeft)
61
+
62
+ # Create a layout for the VispyFastPlotWidget
63
+ self.setLayout(QVBoxLayout())
64
+ # Add the scroll_area to the layout
65
+ self.layout().addWidget(self.scroll_area)
66
+
67
+ # Create a layout for the Scroll Area
68
+ self.scroll_area_layout = QVBoxLayout()
69
+ # Add the layout to the scroll_area
70
+ self.scroll_area.setLayout(self.scroll_area_layout)
71
+
72
+ # Make the plot_widget a child of the scroll_area
73
+ self.container_widget = QWidget()
74
+ self.container_widget.setSizePolicy(
75
+ QSizePolicy.Expanding, QSizePolicy.Expanding
76
+ )
77
+
78
+ self.scroll_area.setWidget(self.container_widget)
79
+
80
+ self.container_widget_layout = QGridLayout()
81
+ self.container_widget.setLayout(self.container_widget_layout)
82
+ self.container_widget.setLayoutDirection(Qt.LeftToRight)
83
+
84
+ self.container_widget_layout.setColumnStretch(0, 0)
85
+ self.container_widget_layout.setColumnStretch(1, 1)
86
+
87
+ self.canvas = VispyFastPlotCanvas(
88
+ parent=self.main_window, scroll_area=self.scroll_area
89
+ )
90
+
91
+ self.max_texture_size = gloo.gl.glGetParameter(gloo.gl.GL_MAX_TEXTURE_SIZE)
92
+ self.plot_widget = self.canvas.native
93
+ self.container_widget_layout.addWidget(self.plot_widget, 0, 1, 0, 1)
94
+
95
+ def configure(
96
+ self,
97
+ lines: int,
98
+ sampling_freuqency: int = 2000,
99
+ display_time: int = 10,
100
+ background_color: np.ndarray = np.array([18.0, 18.0, 18.0, 1]),
101
+ ):
102
+ self.number_of_lines = lines
103
+ self.external_sampling_frequency = sampling_freuqency
104
+
105
+ if (
106
+ self.external_sampling_frequency
107
+ > self.internal_sampling_frequency_threshold
108
+ ):
109
+ self.sampling_factor = int(
110
+ self.external_sampling_frequency
111
+ / self.internal_sampling_frequency_threshold
112
+ )
113
+
114
+ self.internal_sampling_frequency = (
115
+ self.external_sampling_frequency // self.sampling_factor
116
+ )
117
+
118
+ else:
119
+ self.internal_sampling_frequency = self.external_sampling_frequency
120
+
121
+ self.number_of_vertices = int(display_time * self.internal_sampling_frequency)
122
+
123
+ background_color = self._check_background_color_for_format(background_color)
124
+ self.setStyleSheet(
125
+ f"background-color: rgba({background_color[0]}, {background_color[1]}, {background_color[2]}, {background_color[3]});"
126
+ )
127
+ self.container_widget.setStyleSheet(
128
+ f"background-color: rgba({background_color[0]}, {background_color[1]}, {background_color[2]}, {background_color[3]});"
129
+ )
130
+
131
+ self.space_for_each_line = min(
132
+ int(self.max_texture_size // self.number_of_lines // 1.5), 50
133
+ )
134
+
135
+ self.canvas.configure(
136
+ self.number_of_lines, self.number_of_vertices, background_color
137
+ )
138
+
139
+ # Clear the layout
140
+ for i in reversed(range(self.container_widget_layout.rowCount())):
141
+ # Remove the widget from the layout
142
+ if self.container_widget_layout.itemAt(i) is not None:
143
+ self.container_widget_layout.itemAt(i).widget().setParent(None)
144
+
145
+ lines_space = self.space_for_each_line * self.number_of_lines
146
+ self.container_widget.setFixedHeight((lines_space))
147
+
148
+ self.line_checkboxes = []
149
+ self.number_of_lines_enabled = []
150
+ self.lines_enabled = np.ones((self.number_of_lines,)).astype(bool)
151
+
152
+ for i in range(self.number_of_lines):
153
+ checkbox = QCheckBox(f"Line {i + 1}")
154
+ checkbox.setChecked(True)
155
+ checkbox.stateChanged.connect(partial(self._toggle_line, i))
156
+ checkbox.setStyleSheet("padding-left: 10px;")
157
+ checkbox.setFixedHeight(self.space_for_each_line)
158
+ self.line_checkboxes.append(checkbox)
159
+ self.container_widget_layout.addWidget(checkbox, i, 0)
160
+
161
+ self.container_widget_layout.removeWidget(self.plot_widget)
162
+ self.container_widget_layout.addWidget(
163
+ self.plot_widget, 0, 1, self.number_of_lines, 1
164
+ )
165
+
166
+ # Set the standardized height for each row
167
+ for i in range(self.container_widget_layout.rowCount()):
168
+ if i < self.number_of_lines:
169
+ self.container_widget_layout.setRowMinimumHeight(
170
+ i, self.space_for_each_line
171
+ ) # Set the minimum height of each row to 100 pixels
172
+ else:
173
+ self.container_widget_layout.setRowMinimumHeight(i, 0)
174
+
175
+ self.is_configured = True
176
+
177
+ def update_plot(self, input_data: np.ndarray) -> None:
178
+ # Downsample input_data with external sampling frequency to match internal sampling frequency
179
+ if self.external_sampling_frequency != self.internal_sampling_frequency:
180
+ if self.downsample_buffer is not None:
181
+ input_data = np.hstack((self.downsample_buffer, input_data))
182
+ self.downsample_buffer = None
183
+
184
+ input_samples = input_data.shape[1]
185
+ left_over_samples = input_samples % self.sampling_factor
186
+ if left_over_samples > 0:
187
+ self.downsample_buffer = input_data[:, -left_over_samples:]
188
+ input_data = input_data[:, :-left_over_samples]
189
+ output_samples = input_samples // self.sampling_factor
190
+ input_data = resample(
191
+ input_data,
192
+ output_samples,
193
+ axis=1,
194
+ )
195
+
196
+ self.canvas.on_update(input_data)
197
+
198
+ def reset_data(self) -> None:
199
+ self.canvas.on_reset()
200
+
201
+ def _toggle_line(self, line_number: int, state: int) -> None:
202
+ is_checked = state == 2
203
+ self.lines_enabled[line_number] = is_checked
204
+ self.canvas.on_update_color(line_number, not is_checked)
205
+ self.bad_channels_updated.emit(self.lines_enabled)
206
+
207
+ def set_lines_enabled(self, indices: list) -> None:
208
+ self.lines_enabled = np.ones((self.number_of_lines,)).astype(bool)
209
+ self.lines_enabled[indices] = False
210
+
211
+ def resizeEvent(self, event: QResizeEvent) -> None:
212
+ self.container_widget.setFixedWidth(self.scroll_area.width())
213
+
214
+ return super().resizeEvent(event)
215
+
216
+ def _check_background_color_for_format(
217
+ self, input_color: str | list | np.ndarray
218
+ ) -> np.ndarray:
219
+ if isinstance(input_color, str):
220
+ if input_color.startswith("#"):
221
+ input_color = np.array(
222
+ [int(input_color[i : i + 2], 16) / 255 for i in (1, 3, 5)] + [1]
223
+ )
224
+ elif input_color.startswith("rgb"):
225
+ input_color = np.array(
226
+ [int(i) for i in input_color[4:-1].split(",")] + [1]
227
+ )
228
+ elif input_color.startswith("rgba"):
229
+ input_color = np.array([int(i) for i in input_color[5:-1].split(",")])
230
+ # Check if color is given as "red" or "blue" etc.
231
+ elif input_color in self.color_dict:
232
+ input_color = np.array(
233
+ [
234
+ int(self.color_dict[input_color][i : i + 2], 16) / 255
235
+ for i in (1, 3, 5)
236
+ ]
237
+ + [1]
238
+ )
239
+
240
+ elif isinstance(input_color, list):
241
+ input_color = np.array(input_color)
242
+
243
+ if np.max(input_color) > 1:
244
+ input_color = input_color / 255
245
+
246
+ if input_color.shape[0] == 3:
247
+ input_color = np.append(input_color, 1)
248
+
249
+ return input_color
250
+
251
+
252
+ class VispyFastPlotCanvas(app.Canvas):
253
+ def __init__(self, scroll_area: QScrollArea, parent=None):
254
+ super().__init__(keys=None, parent=parent)
255
+
256
+ self.main_widget = parent
257
+ self.plot_scroll_area = scroll_area
258
+ self.line_data = None
259
+ self.line_colors = None
260
+ self._init_shaders()
261
+ self.program = gloo.Program(self.vert_shader, self.frag_shader)
262
+
263
+ self.background_color: np.ndarray = None
264
+
265
+ def configure(
266
+ self,
267
+ lines: int,
268
+ vertices: int,
269
+ background_color: np.ndarray = np.array([18.0, 18.0, 18.0]),
270
+ line_color: np.ndarray | None = None,
271
+ ):
272
+ """_summary_
273
+
274
+ Args:
275
+ lines (int): _description_
276
+ vertices (int): _description_
277
+ background_color (str | np.ndarray, optional): _description_. Defaults to "black".
278
+ line_color (np.ndarray | None, optional): Numpy array of rgb value(s). RGB values range between 0 and 255. Defaults to None.
279
+ """
280
+
281
+ self.lines = lines
282
+ self.vertices = vertices
283
+ self.background_color = background_color
284
+
285
+ # Generate template signal
286
+ self.line_data = np.zeros((self.lines, self.vertices)).astype(np.float32)
287
+
288
+ colors = []
289
+ if line_color is None:
290
+ for line in range(self.lines):
291
+ if self.is_light_color(self.background_color):
292
+ colors.append(
293
+ COLOR_PALETTE_RGB_LIGHT[line % len(COLOR_PALETTE_RGB_LIGHT)]
294
+ / 255.0
295
+ )
296
+ else:
297
+ colors.append(
298
+ COLOR_PALETTE_RGB_DARK[line % len(COLOR_PALETTE_RGB_DARK)]
299
+ / 255.0
300
+ )
301
+ else:
302
+ for line in range(self.lines):
303
+ colors.append(line_color[line % len(line_color)] / 255.0)
304
+
305
+ self.line_colors = np.repeat(colors, self.vertices, axis=0)
306
+
307
+ self.index = np.c_[
308
+ np.repeat(np.repeat(np.arange(1), self.lines), self.vertices),
309
+ np.repeat(np.tile(np.arange(self.lines), 1), self.vertices),
310
+ np.tile(np.arange(self.vertices), self.lines),
311
+ ].astype(np.float32)
312
+
313
+ # Setup Program
314
+ self.program["a_position"] = self.line_data.reshape(-1, 1)
315
+ self.program["a_color"] = self.line_colors
316
+ self.program["a_index"] = self.index
317
+ self.program["u_scale"] = (1.0, 1.0)
318
+ self.program["u_size"] = (self.lines, 1)
319
+ self.program["u_n"] = self.vertices
320
+
321
+ gloo.set_viewport(0, 0, *self.physical_size)
322
+ gloo.set_state(
323
+ clear_color=self.background_color,
324
+ blend=True,
325
+ blend_func=("src_alpha", "one_minus_src_alpha"),
326
+ )
327
+
328
+ def on_update(self, input_data: np.ndarray) -> None:
329
+ input_lines = input_data.shape[0]
330
+ input_vertices = input_data.shape[1]
331
+
332
+ assert (
333
+ input_lines == self.lines
334
+ ), "Input data lines do not match the configured lines."
335
+
336
+ # Flip the order of the input lines backwards
337
+ input_data = np.flip(input_data, axis=0)
338
+
339
+ # Check if the input data has more vertices than the configured vertices
340
+ if input_vertices > self.vertices:
341
+ self.line_data = input_data[:, -self.vertices :]
342
+ else:
343
+ self.line_data[:, :-input_vertices] = self.line_data[:, input_vertices:]
344
+ self.line_data[:, -input_vertices:] = input_data
345
+
346
+ self.program["a_position"].set_data(self.line_data.ravel().astype(np.float32))
347
+ self.update()
348
+ self.context.flush()
349
+
350
+ def on_update_color(self, line_number: int, disable: bool = False) -> None:
351
+ # Update alpha value of the line color
352
+ disable_color = self.background_color
353
+ fliped_line_number = self.lines - line_number - 1
354
+ if disable:
355
+ self.line_colors[
356
+ fliped_line_number
357
+ * self.vertices : (fliped_line_number + 1)
358
+ * self.vertices
359
+ ] = disable_color[:3]
360
+ else:
361
+
362
+ if self.is_light_color(self.background_color):
363
+ self.line_colors[
364
+ fliped_line_number
365
+ * self.vertices : (fliped_line_number + 1)
366
+ * self.vertices
367
+ ] = (
368
+ COLOR_PALETTE_RGB_LIGHT[
369
+ fliped_line_number % len(COLOR_PALETTE_RGB_LIGHT)
370
+ ]
371
+ / 255.0
372
+ )
373
+ else:
374
+ self.line_colors[
375
+ fliped_line_number
376
+ * self.vertices : (fliped_line_number + 1)
377
+ * self.vertices
378
+ ] = (
379
+ COLOR_PALETTE_RGB_DARK[
380
+ fliped_line_number % len(COLOR_PALETTE_RGB_DARK)
381
+ ]
382
+ / 255.0
383
+ )
384
+
385
+ self.program["a_color"].set_data(self.line_colors)
386
+ self.update()
387
+ self.context.flush()
388
+
389
+ def on_reset(self):
390
+ self.line_data = np.zeros((self.lines, self.vertices)).astype(np.float32)
391
+ self.program["a_position"].set_data(self.line_data.ravel().astype(np.float32))
392
+ self.update()
393
+ self.context.flush()
394
+
395
+ def on_resize(self, event):
396
+ gloo.set_viewport(0, 0, *event.physical_size)
397
+
398
+ def on_draw(self, event):
399
+ if self.line_data is None:
400
+ return
401
+
402
+ gloo.clear()
403
+ self.program.draw("line_strip")
404
+
405
+ def on_mouse_wheel(self, event):
406
+ # Get the delta from the mouse event
407
+ dx, dy = event.delta
408
+
409
+ scale_factor = 15
410
+ dx *= scale_factor
411
+ dy *= scale_factor
412
+ # Create a QWheelEvent
413
+ pos = QPoint(event.pos[0], event.pos[1])
414
+ global_pos = QPoint(event.pos[0], event.pos[1])
415
+ pixel_delta = QPoint(dx, dy)
416
+ angle_delta = QPoint(dx * 8, dy * 8) # Convert to eighths of a degree
417
+ buttons = Qt.NoButton
418
+ modifiers = Qt.NoModifier
419
+ phase = Qt.ScrollUpdate
420
+ inverted = False
421
+
422
+ wheel_event = QWheelEvent(
423
+ pos,
424
+ global_pos,
425
+ pixel_delta,
426
+ angle_delta,
427
+ buttons,
428
+ modifiers,
429
+ phase,
430
+ inverted,
431
+ )
432
+
433
+ self.plot_scroll_area.wheelEvent(wheel_event)
434
+
435
+ def is_light_color(self, rgb):
436
+ # Normalize to 1
437
+ luminance = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]
438
+ return luminance > 0.5
439
+
440
+ def _init_shaders(self):
441
+ self.vert_shader = """
442
+ #version 120
443
+ // y coordinate of the position.
444
+ attribute float a_position;
445
+ // row, col, and time index.
446
+ attribute vec3 a_index;
447
+ varying vec3 v_index;
448
+ // 2D scaling factor (zooming).
449
+ uniform vec2 u_scale;
450
+ // Size of the table.
451
+ uniform vec2 u_size;
452
+ // Number of samples per signal.
453
+ uniform float u_n;
454
+ // Color.
455
+ attribute vec3 a_color;
456
+ varying vec4 v_color;
457
+ // Varying variables used for clipping in the fragment shader.
458
+ varying vec2 v_position;
459
+ varying vec4 v_ab;
460
+ void main() {
461
+ float nrows = u_size.x;
462
+ float ncols = u_size.y;
463
+ // Compute the x coordinate from the time index.
464
+ float x = -1 + 2*a_index.z / (u_n-1);
465
+ vec2 position = vec2(x - (1 - 1 / u_scale.x), a_position);
466
+ // Find the affine transformation for the subplots.
467
+ vec2 a = vec2(1./ncols, 1./nrows)*.9;
468
+ vec2 b = vec2(-1 + 2*(a_index.x+.5) / ncols,
469
+ -1 + 2*(a_index.y+.5) / nrows);
470
+ // Apply the static subplot transformation + scaling.
471
+ gl_Position = vec4(a*u_scale*position+b, 0.0, 1.0);
472
+ v_color = vec4(a_color, 1.);
473
+ v_index = a_index;
474
+ // For clipping test in the fragment shader.
475
+ v_position = gl_Position.xy;
476
+ v_ab = vec4(a, b);
477
+ }
478
+ """
479
+
480
+ self.frag_shader = """
481
+ #version 120
482
+ varying vec4 v_color;
483
+ varying vec3 v_index;
484
+ varying vec2 v_position;
485
+ varying vec4 v_ab;
486
+ void main() {
487
+ gl_FragColor = v_color;
488
+ // Discard the fragments between the signals (emulate glMultiDrawArrays).
489
+ if ((fract(v_index.x) > 0.) || (fract(v_index.y) > 0.))
490
+ discard;
491
+ // Clipping test.
492
+ vec2 test = abs((v_position.xy-v_ab.zw)/v_ab.xy);
493
+ if ((test.x > 1) || (test.y > 1))
494
+ discard;
495
+ }
496
+ """
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ui version="4.0">
3
+ <class>DeviceWidgetForm</class>
4
+ <widget class="QWidget" name="DeviceWidgetForm">
5
+ <property name="geometry">
6
+ <rect>
7
+ <x>0</x>
8
+ <y>0</y>
9
+ <width>400</width>
10
+ <height>300</height>
11
+ </rect>
12
+ </property>
13
+ <property name="windowTitle">
14
+ <string>Form</string>
15
+ </property>
16
+ <layout class="QGridLayout" name="gridLayout">
17
+ <item row="0" column="0">
18
+ <widget class="QLabel" name="label">
19
+ <property name="text">
20
+ <string>Device</string>
21
+ </property>
22
+ </widget>
23
+ </item>
24
+ <item row="0" column="1">
25
+ <widget class="QComboBox" name="deviceSelectionComboBox"/>
26
+ </item>
27
+ <item row="1" column="0" colspan="2">
28
+ <widget class="QStackedWidget" name="deviceStackedWidget">
29
+ <property name="currentIndex">
30
+ <number>-1</number>
31
+ </property>
32
+ </widget>
33
+ </item>
34
+ </layout>
35
+ </widget>
36
+ <resources/>
37
+ <connections/>
38
+ </ui>
@@ -0,0 +1,171 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ui version="4.0">
3
+ <class>MuoviPlusForm</class>
4
+ <widget class="QWidget" name="MuoviPlusForm">
5
+ <property name="geometry">
6
+ <rect>
7
+ <x>0</x>
8
+ <y>0</y>
9
+ <width>400</width>
10
+ <height>324</height>
11
+ </rect>
12
+ </property>
13
+ <property name="windowTitle">
14
+ <string>MuoviPlusForm</string>
15
+ </property>
16
+ <layout class="QGridLayout" name="gridLayout">
17
+ <item row="4" column="0">
18
+ <spacer name="verticalSpacer">
19
+ <property name="orientation">
20
+ <enum>Qt::Vertical</enum>
21
+ </property>
22
+ <property name="sizeHint" stdset="0">
23
+ <size>
24
+ <width>20</width>
25
+ <height>86</height>
26
+ </size>
27
+ </property>
28
+ </spacer>
29
+ </item>
30
+ <item row="0" column="0" colspan="2">
31
+ <widget class="QGroupBox" name="connectionGroupBox">
32
+ <property name="title">
33
+ <string>Connection parameters</string>
34
+ </property>
35
+ <layout class="QGridLayout" name="gridLayout_7">
36
+ <item row="1" column="1">
37
+ <widget class="QLabel" name="connectionPortLabel">
38
+ <property name="text">
39
+ <string>54321</string>
40
+ </property>
41
+ </widget>
42
+ </item>
43
+ <item row="1" column="0">
44
+ <widget class="QLabel" name="label_7">
45
+ <property name="text">
46
+ <string>Port</string>
47
+ </property>
48
+ </widget>
49
+ </item>
50
+ <item row="0" column="0">
51
+ <widget class="QLabel" name="label_6">
52
+ <property name="text">
53
+ <string>IP</string>
54
+ </property>
55
+ </widget>
56
+ </item>
57
+ <item row="0" column="2">
58
+ <widget class="QPushButton" name="connectionUpdatePushButton">
59
+ <property name="text">
60
+ <string>Update</string>
61
+ </property>
62
+ </widget>
63
+ </item>
64
+ <item row="0" column="1">
65
+ <widget class="QComboBox" name="connectionIPComboBox"/>
66
+ </item>
67
+ </layout>
68
+ </widget>
69
+ </item>
70
+ <item row="2" column="0" rowspan="2" colspan="2">
71
+ <widget class="QGroupBox" name="commandsGroupBox">
72
+ <property name="title">
73
+ <string>Commands</string>
74
+ </property>
75
+ <layout class="QGridLayout" name="gridLayout_3">
76
+ <item row="0" column="0">
77
+ <widget class="QPushButton" name="commandConnectionPushButton">
78
+ <property name="text">
79
+ <string>Connect</string>
80
+ </property>
81
+ </widget>
82
+ </item>
83
+ <item row="1" column="0">
84
+ <widget class="QPushButton" name="commandConfigurationPushButton">
85
+ <property name="text">
86
+ <string>Configure</string>
87
+ </property>
88
+ </widget>
89
+ </item>
90
+ <item row="2" column="0">
91
+ <widget class="QPushButton" name="commandStreamPushButton">
92
+ <property name="text">
93
+ <string>Stream</string>
94
+ </property>
95
+ </widget>
96
+ </item>
97
+ </layout>
98
+ </widget>
99
+ </item>
100
+ <item row="1" column="0" colspan="2">
101
+ <widget class="QGroupBox" name="inputGroupBox">
102
+ <property name="title">
103
+ <string>Input Parameters</string>
104
+ </property>
105
+ <layout class="QGridLayout" name="gridLayout_4">
106
+ <item row="1" column="1">
107
+ <widget class="QComboBox" name="inputDetectionModeComboBox">
108
+ <property name="currentIndex">
109
+ <number>1</number>
110
+ </property>
111
+ <item>
112
+ <property name="text">
113
+ <string>Monopolar - High Gain</string>
114
+ </property>
115
+ </item>
116
+ <item>
117
+ <property name="text">
118
+ <string>Monopolar - Low Gain</string>
119
+ </property>
120
+ </item>
121
+ <item>
122
+ <property name="text">
123
+ <string>Impedance Check</string>
124
+ </property>
125
+ </item>
126
+ <item>
127
+ <property name="text">
128
+ <string>Test</string>
129
+ </property>
130
+ </item>
131
+ </widget>
132
+ </item>
133
+ <item row="1" column="0">
134
+ <widget class="QLabel" name="label_10">
135
+ <property name="text">
136
+ <string>Detection Mode</string>
137
+ </property>
138
+ </widget>
139
+ </item>
140
+ <item row="0" column="0">
141
+ <widget class="QLabel" name="label">
142
+ <property name="text">
143
+ <string>Working Mode</string>
144
+ </property>
145
+ </widget>
146
+ </item>
147
+ <item row="0" column="1">
148
+ <widget class="QComboBox" name="inputWorkingModeComboBox">
149
+ <property name="currentIndex">
150
+ <number>1</number>
151
+ </property>
152
+ <item>
153
+ <property name="text">
154
+ <string>EEG</string>
155
+ </property>
156
+ </item>
157
+ <item>
158
+ <property name="text">
159
+ <string>EMG</string>
160
+ </property>
161
+ </item>
162
+ </widget>
163
+ </item>
164
+ </layout>
165
+ </widget>
166
+ </item>
167
+ </layout>
168
+ </widget>
169
+ <resources/>
170
+ <connections/>
171
+ </ui>