Skip to content

Commit e0db191

Browse files
committed
Added saccade loading
1 parent d76d94a commit e0db191

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-1
lines changed

facemap/gui/gui.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
QStatusBar,
3131
QToolButton,
3232
QWidget,
33+
QGraphicsItemGroup
3334
)
3435
from scipy.stats import skew, zscore
35-
3636
from facemap import process, roi, utils
3737
from facemap.gui import (
3838
guiparts,
@@ -265,6 +265,7 @@ def __init__(
265265
self.cframe = 0
266266
self.traces1 = None
267267
self.traces2 = None
268+
self.saccade_data = None
268269

269270
## Pose plot
270271
self.pose_scatterplot = pg.ScatterPlotItem(hover=True)
@@ -939,6 +940,7 @@ def reset(self):
939940
self.keypoints_vtick = None
940941
self.svd_plot_vtick = None
941942
self.neural_win = None
943+
self.saccade_data = None
942944

943945
def pupil_sigma_change(self):
944946
self.pupil_sigma = float(self.sigma_box.text())
@@ -2012,9 +2014,59 @@ def plot_trace(self, wplot, proctype, wroi, color, keypoints_group_selected=None
20122014
pen=pg.mkPen(color=(255, 255, 255), width=2, movable=True),
20132015
)
20142016
selected_plot.addItem(self.keypoints_vtick)
2017+
if self.saccade_data is not None:
2018+
self.plot_saccade_data()
20152019
selected_plot.setLimits(xMin=0, xMax=self.nframes)
20162020
return tr
20172021

2022+
def plot_saccade_data(self):
2023+
"""
2024+
Plot saccade data on the SVD traces plot as a single toggleable item.
2025+
"""
2026+
if self.saccade_data is not None:
2027+
# Extract saccade data
2028+
saccade = self.saccade_data['Saccade'][0, 0].squeeze()
2029+
2030+
# Check if saccade data matches the number of frames
2031+
if saccade.shape[0] != self.nframes:
2032+
print("Saccade data shape does not match the number of frames.")
2033+
return
2034+
2035+
# Find start and end indices for saccades
2036+
sac_start_idx = np.where(np.diff(saccade) == 1)[0] + 1
2037+
sac_end_idx = np.where(np.diff(saccade) == -1)[0] + 1
2038+
2039+
# Remove any existing saccade overlays to avoid duplication
2040+
if hasattr(self, 'saccade_vspan_items') and self.saccade_vspan_items:
2041+
for item in self.saccade_vspan_items:
2042+
self.svd_traces_plot.removeItem(item)
2043+
2044+
# Create a list to store the saccade region items
2045+
self.saccade_vspan_items = []
2046+
2047+
# Add vertical spans for each saccade
2048+
for start, end in zip(sac_start_idx, sac_end_idx):
2049+
if start < end and end < self.nframes:
2050+
# Create a LinearRegionItem for the saccade
2051+
vspan = pg.LinearRegionItem(
2052+
values=(start, end), # Start and end positions
2053+
brush=pg.mkBrush(255, 255, 255, 50), # Semi-transparent white
2054+
movable=False, # Non-movable
2055+
)
2056+
2057+
# Add the region to the plot
2058+
self.svd_traces_plot.addItem(vspan)
2059+
2060+
# Keep track of added items for later removal
2061+
self.saccade_vspan_items.append(vspan)
2062+
else:
2063+
print("No saccade data available.")
2064+
2065+
def toggle_saccade_vspans(self, show):
2066+
# Toggles visibility of the saccade vspans group
2067+
if hasattr(self, 'saccade_vspan_group'): # Check if the group exists
2068+
self.saccade_vspan_group.setVisible(show)
2069+
20182070
def on_click_svd_plot(self, event):
20192071
"""
20202072
Update vtick position of svd plot when user clicks

facemap/gui/io.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,24 @@ def load_movies(parent, filelist=None):
354354
parent.load_keypoints_from_videodir()
355355
return good
356356

357+
def load_saccade(parent):
358+
path = QFileDialog.getOpenFileName(parent, "Select a file", filter="MAT (*.mat)")
359+
# Check if path exists
360+
if path[0]:
361+
try:
362+
import scipy.io
363+
364+
saccade_data = scipy.io.loadmat(path[0])
365+
parent.saccade_data = saccade_data['eye_movement_data']
366+
parent.update_status_bar("Saccade data loaded")
367+
parent.plot_saccade_data()
368+
except Exception as e:
369+
msg = QMessageBox(parent)
370+
msg.setIcon(QMessageBox.Icon.Warning)
371+
msg.setText("Error loading saccade data: " + str(e))
372+
msg.setStandardButtons(QMessageBox.StandardButton.Ok)
373+
msg.exec_()
374+
357375

358376
def load_npy_file(parent, allow_mat=False):
359377
# Open a file dialog to select a folder

facemap/gui/menus.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ def mainmenu(parent):
2323
load_proc.triggered.connect(lambda: io.open_proc(parent))
2424
parent.addAction(load_proc)
2525

26+
# Load saccade data
27+
load_saccade = QAction("Load saccade data", parent)
28+
load_saccade.triggered.connect(lambda: io.load_saccade(parent))
29+
parent.addAction(load_saccade)
30+
2631
# Set output folder
2732
set_output_folder = QAction("Set output folder", parent)
2833
set_output_folder.setShortcut("Ctrl+S")
@@ -95,6 +100,7 @@ def mainmenu(parent):
95100
file_menu.addAction(open_file)
96101
file_menu.addAction(open_folder)
97102
file_menu.addAction(load_proc)
103+
file_menu.addAction(load_saccade)
98104
file_menu.addAction(set_output_folder)
99105

100106
pose_menu = main_menu.addMenu("Pose")

0 commit comments

Comments
 (0)