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()
class RasterizedDataWindow(tkinter.Toplevel):
 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.
RasterizedDataWindow( side: int, exp_data: models.experiment_process_data.ExperimentProcessData)
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.
exp_data
event_data
color_cycle
color_index
def show(self):
83    def show(self):
84        """
85        Unhides the window.
86        """
87        self.deiconify()

Unhides the window.

def create_plot(self) -> None:
 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.
def update_plot( self, lick_times: Optional[list[float]] = None, logical_trial: Optional[int] = None) -> None:
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).