rasterized_data_window
Defines the RasterizedDataWindow class, a Tkinter Toplevel window designed to display lick event data as a raster plot using Matplotlib figures.
This view provides a visualization of lick timestamps for a specific recording side (e.g., Port 1 or Port 2), plotting each lick as a marker against trial number and time within the trial (relative to the first lick). It updates as new trial data becomes available.
1""" 2Defines the RasterizedDataWindow class, a Tkinter Toplevel window designed 3to display lick event data as a raster plot using Matplotlib figures. 4 5This view provides a visualization of lick timestamps for a specific recording side (e.g., Port 1 or Port 2), 6plotting each lick as a marker against trial number and time within the trial (relative to the first lick). 7It updates as new trial data becomes available. 8""" 9 10import tkinter as tk 11from typing import Optional 12import matplotlib.pyplot as plt 13import matplotlib.cm as cm 14from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 15from matplotlib.backends._backend_tk import NavigationToolbar2Tk 16 17### Type Hinting### 18from models.experiment_process_data import ExperimentProcessData 19### Type Hinting### 20 21 22class RasterizedDataWindow(tk.Toplevel): 23 """ 24 A Toplevel window creating a Matplotlib raster plot to visualize lick events. 25 26 This window displays lick timestamps for a specific experimental side (passed during 27 initialization) against the trial number. Each lick within a trial is plotted 28 as a vertical marker (|). The time axis typically represents the time elapsed since 29 the first lick in that trial. The plot updates as new lick data arrives 30 via the `update_plot` method. 31 32 Attributes 33 ---------- 34 - **exp_data** (*ExperimentProcessData*): An instance holding experiment-related data, 35 including trial parameters (`exp_var_entries`) used for setting Y-axis plot limits. 36 - **color_cycle** (*Colormap*): A Matplotlib colormap instance (`tab10`) used 37 to cycle through colors for plotting data from different trials/updates. 38 In other words, makes it easier to distinguish trials. 39 - **color_index** (*int*): The current index into the `color_cycle`. 40 - **canvas** (*FigureCanvasTkAgg | None*): The Matplotlib canvas widget embedded in the 41 Tkinter window. Initialized in `create_plot`. 42 - **axes** (*Axes | None*): The Matplotlib axes object where the raster 43 plot is drawn. Initialized in `create_plot`. 44 45 Methods 46 ------- 47 - `show()` 48 Makes the window visible. 49 - `create_plot()` 50 Creates the initial Matplotlib figure, axes, canvas, and toolbar. Sets axis limits. 51 - `update_plot(lick_times, logical_trial)` 52 Adds lick data for a specific trial to the plot and redraws the canvas. 53 """ 54 55 def __init__(self, side: int, exp_data: ExperimentProcessData) -> None: 56 """ 57 Initializes the RasterizedDataWindow. Sets basic window attributes and initializes class attributes. 58 59 Parameters 60 ---------- 61 - **side** (*int*): The experimental side (e.g., 1 or 2) this plot represents. 62 - **exp_data** (*ExperimentProcessData*): The data object containing experiment parameters 63 and facilitates event data access. 64 """ 65 super().__init__() 66 self.exp_data = exp_data 67 self.event_data = self.exp_data.event_data 68 69 self.protocol("WM_DELETE_WINDOW", lambda: self.withdraw()) 70 self.bind("<Control-w>", lambda e: self.withdraw()) 71 72 self.title(f"Side {side} Lick Time Raster Plot") 73 74 # Initialize the color cycle 75 self.color_cycle = cm.get_cmap("tab10", 10) 76 77 # Initialize the color index 78 self.color_index = 0 79 80 self.withdraw() 81 82 def show(self): 83 """ 84 Unhides the window. 85 """ 86 self.deiconify() 87 88 def create_plot(self) -> None: 89 """ 90 Creates the Matplotlib figure, axes, canvas, and toolbar, and sets initial properties. 91 92 Sets up a container frame. Initializes the Matplotlib figure and axes. 93 Sets the X and Y axis limits based on expected time range and total trial number. 94 Adds labels to the axes and a title to the plot. Plades the figure 95 in a Tkinter canvas and adds the navigation toolbar. Assigns the created 96 components to instance attributes `self.fig`, `self.axes`, and `self.canvas`. 97 98 Raises 99 ------ 100 - *KeyError*: If "Num Trials" is not found in `self.exp_data.exp_var_entries`. 101 - *tk.TclError*: If Tkinter widget creation or packing fails. 102 """ 103 container = tk.Frame(self) 104 container.pack(side=tk.TOP, fill=tk.BOTH, expand=True) 105 106 fig, axes = plt.subplots() 107 canvas = FigureCanvasTkAgg(fig, container) 108 109 canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) 110 111 toolbar = NavigationToolbar2Tk(canvas, container) 112 toolbar.update() 113 114 num_trials = self.exp_data.exp_var_entries["Num Trials"] 115 116 axes.set_xlim(0, 21) 117 axes.set_ylim(0, num_trials + 1) 118 119 self.canvas = canvas 120 self.axes = axes 121 122 def update_plot( 123 self, 124 lick_times: Optional[list[float]] = None, 125 logical_trial: Optional[int] = None, 126 ) -> None: 127 """ 128 Adds lick data for a specific trial to the raster plot and redraws the canvas. 129 130 Checks if the plot components (`axes`, `canvas`) have been initialized. 131 If `lick_times` data is provided and valid, it calculates the time relative 132 to the first lick in the trial, assigns a color based on the trial index, 133 and plots the lick times as vertical markers ('|') at the corresponding 134 `logical_trial` row using `scatter`. Finally, it redraws the canvas to display 135 the updated plot. 136 137 Parameters 138 ---------- 139 - **lick_times** (*Optional[List[float]]*): A list of timestamps (in seconds) for licks 140 recorded during the trial. If None or empty, the plot is not updated for this trial. 141 - **logical_trial** (*Optional[int]*): The zero-based index of the trial corresponding 142 to the `lick_times`. Used as the Y-coordinate for plotting. If None, the plot is not updated. 143 144 Raises 145 ------ 146 - *TypeError*: If `logical_trial` is not an integer when provided. 147 - *IndexError*: Potentially if `lick_times` is accessed incorrectly (though unlikely with current logic). 148 """ 149 axes = self.axes 150 canvas = self.canvas 151 152 self.color_index = (self.color_index + 1) % 10 153 154 if lick_times and len(lick_times) > 0: 155 color = [self.color_cycle(self.color_index)] 156 157 # x values for this trial 158 lick_times = [stamp - lick_times[0] for stamp in lick_times] 159 160 # we need a y for each x value (lick timestamp) 161 y_values = [logical_trial] * len(lick_times) 162 163 axes.scatter(lick_times, y_values, marker="|", c=color, s=100) 164 165 canvas.draw()
23class RasterizedDataWindow(tk.Toplevel): 24 """ 25 A Toplevel window creating a Matplotlib raster plot to visualize lick events. 26 27 This window displays lick timestamps for a specific experimental side (passed during 28 initialization) against the trial number. Each lick within a trial is plotted 29 as a vertical marker (|). The time axis typically represents the time elapsed since 30 the first lick in that trial. The plot updates as new lick data arrives 31 via the `update_plot` method. 32 33 Attributes 34 ---------- 35 - **exp_data** (*ExperimentProcessData*): An instance holding experiment-related data, 36 including trial parameters (`exp_var_entries`) used for setting Y-axis plot limits. 37 - **color_cycle** (*Colormap*): A Matplotlib colormap instance (`tab10`) used 38 to cycle through colors for plotting data from different trials/updates. 39 In other words, makes it easier to distinguish trials. 40 - **color_index** (*int*): The current index into the `color_cycle`. 41 - **canvas** (*FigureCanvasTkAgg | None*): The Matplotlib canvas widget embedded in the 42 Tkinter window. Initialized in `create_plot`. 43 - **axes** (*Axes | None*): The Matplotlib axes object where the raster 44 plot is drawn. Initialized in `create_plot`. 45 46 Methods 47 ------- 48 - `show()` 49 Makes the window visible. 50 - `create_plot()` 51 Creates the initial Matplotlib figure, axes, canvas, and toolbar. Sets axis limits. 52 - `update_plot(lick_times, logical_trial)` 53 Adds lick data for a specific trial to the plot and redraws the canvas. 54 """ 55 56 def __init__(self, side: int, exp_data: ExperimentProcessData) -> None: 57 """ 58 Initializes the RasterizedDataWindow. Sets basic window attributes and initializes class attributes. 59 60 Parameters 61 ---------- 62 - **side** (*int*): The experimental side (e.g., 1 or 2) this plot represents. 63 - **exp_data** (*ExperimentProcessData*): The data object containing experiment parameters 64 and facilitates event data access. 65 """ 66 super().__init__() 67 self.exp_data = exp_data 68 self.event_data = self.exp_data.event_data 69 70 self.protocol("WM_DELETE_WINDOW", lambda: self.withdraw()) 71 self.bind("<Control-w>", lambda e: self.withdraw()) 72 73 self.title(f"Side {side} Lick Time Raster Plot") 74 75 # Initialize the color cycle 76 self.color_cycle = cm.get_cmap("tab10", 10) 77 78 # Initialize the color index 79 self.color_index = 0 80 81 self.withdraw() 82 83 def show(self): 84 """ 85 Unhides the window. 86 """ 87 self.deiconify() 88 89 def create_plot(self) -> None: 90 """ 91 Creates the Matplotlib figure, axes, canvas, and toolbar, and sets initial properties. 92 93 Sets up a container frame. Initializes the Matplotlib figure and axes. 94 Sets the X and Y axis limits based on expected time range and total trial number. 95 Adds labels to the axes and a title to the plot. Plades the figure 96 in a Tkinter canvas and adds the navigation toolbar. Assigns the created 97 components to instance attributes `self.fig`, `self.axes`, and `self.canvas`. 98 99 Raises 100 ------ 101 - *KeyError*: If "Num Trials" is not found in `self.exp_data.exp_var_entries`. 102 - *tk.TclError*: If Tkinter widget creation or packing fails. 103 """ 104 container = tk.Frame(self) 105 container.pack(side=tk.TOP, fill=tk.BOTH, expand=True) 106 107 fig, axes = plt.subplots() 108 canvas = FigureCanvasTkAgg(fig, container) 109 110 canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) 111 112 toolbar = NavigationToolbar2Tk(canvas, container) 113 toolbar.update() 114 115 num_trials = self.exp_data.exp_var_entries["Num Trials"] 116 117 axes.set_xlim(0, 21) 118 axes.set_ylim(0, num_trials + 1) 119 120 self.canvas = canvas 121 self.axes = axes 122 123 def update_plot( 124 self, 125 lick_times: Optional[list[float]] = None, 126 logical_trial: Optional[int] = None, 127 ) -> None: 128 """ 129 Adds lick data for a specific trial to the raster plot and redraws the canvas. 130 131 Checks if the plot components (`axes`, `canvas`) have been initialized. 132 If `lick_times` data is provided and valid, it calculates the time relative 133 to the first lick in the trial, assigns a color based on the trial index, 134 and plots the lick times as vertical markers ('|') at the corresponding 135 `logical_trial` row using `scatter`. Finally, it redraws the canvas to display 136 the updated plot. 137 138 Parameters 139 ---------- 140 - **lick_times** (*Optional[List[float]]*): A list of timestamps (in seconds) for licks 141 recorded during the trial. If None or empty, the plot is not updated for this trial. 142 - **logical_trial** (*Optional[int]*): The zero-based index of the trial corresponding 143 to the `lick_times`. Used as the Y-coordinate for plotting. If None, the plot is not updated. 144 145 Raises 146 ------ 147 - *TypeError*: If `logical_trial` is not an integer when provided. 148 - *IndexError*: Potentially if `lick_times` is accessed incorrectly (though unlikely with current logic). 149 """ 150 axes = self.axes 151 canvas = self.canvas 152 153 self.color_index = (self.color_index + 1) % 10 154 155 if lick_times and len(lick_times) > 0: 156 color = [self.color_cycle(self.color_index)] 157 158 # x values for this trial 159 lick_times = [stamp - lick_times[0] for stamp in lick_times] 160 161 # we need a y for each x value (lick timestamp) 162 y_values = [logical_trial] * len(lick_times) 163 164 axes.scatter(lick_times, y_values, marker="|", c=color, s=100) 165 166 canvas.draw()
A Toplevel window creating a Matplotlib raster plot to visualize lick events.
This window displays lick timestamps for a specific experimental side (passed during
initialization) against the trial number. Each lick within a trial is plotted
as a vertical marker (|). The time axis typically represents the time elapsed since
the first lick in that trial. The plot updates as new lick data arrives
via the update_plot
method.
Attributes
- exp_data (ExperimentProcessData): An instance holding experiment-related data,
including trial parameters (
exp_var_entries
) used for setting Y-axis plot limits. - color_cycle (Colormap): A Matplotlib colormap instance (
tab10
) used to cycle through colors for plotting data from different trials/updates. In other words, makes it easier to distinguish trials. - color_index (int): The current index into the
color_cycle
. - canvas (FigureCanvasTkAgg | None): The Matplotlib canvas widget embedded in the
Tkinter window. Initialized in
create_plot
. - axes (Axes | None): The Matplotlib axes object where the raster
plot is drawn. Initialized in
create_plot
.
Methods
show()
Makes the window visible.create_plot()
Creates the initial Matplotlib figure, axes, canvas, and toolbar. Sets axis limits.update_plot(lick_times, logical_trial)
Adds lick data for a specific trial to the plot and redraws the canvas.
56 def __init__(self, side: int, exp_data: ExperimentProcessData) -> None: 57 """ 58 Initializes the RasterizedDataWindow. Sets basic window attributes and initializes class attributes. 59 60 Parameters 61 ---------- 62 - **side** (*int*): The experimental side (e.g., 1 or 2) this plot represents. 63 - **exp_data** (*ExperimentProcessData*): The data object containing experiment parameters 64 and facilitates event data access. 65 """ 66 super().__init__() 67 self.exp_data = exp_data 68 self.event_data = self.exp_data.event_data 69 70 self.protocol("WM_DELETE_WINDOW", lambda: self.withdraw()) 71 self.bind("<Control-w>", lambda e: self.withdraw()) 72 73 self.title(f"Side {side} Lick Time Raster Plot") 74 75 # Initialize the color cycle 76 self.color_cycle = cm.get_cmap("tab10", 10) 77 78 # Initialize the color index 79 self.color_index = 0 80 81 self.withdraw()
Initializes the RasterizedDataWindow. Sets basic window attributes and initializes class attributes.
Parameters
- side (int): The experimental side (e.g., 1 or 2) this plot represents.
- exp_data (ExperimentProcessData): The data object containing experiment parameters and facilitates event data access.
89 def create_plot(self) -> None: 90 """ 91 Creates the Matplotlib figure, axes, canvas, and toolbar, and sets initial properties. 92 93 Sets up a container frame. Initializes the Matplotlib figure and axes. 94 Sets the X and Y axis limits based on expected time range and total trial number. 95 Adds labels to the axes and a title to the plot. Plades the figure 96 in a Tkinter canvas and adds the navigation toolbar. Assigns the created 97 components to instance attributes `self.fig`, `self.axes`, and `self.canvas`. 98 99 Raises 100 ------ 101 - *KeyError*: If "Num Trials" is not found in `self.exp_data.exp_var_entries`. 102 - *tk.TclError*: If Tkinter widget creation or packing fails. 103 """ 104 container = tk.Frame(self) 105 container.pack(side=tk.TOP, fill=tk.BOTH, expand=True) 106 107 fig, axes = plt.subplots() 108 canvas = FigureCanvasTkAgg(fig, container) 109 110 canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) 111 112 toolbar = NavigationToolbar2Tk(canvas, container) 113 toolbar.update() 114 115 num_trials = self.exp_data.exp_var_entries["Num Trials"] 116 117 axes.set_xlim(0, 21) 118 axes.set_ylim(0, num_trials + 1) 119 120 self.canvas = canvas 121 self.axes = axes
Creates the Matplotlib figure, axes, canvas, and toolbar, and sets initial properties.
Sets up a container frame. Initializes the Matplotlib figure and axes.
Sets the X and Y axis limits based on expected time range and total trial number.
Adds labels to the axes and a title to the plot. Plades the figure
in a Tkinter canvas and adds the navigation toolbar. Assigns the created
components to instance attributes self.fig
, self.axes
, and self.canvas
.
Raises
- KeyError: If "Num Trials" is not found in
self.exp_data.exp_var_entries
. - tk.TclError: If Tkinter widget creation or packing fails.
123 def update_plot( 124 self, 125 lick_times: Optional[list[float]] = None, 126 logical_trial: Optional[int] = None, 127 ) -> None: 128 """ 129 Adds lick data for a specific trial to the raster plot and redraws the canvas. 130 131 Checks if the plot components (`axes`, `canvas`) have been initialized. 132 If `lick_times` data is provided and valid, it calculates the time relative 133 to the first lick in the trial, assigns a color based on the trial index, 134 and plots the lick times as vertical markers ('|') at the corresponding 135 `logical_trial` row using `scatter`. Finally, it redraws the canvas to display 136 the updated plot. 137 138 Parameters 139 ---------- 140 - **lick_times** (*Optional[List[float]]*): A list of timestamps (in seconds) for licks 141 recorded during the trial. If None or empty, the plot is not updated for this trial. 142 - **logical_trial** (*Optional[int]*): The zero-based index of the trial corresponding 143 to the `lick_times`. Used as the Y-coordinate for plotting. If None, the plot is not updated. 144 145 Raises 146 ------ 147 - *TypeError*: If `logical_trial` is not an integer when provided. 148 - *IndexError*: Potentially if `lick_times` is accessed incorrectly (though unlikely with current logic). 149 """ 150 axes = self.axes 151 canvas = self.canvas 152 153 self.color_index = (self.color_index + 1) % 10 154 155 if lick_times and len(lick_times) > 0: 156 color = [self.color_cycle(self.color_index)] 157 158 # x values for this trial 159 lick_times = [stamp - lick_times[0] for stamp in lick_times] 160 161 # we need a y for each x value (lick timestamp) 162 y_values = [logical_trial] * len(lick_times) 163 164 axes.scatter(lick_times, y_values, marker="|", c=color, s=100) 165 166 canvas.draw()
Adds lick data for a specific trial to the raster plot and redraws the canvas.
Checks if the plot components (axes
, canvas
) have been initialized.
If lick_times
data is provided and valid, it calculates the time relative
to the first lick in the trial, assigns a color based on the trial index,
and plots the lick times as vertical markers ('|') at the corresponding
logical_trial
row using scatter
. Finally, it redraws the canvas to display
the updated plot.
Parameters
- lick_times (Optional[List[float]]): A list of timestamps (in seconds) for licks recorded during the trial. If None or empty, the plot is not updated for this trial.
- logical_trial (Optional[int]): The zero-based index of the trial corresponding
to the
lick_times
. Used as the Y-coordinate for plotting. If None, the plot is not updated.
Raises
- TypeError: If
logical_trial
is not an integer when provided. - IndexError: Potentially if
lick_times
is accessed incorrectly (though unlikely with current logic).