From b97f15fdb5ed261d48afdb12d11f47014a1906e4 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 18 Dec 2023 20:52:10 -0500 Subject: [PATCH 01/11] changes adapt pyqtgraph --- .../WriteYourOwnDataSourceExamples/LSLExampleOutlet.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py b/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py index 0b6574d7..83192485 100644 --- a/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py +++ b/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py @@ -4,9 +4,10 @@ # create a new stream info and outlet -stream_name = 'python_lsl_my_stream_name' +stream_name = 'stream' +stream_type = 'LSL' -n_channels = 8 +n_channels = 500 # using the local_clock() to track elapsed time start_time = local_clock() # track how many samples we have sent @@ -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) From 0056aa0fef2211c33f8de1a7ddcd682c128e82f4 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 18 Dec 2023 21:12:56 -0500 Subject: [PATCH 02/11] forgot to add actual changes --- physiolabxr/PhysioLabXR.py => PhysioLabXR.py | 0 physiolabxr/ui/GroupPlotWidget.py | 43 +++++++++++++------- physiolabxr/utils/video_capture_utils.py | 2 +- 3 files changed, 30 insertions(+), 15 deletions(-) rename physiolabxr/PhysioLabXR.py => PhysioLabXR.py (100%) diff --git a/physiolabxr/PhysioLabXR.py b/PhysioLabXR.py similarity index 100% rename from physiolabxr/PhysioLabXR.py rename to PhysioLabXR.py diff --git a/physiolabxr/ui/GroupPlotWidget.py b/physiolabxr/ui/GroupPlotWidget.py index 1b675083..c9791e8c 100644 --- a/physiolabxr/ui/GroupPlotWidget.py +++ b/physiolabxr/ui/GroupPlotWidget.py @@ -100,18 +100,28 @@ 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) + 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] - 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 + # 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 + channel_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' + 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 def init_image(self): self.plot_widget = pg.PlotWidget() @@ -193,12 +203,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]) + y_vals = np.zeros((len(channel_indices), time_vector.shape[0])) 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[channel_index] = data[channel_index, :]+linechart_config.channels_constant_offset*index_in_group + plot_data_item = self.linechart_widget.plotItem.curves[0] + plot_data_item.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) diff --git a/physiolabxr/utils/video_capture_utils.py b/physiolabxr/utils/video_capture_utils.py index 08f28af0..d42351fc 100644 --- a/physiolabxr/utils/video_capture_utils.py +++ b/physiolabxr/utils/video_capture_utils.py @@ -38,8 +38,8 @@ def get_working_camera_ports(): # SplashLoadingTextNotifier().set_loading_text("Video port %s is not working." %dev_port) else: is_reading, img = camera.read() - h, w, ch = img.shape if is_reading: + h, w, ch = img.shape # SplashLoadingTextNotifier().set_loading_text("Video port %s is working and reads images (%s x %s)" %(dev_port,h,w)) working_cams.append({'stream_name': f"Camera {dev_port}", 'width': w, 'height': h, 'nchannels': ch, 'video_id': dev_port}) else: From 0e2200fca55c3eb49f392304119df478cb1d2c00 Mon Sep 17 00:00:00 2001 From: apocalyvec Date: Mon, 18 Dec 2023 22:50:17 -0500 Subject: [PATCH 03/11] move PhysioLabXR.py to physiolabxr folder --- PhysioLabXR.py => physiolabxr/PhysioLabXR.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename PhysioLabXR.py => physiolabxr/PhysioLabXR.py (100%) diff --git a/PhysioLabXR.py b/physiolabxr/PhysioLabXR.py similarity index 100% rename from PhysioLabXR.py rename to physiolabxr/PhysioLabXR.py From 16dacfd59b01ec04aaca0431cb2485c9a3f2ca3c Mon Sep 17 00:00:00 2001 From: apocalyvec Date: Tue, 19 Dec 2023 15:31:23 -0500 Subject: [PATCH 04/11] change channel number of LSLExampleOutlet.py --- .../examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py b/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py index 83192485..70bf75c8 100644 --- a/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py +++ b/physiolabxr/examples/WriteYourOwnDataSourceExamples/LSLExampleOutlet.py @@ -7,7 +7,7 @@ stream_name = 'stream' stream_type = 'LSL' -n_channels = 500 +n_channels = 8 # using the local_clock() to track elapsed time start_time = local_clock() # track how many samples we have sent From b15caa591aefdc6fc0f5626c839177df990b9d20 Mon Sep 17 00:00:00 2001 From: apocalyvec Date: Tue, 19 Dec 2023 15:40:31 -0500 Subject: [PATCH 05/11] update requirements pyqtgraph to @wutwasthat's fork --- requirements.dev.txt | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index 34d681de..c3bbf46a 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -5,7 +5,7 @@ pyserial pylsl scikit-learn scipy~=1.11.0 -pyqtgraph +git+https://github.com/wutwasthat/pyqtgraph.git@physio pyxdf pyscreeze opencv-python diff --git a/requirements.txt b/requirements.txt index 86ea6f34..2bd374de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pyserial pylsl scikit-learn scipy~=1.11.0 -pyqtgraph +git+https://github.com/wutwasthat/pyqtgraph.git@physio pyxdf pyscreeze opencv-python From 98e4cdcc168e1a40476204678ac4fcaf8908c694 Mon Sep 17 00:00:00 2001 From: Ziheng 'Leo' Li Date: Tue, 19 Dec 2023 19:29:39 -0500 Subject: [PATCH 06/11] fix issue with creating/editing groups --- physiolabxr/ui/GroupPlotWidget.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/physiolabxr/ui/GroupPlotWidget.py b/physiolabxr/ui/GroupPlotWidget.py index c9791e8c..18d03178 100644 --- a/physiolabxr/ui/GroupPlotWidget.py +++ b/physiolabxr/ui/GroupPlotWidget.py @@ -102,8 +102,7 @@ def init_line_chart(self): # self.linechart_widget.enableAutoRange(enable=False) pens = [] names = [] - for channel_index_in_group, (channel_index, channel_name) in enumerate( - zip(channel_indices, self.channel_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) @@ -204,11 +203,11 @@ def plot_data(self, data): # data = data + time_vector = np.linspace(0., duration, data.shape[1]) - y_vals = np.zeros((len(channel_indices), time_vector.shape[0])) - for index_in_group, channel_index in enumerate(channel_indices): - y_vals[channel_index] = data[channel_index, :]+linechart_config.channels_constant_offset*index_in_group - plot_data_item = self.linechart_widget.plotItem.curves[0] - plot_data_item.setData(time_vector, y_vals) + + 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) From 19aeec619683aeb226d12524855e031d32348293 Mon Sep 17 00:00:00 2001 From: Ziheng 'Leo' Li Date: Tue, 19 Dec 2023 19:38:42 -0500 Subject: [PATCH 07/11] fix issue with changing channel names --- physiolabxr/ui/GroupPlotWidget.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/physiolabxr/ui/GroupPlotWidget.py b/physiolabxr/ui/GroupPlotWidget.py index 18d03178..df6f461e 100644 --- a/physiolabxr/ui/GroupPlotWidget.py +++ b/physiolabxr/ui/GroupPlotWidget.py @@ -115,11 +115,11 @@ def init_line_chart(self): # channel_plot_item.setClipToView(True) # channel_plot_item.setSkipFiniteCheck(True) # self.channel_plot_item_dict[channel_name] = channel_plot_item - channel_plot_item = self.linechart_widget.plot([], [], pen=pens, name=names) + 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' - channel_plot_item.setDownsampling(auto=True, method=downsample_method) + group_plot_item.setDownsampling(auto=True, method=downsample_method) # channel_plot_item.setClipToView(True) - channel_plot_item.setSkipFiniteCheck(True) + group_plot_item.setSkipFiniteCheck(True) # self.channel_plot_item_dict[channel_name] = channel_plot_item def init_image(self): @@ -291,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) From bfb993f7b7d8935b192daa5771f518ca5e407478 Mon Sep 17 00:00:00 2001 From: Ziheng 'Leo' Li Date: Wed, 7 Feb 2024 00:49:37 -0500 Subject: [PATCH 08/11] rename visualization test --- tests/{RenaVisualizationTest.py => VisualizationTest.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{RenaVisualizationTest.py => VisualizationTest.py} (100%) diff --git a/tests/RenaVisualizationTest.py b/tests/VisualizationTest.py similarity index 100% rename from tests/RenaVisualizationTest.py rename to tests/VisualizationTest.py From a31d89785403a71a34c2d1e05d44b82290e5b942 Mon Sep 17 00:00:00 2001 From: apocalyvec Date: Wed, 7 Feb 2024 00:54:48 -0500 Subject: [PATCH 09/11] remove deprecated files, add toml to requirements --- physiolabxr/examples/LSLExampleOutlet.py | 2 +- physiolabxr/examples/LSLExampleOutlet1.py | 74 -------------------- physiolabxr/examples/LSLExampleOutletJohn.py | 69 ------------------ requirements.dev.txt | 1 + requirements.txt | 1 + 5 files changed, 3 insertions(+), 144 deletions(-) delete mode 100644 physiolabxr/examples/LSLExampleOutlet1.py delete mode 100644 physiolabxr/examples/LSLExampleOutletJohn.py diff --git a/physiolabxr/examples/LSLExampleOutlet.py b/physiolabxr/examples/LSLExampleOutlet.py index d1d940a3..44a1872c 100644 --- a/physiolabxr/examples/LSLExampleOutlet.py +++ b/physiolabxr/examples/LSLExampleOutlet.py @@ -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 -n -t ' try: opts, args = getopt.getopt(argv, "hs:c:n:t:", longopts=["srate=", "channels=", "name=", "type"]) diff --git a/physiolabxr/examples/LSLExampleOutlet1.py b/physiolabxr/examples/LSLExampleOutlet1.py deleted file mode 100644 index f1df5611..00000000 --- a/physiolabxr/examples/LSLExampleOutlet1.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Example program to demonstrate how to send a multi-channel time series to -LSL.""" -import random -import sys -import getopt -import string -import numpy as np -import time -from random import random as rand - -from pylsl import StreamInfo, StreamOutlet, local_clock - - -def main(argv): - letters = string.digits - - srate = 128 - name = 'EEG-8Chan' - print('Stream name is ' + name) - type = 'EEG' - n_channels = 12 - help_string = 'SendData.py -s -n -t ' - try: - opts, args = getopt.getopt(argv, "hs:c:n:t:", longopts=["srate=", "channels=", "name=", "type"]) - except getopt.GetoptError: - print(help_string) - sys.exit(2) - for opt, arg in opts: - if opt == '-h': - print(help_string) - sys.exit() - elif opt in ("-s", "--srate"): - srate = float(arg) - elif opt in ("-c", "--channels"): - n_channels = int(arg) - elif opt in ("-n", "--name"): - name = arg - elif opt in ("-t", "--type"): - type = arg - - # first create a new stream info (here we set the name to BioSemi, - # the content-type to EEG, 8 channels, 100 Hz, and float-valued data) The - # last value would be the serial number of the device or some other more or - # less locally unique identifier for the stream as far as available (you - # could also omit it but interrupted connections wouldn't auto-recover) - info = StreamInfo(name, type, n_channels, srate, 'float32', 'someuuid1234') - - # next make an outlet - outlet = StreamOutlet(info) - - print("now sending data...") - start_time = local_clock() - sent_samples = 0 - while True: - elapsed_time = local_clock() - start_time - required_samples = int(srate * elapsed_time) - sent_samples - for sample_ix in range(required_samples): - # make a new random n_channels sample; this is converted into a - # pylsl.vectorf (the data type that is expected by push_sample) - mysample = [rand() * 200 for _ in range(n_channels)] - # now send it - mysample[0] = time.time() - mysample[-3] = rand() - mysample[-2] = rand() - mysample[-1] = rand() - - outlet.push_sample(mysample) - sent_samples += required_samples - # now send it and wait for a bit before trying again. - time.sleep(0.01) - - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/physiolabxr/examples/LSLExampleOutletJohn.py b/physiolabxr/examples/LSLExampleOutletJohn.py deleted file mode 100644 index f03196a9..00000000 --- a/physiolabxr/examples/LSLExampleOutletJohn.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Example program to demonstrate how to send a multi-channel time series to -LSL.""" -import random -import sys -import getopt -import string -import numpy as np -import time -from random import random as rand - -from pylsl import StreamInfo, StreamOutlet, local_clock - - -def main(argv): - letters = string.digits - - srate = 128 - name = 'Dummy-8Chan2' - print('Stream name is ' + name) - type = 'EEG' - n_channels = 12 - help_string = 'SendData.py -s -n -t ' - try: - opts, args = getopt.getopt(argv, "hs:c:n:t:", longopts=["srate=", "channels=", "name=", "type"]) - except getopt.GetoptError: - print(help_string) - sys.exit(2) - for opt, arg in opts: - if opt == '-h': - print(help_string) - sys.exit() - elif opt in ("-s", "--srate"): - srate = float(arg) - elif opt in ("-c", "--channels"): - n_channels = int(arg) - elif opt in ("-n", "--name"): - name = arg - elif opt in ("-t", "--type"): - type = arg - - # first create a new stream info (here we set the name to BioSemi, - # the content-type to EEG, 8 channels, 100 Hz, and float-valued data) The - # last value would be the serial number of the device or some other more or - # less locally unique identifier for the stream as far as available (you - # could also omit it but interrupted connections wouldn't auto-recover) - info = StreamInfo(name, type, n_channels, srate, 'float32', 'someuuid1234') - - # next make an outlet - outlet = StreamOutlet(info) - - print("now sending data...") - start_time = local_clock() - sent_samples = 0 - while True: - elapsed_time = local_clock() - start_time - required_samples = int(srate * elapsed_time) - sent_samples - for sample_ix in range(required_samples): - # make a new random n_channels sample; this is converted into a - # pylsl.vectorf (the data type that is expected by push_sample) - mysample = [rand()*100 for _ in range(n_channels)] - # now send it - outlet.push_sample(mysample) - sent_samples += required_samples - # now send it and wait for a bit before trying again. - time.sleep(0.01) - - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/requirements.dev.txt b/requirements.dev.txt index c3bbf46a..390ae46f 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -19,3 +19,4 @@ PyOpenGL_accelerate soundfile matplotlib imblearn +toml diff --git a/requirements.txt b/requirements.txt index 2bd374de..b926a504 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,4 @@ PyOpenGL_accelerate soundfile matplotlib imblearn +toml From b3adb8a9c93d7780292caab538f78199447285f0 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 29 Feb 2024 02:49:37 -0500 Subject: [PATCH 10/11] shortcuts --- physiolabxr/ui/BaseStreamWidget.py | 14 ++++++- physiolabxr/ui/MainWindow.py | 62 ++++++++++++++++++++++++++++-- physiolabxr/utils/ui_utils.py | 8 +++- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/physiolabxr/ui/BaseStreamWidget.py b/physiolabxr/ui/BaseStreamWidget.py index 7dbfddca..c6e204f3 100644 --- a/physiolabxr/ui/BaseStreamWidget.py +++ b/physiolabxr/ui/BaseStreamWidget.py @@ -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 @@ -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() @@ -532,4 +533,13 @@ def run_data_processor(self, data_dict): def get_viz_components(self): - return self.viz_components \ No newline at end of file + 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() \ No newline at end of file diff --git a/physiolabxr/ui/MainWindow.py b/physiolabxr/ui/MainWindow.py index a483b689..bb01b406 100644 --- a/physiolabxr/ui/MainWindow.py +++ b/physiolabxr/ui/MainWindow.py @@ -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 @@ -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 @@ -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 @@ -503,4 +514,49 @@ 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 \ No newline at end of file + 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: + print(len(self.stream_widgets)) + for widget in self.stream_widgets.values(): + print(widget.objectName()) + 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() + print("current",widgests[index].objectName(), widgests[index].show_border, widgests[index].hasFocus()) + 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) \ No newline at end of file diff --git a/physiolabxr/utils/ui_utils.py b/physiolabxr/utils/ui_utils.py index af5b245b..cf251f49 100644 --- a/physiolabxr/utils/ui_utils.py +++ b/physiolabxr/utils/ui_utils.py @@ -314,4 +314,10 @@ def set_back(): line_edit.setStyleSheet("") line_edit.textChanged.disconnect(set_back) line_edit.textChanged.connect(set_back) - raise RenaError(f'{name} must be an integer') \ No newline at end of file + raise RenaError(f'{name} must be an integer') + +class ShortCutType(Enum): + switch = 1 + delete = 2 + start = 3 + pop = 4 From d06188c4b78c8217766b9e181af4b023803127d7 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 29 Feb 2024 02:57:10 -0500 Subject: [PATCH 11/11] remove prints --- physiolabxr/ui/MainWindow.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/physiolabxr/ui/MainWindow.py b/physiolabxr/ui/MainWindow.py index bb01b406..f54ebd60 100644 --- a/physiolabxr/ui/MainWindow.py +++ b/physiolabxr/ui/MainWindow.py @@ -519,9 +519,7 @@ def adjust_notification_panel_location(self): def eventFilter(self, source, event): if event.type() == QKeyEvent.Type.KeyRelease: if event.key() == Qt.Key.Key_Alt: - print(len(self.stream_widgets)) for widget in self.stream_widgets.values(): - print(widget.objectName()) if widget.show_border: widget.show_border = False widget.update() @@ -545,7 +543,6 @@ def event(self, event): widgests[index].show_border = True widgests[index].update() widgests[index].setFocus() - print("current",widgests[index].objectName(), widgests[index].show_border, widgests[index].hasFocus()) case ShortCutType.delete: if widgests[index].hasFocus(): widgests[index].try_close()