Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion physiolabxr/examples/LSLExampleOutlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def main(argv):
name = 'Dummy-8Chan'
print('Stream name is ' + name)
type = 'EEG'
n_channels = 8
n_channels = 800
help_string = 'SendData.py -s <sampling_rate> -n <stream_name> -t <stream_type>'
try:
opts, args = getopt.getopt(argv, "hs:c:n:t:", longopts=["srate=", "channels=", "name=", "type"])
Expand Down
74 changes: 0 additions & 74 deletions physiolabxr/examples/LSLExampleOutlet1.py

This file was deleted.

69 changes: 0 additions & 69 deletions physiolabxr/examples/LSLExampleOutletJohn.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

# create a new stream info and outlet

stream_name = 'python_lsl_my_stream_name'
stream_name = 'stream'
stream_type = 'LSL'

n_channels = 8
# using the local_clock() to track elapsed time
Expand All @@ -14,7 +15,7 @@
# set the sampling rate to 100 Hz
nominal_sampling_rate = 100

info = StreamInfo('stream_name', 'my_stream_type', n_channels, nominal_sampling_rate, 'float32',
info = StreamInfo(stream_name, stream_type, n_channels, nominal_sampling_rate, 'float32',
'my_stream_id')
outlet = StreamOutlet(info)

Expand Down
14 changes: 12 additions & 2 deletions physiolabxr/ui/BaseStreamWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from PyQt6 import QtWidgets, uic, QtCore
from PyQt6.QtCore import QTimer, QThread, QMutex, Qt
from PyQt6.QtGui import QPixmap, QMovie
from PyQt6.QtGui import QPixmap, QMovie, QPainter, QPen
from PyQt6.QtWidgets import QDialogButtonBox, QSplitter

from physiolabxr.configs import config_ui
Expand Down Expand Up @@ -134,6 +134,7 @@ def __init__(self, parent_widget, parent_layout, preset_type, stream_name, data_
# mutex for not update the settings while plotting
self.setting_update_viz_mutex = QMutex()
self.set_pop_button_icons()
self.show_border = False

def start_timers(self):
self.v_timer.start()
Expand Down Expand Up @@ -532,4 +533,13 @@ def run_data_processor(self, data_dict):


def get_viz_components(self):
return self.viz_components
return self.viz_components

def paintEvent(self, event):
super().paintEvent(event)
if self.show_border:
painter = QPainter(self)
pen = QPen(Qt.GlobalColor.red, 2)
painter.setPen(pen)
painter.drawRect(self.rect())
painter.end()
53 changes: 33 additions & 20 deletions physiolabxr/ui/GroupPlotWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,27 @@ def init_line_chart(self):
distinct_colors = get_distinct_colors(len(channel_indices))
self.legends = self.linechart_widget.addLegend()
# self.linechart_widget.enableAutoRange(enable=False)
for channel_index_in_group, (channel_index, channel_name) in enumerate(
zip(channel_indices, self.channel_names)):
is_channel_shown = is_channels_shown[channel_index_in_group]
channel_plot_item = self.linechart_widget.plot([], [], pen=pg.mkPen(color=distinct_colors[channel_index_in_group]), name=channel_name)
self.channel_index_channel_dict[int(channel_index)] = channel_plot_item
if not is_channel_shown:
channel_plot_item.hide() # TODO does disable do what it should do: uncheck from the plots
downsample_method = 'mean' if self.sampling_rate > AppConfigs().downsample_method_mean_sr_threshold else 'subsample'
channel_plot_item.setDownsampling(auto=True, method=downsample_method)
channel_plot_item.setClipToView(True)
channel_plot_item.setSkipFiniteCheck(True)
self.channel_plot_item_dict[channel_name] = channel_plot_item
pens = []
names = []
for channel_index_in_group, (channel_index, channel_name) in enumerate(zip(channel_indices, self.channel_names)):
# is_channel_shown = is_channels_shown[channel_index_in_group]
pens.append(pg.mkPen(color=distinct_colors[channel_index_in_group]))
names.append(channel_name)
# channel_plot_item = self.linechart_widget.plot([], [], pen=pg.mkPen(color=distinct_colors[channel_index_in_group]), name=channel_name)
# self.channel_index_channel_dict[int(channel_index)] = channel_plot_item
# if not is_channel_shown:
# channel_plot_item.hide() # TODO does disable do what it should do: uncheck from the plots
# downsample_method = 'mean' if self.sampling_rate > AppConfigs().downsample_method_mean_sr_threshold else 'subsample'
# channel_plot_item.setDownsampling(auto=True, method=downsample_method)
# channel_plot_item.setClipToView(True)
# channel_plot_item.setSkipFiniteCheck(True)
# self.channel_plot_item_dict[channel_name] = channel_plot_item
group_plot_item = self.linechart_widget.plot([], [], pen=pens, name=names)
downsample_method = 'mean' if self.sampling_rate > AppConfigs().downsample_method_mean_sr_threshold else 'subsample'
group_plot_item.setDownsampling(auto=True, method=downsample_method)
# channel_plot_item.setClipToView(True)
group_plot_item.setSkipFiniteCheck(True)
# self.channel_plot_item_dict[channel_name] = channel_plot_item

def init_image(self):
self.plot_widget = pg.PlotWidget()
Expand Down Expand Up @@ -193,12 +202,17 @@ def plot_data(self, data):
# if line_chat_config.channels_constant_offset!=0:
# data = data +


time_vector = np.linspace(0., duration, data.shape[1])
for index_in_group, channel_index in enumerate(channel_indices):
plot_data_item = self.linechart_widget.plotItem.curves[index_in_group]
if plot_data_item.isVisible():
plot_data_item.setData(time_vector, data[channel_index, :]+linechart_config.channels_constant_offset*index_in_group)

y_vals = data[channel_indices]
channel_offsets = np.arange(y_vals.shape[0]) * linechart_config.channels_constant_offset
y_vals = y_vals + channel_offsets.reshape(-1, 1)
self.linechart_widget.plotItem.curves[0].setData(time_vector, y_vals)
# plot_data_item = self.linechart_widget.plotItem.curves[index_in_group]
# if plot_data_item.isVisible():
# print('plotting channel', channel_index, 'in group', self.group_name)
# print(time_vector.shape, data[channel_index, :].shape)
# plot_data_item.setData(time_vector, data[channel_index, :]+linechart_config.channels_constant_offset*index_in_group)

elif selected_plot_format == 1 and get_group_image_valid(self.stream_name, self.group_name):
image_config = get_group_image_config(self.stream_name, self.group_name)
Expand Down Expand Up @@ -277,9 +291,8 @@ def change_group_name(self, new_group_name):

def change_channel_name(self, new_ch_name, old_ch_name, lsl_index):
# change_plot_label(self.linechart_widget, self.channel_plot_item_dict[old_ch_name], new_ch_name)
self.channel_plot_item_dict[old_ch_name].setData(name=new_ch_name)
self.channel_plot_item_dict[new_ch_name] = self.channel_plot_item_dict.pop(old_ch_name)

# self.channel_plot_item_dict[old_ch_name].setData(name=new_ch_name)
# self.channel_plot_item_dict[new_ch_name] = self.channel_plot_item_dict.pop(old_ch_name)
# self.channel_plot_item_dict[old_ch_name].legend.setText(new_ch_name)
channel_indices = get_group_channel_indices(self.stream_name, self.group_name)
index_in_group = channel_indices.index(lsl_index)
Expand Down
59 changes: 56 additions & 3 deletions physiolabxr/ui/MainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from typing import Dict

from PyQt6 import QtWidgets, uic
from PyQt6.QtCore import QTimer
from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtGui import QKeyEvent, QKeySequence, QShortcutEvent
from PyQt6.QtWidgets import QMessageBox, QDialogButtonBox

from physiolabxr.configs.GlobalSignals import GlobalSignals
Expand Down Expand Up @@ -38,7 +39,7 @@
from physiolabxr.ui.SettingsWidget import SettingsWidget
from physiolabxr.ui.ReplayTab import ReplayTab
from physiolabxr.utils.buffers import DataBuffer
from physiolabxr.utils.ui_utils import another_window
from physiolabxr.utils.ui_utils import another_window, ShortCutType
from physiolabxr.ui.dialogs import dialog_popup

import numpy as np
Expand Down Expand Up @@ -145,6 +146,16 @@ def __init__(self, app, ask_to_close=True, *args, **kwargs):
# notification pane
self.notification_panel = NotificationPane(self)

# shortcut setup
QtWidgets.QApplication.instance().installEventFilter(self)
self.shortcut_ids = {
self.grabShortcut(QKeySequence('Alt+Tab'), context=Qt.ShortcutContext.ApplicationShortcut): ShortCutType.switch,
self.grabShortcut(QKeySequence('Alt+D'), context=Qt.ShortcutContext.ApplicationShortcut): ShortCutType.delete,
self.grabShortcut(QKeySequence('Alt+S'), context=Qt.ShortcutContext.ApplicationShortcut): ShortCutType.start,
self.grabShortcut(QKeySequence('Alt+P'), context=Qt.ShortcutContext.ApplicationShortcut): ShortCutType.pop
}



# # fmri widget
# # TODO: FMRI WIDGET
Expand Down Expand Up @@ -503,4 +514,46 @@ def resizeEvent(self, a0):
self.adjust_notification_panel_location()

def adjust_notification_panel_location(self):
self.notification_panel.move(self.width() - self.notification_panel.width() - 9, self.height() - self.notification_panel.height() - self.recording_file_size_label.height() - 12) # substract 64 to account for margin
self.notification_panel.move(self.width() - self.notification_panel.width() - 9, self.height() - self.notification_panel.height() - self.recording_file_size_label.height() - 12) # substract 64 to account for margin

def eventFilter(self, source, event):
if event.type() == QKeyEvent.Type.KeyRelease:
if event.key() == Qt.Key.Key_Alt:
for widget in self.stream_widgets.values():
if widget.show_border:
widget.show_border = False
widget.update()
return True
return super(MainWindow, self).eventFilter(source, event)

def event(self, event):
if isinstance(event, QShortcutEvent):
shortcut = self.shortcut_ids.get(event.shortcutId())
widgests = list(self.stream_widgets.values())[::-1]
index = next((i for i, widget in enumerate(widgests) if widget.hasFocus()), -1)
match shortcut:
case ShortCutType.switch:
widgests[index].show_border = False
widgests[index].update()
index = (index+1)%len(widgests)
if widgests[index].is_popped:
widgests[index].activateWindow()
else:
self.activateWindow()
widgests[index].show_border = True
widgests[index].update()
widgests[index].setFocus()
case ShortCutType.delete:
if widgests[index].hasFocus():
widgests[index].try_close()
case ShortCutType.start:
if widgests[index].hasFocus():
widgests[index].start_stop_stream_btn_clicked()
case ShortCutType.pop:
if widgests[index].hasFocus():
if widgests[index].is_popped:
widgests[index].dock_window()
else:
widgests[index].pop_window()
widgests[index].setFocus()
return super().event(event)
10 changes: 6 additions & 4 deletions physiolabxr/utils/ui_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ def set_back():
line_edit.textChanged.disconnect(set_back)
line_edit.textChanged.connect(set_back)
raise RenaError(f'{name} must be an integer')
class ShortCutType(Enum):
switch = 1
delete = 2
start = 3
pop = 4

# def add_items(combobox: QComboBox, items: Iterable):
# """
Expand All @@ -326,7 +331,4 @@ def set_back():
# combobox.addItems(items)
# # remove the placeholder item from the combobox if it exists
# if placeholder_index != -1:
# combobox.removeItem(placeholder_index)



# combobox.removeItem(placeholder_index)
Loading